Skip to content

Commit

Permalink
feat: tabs
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit 6ce9386
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 14:57:04 2024 +0200

    docs: tabs in changelog

commit 01ab8e5
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 14:54:24 2024 +0200

    fix: autosave for all documents in tabs & disable autosave

commit 4e2ca6a
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 14:51:26 2024 +0200

    feat: allow disabling tabs

commit 4b5238d
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 12:57:33 2024 +0200

    feat: window switch menu

commit a52b7d1
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 12:24:56 2024 +0200

    feat: allow opening new windows

commit 0e462ab
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 12:03:50 2024 +0200

    fix: on open from file manager, only open chosen file

commit 1076df7
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 01:46:57 2024 +0200

    fix: don't reopen outline sidebar on tab switch

commit 8aef1fa
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 01:38:51 2024 +0200

    fix: open with only one document by default

commit 64fdd18
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 01:37:59 2024 +0200

    fix: default document zoom for initial document

commit 76c331e
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 00:54:40 2024 +0200

    feat: shortcuts for prev/next tab

commit b11c94a
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 00:39:57 2024 +0200

    fix: prompt saving all dirty tabs on close

commit 19bda97
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 00:19:27 2024 +0200

    ops: bump version to 0.0.8-dev

commit a2217f2
Author: dominiksta <[email protected]>
Date:   Fri Aug 30 00:18:15 2024 +0200

    fix: pdf zoom preview invert

commit 291ba34
Author: dominiksta <[email protected]>
Date:   Thu Aug 29 23:20:55 2024 +0200

    fix: pdf text select context menu per tab

commit eae297b
Author: dominiksta <[email protected]>
Date:   Thu Aug 29 22:53:30 2024 +0200

    fix: use new mvui foreach to fix tab close/move behaviour

commit 246dd7b
Author: dominiksta <[email protected]>
Date:   Thu Aug 29 22:37:32 2024 +0200

    fix: on tab close, open tab left to closed

commit 6e632a2
Author: dominiksta <[email protected]>
Date:   Wed Aug 21 22:18:49 2024 +0200

    perf: only compile json schemas once

commit 2b0b103
Author: dominiksta <[email protected]>
Date:   Wed Aug 21 22:14:53 2024 +0200

    perf: load source maps only when error actually pops up

commit 115f766
Author: dominiksta <[email protected]>
Date:   Mon Aug 19 00:37:12 2024 +0200

    fix: dark mode tab button text color

commit 440b52b
Author: dominiksta <[email protected]>
Date:   Mon Aug 19 00:26:34 2024 +0200

    fix: open from system file manager now uses previous instance

commit 1bc7eab
Author: dominiksta <[email protected]>
Date:   Sun Aug 18 23:52:34 2024 +0200

    fix: change filename in tab on save

commit 5a6ea86
Author: dominiksta <[email protected]>
Date:   Sun Aug 18 23:49:44 2024 +0200

    fix: no idea why, but files didnt save with the sync electron dialog

commit d134161
Author: dominiksta <[email protected]>
Date:   Sun Aug 18 23:24:51 2024 +0200

    fix: dont open same document twice in same instance

commit 15d6529
Author: dominiksta <[email protected]>
Date:   Sun Aug 18 23:11:49 2024 +0200

    wip: basic partially working tabs

commit b1cb415
Author: dominiksta <[email protected]>
Date:   Sun Aug 18 22:11:11 2024 +0200

    fix: Ctrl-R no longer refreshes in prod

commit dbf4b7c
Author: dominiksta <[email protected]>
Date:   Sat Aug 17 20:45:20 2024 +0200

    wip: drag&drop to move tabs

commit 3b03359
Merge: 6edb159 fc25bf3
Author: dominiksta <[email protected]>
Date:   Sat Aug 17 16:37:24 2024 +0200

    Merge branch 'dev' into tabs

commit 6edb159
Author: dominiksta <[email protected]>
Date:   Fri Aug 16 13:37:26 2024 +0200

    wip: slightly better styling

commit 63a3d44
Author: dominiksta <[email protected]>
Date:   Fri Aug 16 02:16:23 2024 +0200

    wip: overflow behaviour

commit a9ace21
Author: dominiksta <[email protected]>
Date:   Fri Aug 16 00:33:12 2024 +0200

    wip: text overflow

commit b23b9fb
Author: dominiksta <[email protected]>
Date:   Fri Aug 16 00:28:46 2024 +0200

    wip: better close behaviour

