feat(editor,quick-entry): page editor with splits, links, attachments and one-line quick entry
This commit is contained in:
1 parent
135a2d9328
commit
7e19684cff
22 files changed
+4067
-10
No files matched your search
+32
-5
@@ -1,3 +1,5 @@
|
||||
import { getContext, setContext, untrack } from "svelte";
|
||||
|
||||
export interface Binding {
|
||||
group?: string;
|
||||
hidden?: boolean;
|
||||
@@ -11,6 +13,16 @@ const SEQUENCE_MS = 1200;
|
||||
const EDITABLE =
|
||||
"input, textarea, select, [contenteditable=''], [contenteditable='true']";
|
||||
|
||||
function baseKey(event: KeyboardEvent): string {
|
||||
if (event.altKey && event.code.startsWith("Key")) {
|
||||
return event.code.slice(3).toLowerCase();
|
||||
}
|
||||
if (event.altKey && event.code.startsWith("Digit")) {
|
||||
return event.code.slice(5);
|
||||
}
|
||||
return event.key === " " ? "Space" : event.key;
|
||||
}
|
||||
|
||||
function token(event: KeyboardEvent): string {
|
||||
const parts: string[] = [];
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
@@ -19,7 +31,7 @@ function token(event: KeyboardEvent): string {
|
||||
if (event.altKey) {
|
||||
parts.push("alt");
|
||||
}
|
||||
const key = event.key === " " ? "Space" : event.key;
|
||||
const key = baseKey(event);
|
||||
if (event.shiftKey && key.length > 1) {
|
||||
parts.push("shift");
|
||||
}
|
||||
@@ -32,16 +44,24 @@ export function isEditable(target: EventTarget | null): boolean {
|
||||
}
|
||||
|
||||
class Keys {
|
||||
layers = $state<Binding[][]>([]);
|
||||
layers = $state.raw<Binding[][]>([]);
|
||||
pending = $state("");
|
||||
sheetOpen = $state(false);
|
||||
private timer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
all = $derived(this.layers.flat());
|
||||
|
||||
register(bindings: Binding[]): () => void {
|
||||
private readonly depths = new WeakMap<Binding[], number>();
|
||||
|
||||
register(bindings: Binding[], depth = 0): () => void {
|
||||
const layer = bindings;
|
||||
this.layers = [...this.layers, layer];
|
||||
this.depths.set(layer, depth);
|
||||
const above = this.layers.findIndex(
|
||||
(l) => (this.depths.get(l) ?? 0) > depth
|
||||
);
|
||||
const next = [...this.layers];
|
||||
next.splice(above < 0 ? next.length : above, 0, layer);
|
||||
this.layers = next;
|
||||
return () => {
|
||||
this.layers = this.layers.filter((l) => l !== layer);
|
||||
};
|
||||
@@ -99,8 +119,15 @@ class Keys {
|
||||
|
||||
export const keys = new Keys();
|
||||
|
||||
const DEPTH = Symbol("keys-depth");
|
||||
|
||||
export function useKeys(bindings: () => Binding[]) {
|
||||
$effect(() => keys.register(bindings()));
|
||||
const depth = getContext<number | undefined>(DEPTH) ?? 0;
|
||||
setContext(DEPTH, depth + 1);
|
||||
$effect(() => {
|
||||
const list = bindings();
|
||||
return untrack(() => keys.register(list, depth));
|
||||
});
|
||||
}
|
||||
|
||||
const APPLE = /Mac|iPhone|iPad/;
|
||||
|
||||
Reference in new issue
Block a user