commit 64f8d80
Author: dominiksta <[email protected]>
Date:   Fri Aug 16 00:17:03 2024 +0200

    wip: basic tab bar impl

commit ed82486
Merge: 0b948f9 1087d34
Author: dominiksta <[email protected]>
Date:   Thu Aug 15 22:24:14 2024 +0200

    Merge branch 'dev' into tabs

commit 0b948f9
Author: dominiksta <[email protected]>
Date:   Fri Aug 9 13:39:19 2024 +0200

    refactor: main app layout as flex, not fixed
  • Loading branch information
dominiksta committed Aug 30, 2024
1 parent fc25bf3 commit 9fd8f7a
Show file tree
Hide file tree
Showing 31 changed files with 1,163 additions and 357 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
Changelog
======================================================================

`0.0.8` - _unreleased_
----------------------------------------------------------------------
**Tabs!**

### Added

- Tabs! By default, Wournal will now open documents in tabs. You can still
create new Windows with Ctrl+Shift+N or in the menu from `Window -> New
Window`. If you don't like tabs and want every document to open in its own
window, you can switch to the previous behaviour in the settings under `[Menu]
-> Edit -> Open Preferences -> UI -> Enable Tabs`.

### Fixed

- PDF zoom preview in dark mode was not inverted, resulting in a flashbang on
every zoom
- The default document zoom level was not respected for the first page in the
initial startup document
- Autosaves could not be disabled


`0.0.7` - _2024-08-16_
----------------------------------------------------------------------
**Hotfix for Autosaves and some Quality of Life**
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ quite a bit when that happens.
- [x] Default Document Zoom Level Config Option
- [x] Drag&Drop From File Manager to Open
- [x] Jumplist
- [ ] Customizable Pen Cursor Angle Change (Especially for Lefties)
- [x] Customizable Pen Cursor Angle Change (Especially for Lefties)
- [ ] Lasso Select
- [ ] Set Default Paper Style
- [ ] Vertical Space Tool
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "wournal",
"productName": "Wournal",
"version": "0.0.7",
"version": "0.0.8-dev",
"description": "An SVG Editor for Handwritten Documents",
"author": {
"name": "dominiksta"
Expand All @@ -24,7 +24,7 @@
"make": "electron-forge make"
},
"dependencies": {
"@mvuijs/core": "0.0.3",
"@mvuijs/core": "0.0.4",
"@mvuijs/ui5": "1.24.8",
"@ui5/webcomponents": "1.24.8",
"@pdf-lib/fontkit": "^1.1.1",
Expand Down
43 changes: 34 additions & 9 deletions src/main/api-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from 'electron';
import type { ElectronApi, ApiSpec, ApiRouteName, ElectronCallbacks } from './api';
import fs from 'fs';
import { instances } from './main';
import { createWindow, instances } from './main';
import { parseArgs } from 'node:util';
import { argvParseSpec } from './argv';
import { homedir } from 'os';
Expand Down Expand Up @@ -74,11 +74,11 @@ export function registerApiHandlers() {
},
'file:savePrompt': async (e, defaultPath, filters) => {
const win = instances.get(e.sender)!.win;
const resp = dialog.showSaveDialogSync(win, {
const resp = await dialog.showSaveDialog(win, {
filters, properties: ['showOverwriteConfirmation'], defaultPath
});
if (!resp) return false;
return resp;
if (resp.canceled) return false;
return resp.filePath;
},
'file:exists': async (_, fileName) => {
fileName = fileName.replace(/^~/, homedir);
Expand All @@ -102,8 +102,7 @@ export function registerApiHandlers() {
},

'process:argv': async (e) => {
const argv = instances.get(e.sender)!.argv;
return parseArgs({ args: argv, ...argvParseSpec});
return instances.get(e.sender)!.argv;
},
'process:getRendererSourceDir': async () => {
// @ts-ignore
Expand All @@ -122,10 +121,29 @@ export function registerApiHandlers() {
'window:setTitle': async (e, title) => {
instances.get(e.sender)!.win.setTitle(title);
},
'window:destroy': async (e) => { instances.get(e.sender)!.win.destroy(); },
'window:destroy': async (e) => {
instances.get(e.sender)!.win.destroy();
instances.delete(e.sender);
},
'window:setZoom': async (e, zoom) => {
instances.get(e.sender)!.win.webContents.setZoomFactor(zoom);
},
'window:new': async (e, argv, pwd) => {
createWindow(
argv ?? parseArgs({ args: [], ...argvParseSpec}),
pwd ?? instances.get(e.sender)!.pwd
);
},
'window:list': async _ => {
return [...instances.values()]
.map(inst => ({
title: inst.win.getTitle(), id: inst.id,
focused: inst.lastFocused
}));
},
'window:focus': async (_, id) => {
[...instances.values()].find(inst => inst.id === id).win.focus();
},

'clipboard:writeWournal': async (_, d) => clipboard.writeBuffer(
'image/wournal', Buffer.from(JSON.stringify(d), 'utf-8')
Expand Down Expand Up @@ -161,12 +179,19 @@ export function registerCallbacks(win: BrowserWindow) {
send({})
}),

'file:open': send => app.on('second-instance', (_, argv, pwd) => {
const lastFocused = [...instances.values()].find(instance => instance.lastFocused);
if (lastFocused.win !== win) return;
win.focus();
send({ argv: parseArgs({ args: argv, ...argvParseSpec}), pwd });
})

}

for (let key in impl) (impl as any)[key](
(...args: any[]) => {
(args: any) => {
LOG.info(`Callback Triggered: ${key}`);
win.webContents.send(key, ...args);
win.webContents.send(key, args);
}
)
}
10 changes: 9 additions & 1 deletion src/main/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const ApiRouteNames = [
'window:setTitle',
'window:destroy',
'window:setZoom',
'window:new',
'window:list',
'window:focus',

'clipboard:writeWournal',
'clipboard:readText',
Expand Down Expand Up @@ -66,6 +69,9 @@ export interface ElectronApi extends ApiSpec<ApiRouteName> {
'window:setTitle': (title: string) => Promise<void>;
'window:destroy': () => Promise<void>;
'window:setZoom': (zoom: number) => Promise<void>;
'window:new': (argv?: ArgvParsed, pwd?: string) => Promise<void>;
'window:list': () => Promise<{ id: number, title: string, focused: boolean }[]>;
'window:focus': (id: number) => Promise<void>;

'clipboard:writeWournal': (data: any) => Promise<void>;
'clipboard:readText': () => Promise<string | false>;
Expand All @@ -75,11 +81,13 @@ export interface ElectronApi extends ApiSpec<ApiRouteName> {
}

export const ElectronCallbackNames = [
'window:close'
'window:close',
'file:open',
] as const;

export interface ElectronCallbacks {

'window:close': { },
'file:open': { argv: ArgvParsed, pwd: string },

}
33 changes: 27 additions & 6 deletions src/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { registerApiHandlers, registerCallbacks } from './api-impl';
import { app, BrowserWindow, shell, WebContents } from 'electron';
import { parseArgs } from 'node:util';
import path from 'node:path';
import environment from 'Shared/environment';
import { loggingOverwriteConsoleLogFunctions } from 'Shared/logging';
import { getTempConfig, TEMP_CONFIG_CURRENT_VERSION, writeTempConfig } from './temp-config';
import IdCounter from 'Shared/id-counter';
import { ArgvParsed, argvParseSpec } from './argv';

{
loggingOverwriteConsoleLogFunctions();
Expand All @@ -26,10 +29,13 @@ if (environment.pkgPortable) app.setPath(
);

export const instances: Map<WebContents, {
win: BrowserWindow, pwd: string, argv: string[]
win: BrowserWindow, pwd: string, argv: ArgvParsed, lastFocused: boolean,
id: number,
}> = new Map();

function createWindow(argv: string[], pwd: string) {
const instanceIds = new IdCounter();

export function createWindow(argv: ArgvParsed, pwd: string) {
const tempCfg = getTempConfig();
const win = new BrowserWindow({
width: tempCfg.windowWidth,
Expand Down Expand Up @@ -59,9 +65,19 @@ function createWindow(argv: string[], pwd: string) {
win.webContents.openDevTools();
}

instances.set(win.webContents, { win, argv, pwd });
instances.set(win.webContents, {
win, argv, pwd, lastFocused: true, id: instanceIds.nextId()
});

win.on('close', () => {

// when a window is closed and another one is not immediatly focused, we set
// lastFocused to the first window to still have some sane value
if (instances.get(win.webContents).lastFocused) {
const instancesV = [...instances.values()];
if (instancesV.length !== 0) instancesV[0].lastFocused = true;
}

writeTempConfig({
version: TEMP_CONFIG_CURRENT_VERSION,
windowHeight: win.getBounds().height,
Expand All @@ -70,6 +86,11 @@ function createWindow(argv: string[], pwd: string) {
})
});

win.on('focus', () => {
for (const instance of instances.values()) instance.lastFocused = false;
instances.get(win.webContents).lastFocused = true;
});

win.show();
}

Expand All @@ -78,10 +99,10 @@ if (!gotTheLock) {
app.quit();
} else {
registerApiHandlers();
app.whenReady().then(() => createWindow(process.argv, process.cwd()));
app.on('second-instance', (_, argv, pwd) => createWindow(argv, pwd));
app.whenReady().then(() => createWindow(
parseArgs({ args: process.argv, ...argvParseSpec}), process.cwd()
));
app.on('window-all-closed', () => {
app.quit();

});
}
3 changes: 2 additions & 1 deletion src/main/temp-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ const TempConfigSchema = {
} as const;
export type TempConfig = JTDDataType<typeof TempConfigSchema>;

const validate = ajv.compile(TempConfigSchema);

export const TempConfigVersioner = new DTOVersioner<TempConfig>({
name: 'TempConfig',
validator: ((() => {
const validate = ajv.compile(TempConfigSchema);
return obj => {
const res = validate(obj);
return { success: res, error: JSON.stringify(validate.errors) };
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ export interface WournalApi {
defaultIdentification?: string
): Promise<string | false>;
loadDocumentPrompt(): Promise<boolean>;
loadDocument(identification: string): Promise<boolean>;
loadDocument(identification: string, replace?: boolean): Promise<boolean>;
newDocument(props?: PageProps, identification?: string): void;
closeDocumentPrompt(): Promise<boolean>;
closeDocumentPromptAll(): Promise<boolean>;
getDocumentId(): string | false;
createTestPages(): void;
promptClosingUnsaved(): Promise<boolean>;
Expand Down
28 changes: 12 additions & 16 deletions src/renderer/app/color-palette-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default class ColorPaletteEditor extends Component {

render() {
const { palette } = this.props;

const cfg = this.getContext(ConfigCtx);
const darkInverts = rx.derive(cfg, style.currentTheme, (cfg, curr) =>
cfg.invertDocument && (
Expand Down Expand Up @@ -41,29 +40,26 @@ export default class ColorPaletteEditor extends Component {
}
}, 'Colors are inverted because dark mode is enabled'),
h.table(
// palette.derive(p => p.map(c => h.tr(JSON.stringify(c)))),
palette.derive(p => p.map((color, idx) => h.tr([
h.foreach(palette.asReadonly(), 'pos', (color, i) => h.tr([
h.td(
ui5.input({
fields: { value: color.name },
fields: { value: color.derive(c => c.name) },
events: {
change: e => {
palette.next(v => arrayPatch(v, idx, {
...color,
name: (e.target as ui5.types.Input).value,
keyup: e => {
palette.next(v => arrayPatch(v, i, {
...color.value, name: (e.target as ui5.types.Input).value,
}));
}
}
})
),
h.td(
ColorPicker.t({
props: { color: color.color },
props: { color: color.derive(c => c.color) },
events: {
change: e => {
palette.next(v => arrayPatch(v, idx, {
...color,
color: e.detail,
palette.next(v => arrayPatch(v, i, {
...color.value, color: e.detail,
}));
}
}
Expand All @@ -73,11 +69,11 @@ export default class ColorPaletteEditor extends Component {
ui5.button({
fields: { icon: 'delete' },
events: { click: () => palette.next(v => [
...v.slice(0, idx), ...v.slice(idx+1)
...v.slice(0, i), ...v.slice(i+1)
])}
})
)
])))
]))
),
ui5.button({
fields: { icon: 'add' },
Expand Down Expand Up @@ -105,17 +101,17 @@ export class ColorPicker extends Component<{
}> {

props = {
color: rx.prop<string>(),
color: rx.prop<string>({ reflect: true }),
}

render() {
const { color } = this.props;
this.subscribe(color.pipe(rx.skip(1)), col => this.dispatch('change', col));

const changeColor = (e: Event) => {
const newCol = (e.target as ui5.types.Input).value.toUpperCase();
if (!newCol.match("^#[A-F0-9]*$")) return;
color.next(newCol);
this.dispatch('change', newCol);
}

return [
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/app/document-context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { rx } from "@mvuijs/core";
import { WournalDocument } from "document/WournalDocument";

export const DocumentCtx = new rx.Context<rx.State<WournalDocument>>();
export const DocumentCtx = new rx.Context<rx.DerivedState<WournalDocument>>();
Loading

0 comments on commit 9fd8f7a

Please sign in to comment.