@ecsplico/editor 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/protocol/generate.d.ts +1 -1
- package/dist/features/composites/components/CompositeView.js +5 -1
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/feature.js +12 -1
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/index.d.ts +2 -1
- package/dist/features/composites/index.js +2 -1
- package/dist/features/composites/kinds.d.ts +6 -1
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/merge.d.ts +24 -0
- package/dist/features/composites/merge.js +47 -0
- package/dist/features/composites/merge.js.map +1 -0
- package/dist/features/composites/nodes.d.ts +1 -0
- package/dist/features/composites/nodes.js +1 -1
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/react/CompositeView.js +4 -1
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/i18n/de/composites.js +1 -0
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/tree.js +1 -0
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +2 -0
- package/dist/i18n/en/composites.js +1 -0
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/tree.js +1 -0
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
- package/dist/nuxt/module.d.ts +4 -0
- package/dist/server/handlers/ai.d.ts +3 -3
- package/dist/styles/composites.css +10 -1
- package/dist/tree/DocumentTree.js +20 -5
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +12 -1
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +10 -0
- package/dist/tree/tree-host.js.map +1 -1
- package/dist/tree/tree-items.js +1 -0
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +9 -0
- package/dist/tree/tree-model.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
- package/dist/vue/SimpleEditor.vue.d.ts +4 -4
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/package.json +1 -1
package/dist/tree/tree-host.js
CHANGED
|
@@ -18,6 +18,10 @@ function treeEditorOf(editor, t) {
|
|
|
18
18
|
} : moveIntoVerdict(editor, id, targetId, t),
|
|
19
19
|
canTransfer: () => !editor.isDestroyed && editor.storage.blockDropTarget !== void 0 && typeof editor.commands.transferBlock === "function",
|
|
20
20
|
focusedId: () => editor.isDestroyed ? null : compositeFocusOf(editor.state),
|
|
21
|
+
canMerge: (id) => {
|
|
22
|
+
const found = editor.isDestroyed ? void 0 : findBlockById(editor.state.doc, id);
|
|
23
|
+
return !!found && editor.can().mergeCompositeWithNext(found.pos);
|
|
24
|
+
},
|
|
21
25
|
onChange(listener) {
|
|
22
26
|
let focused = editor.isDestroyed ? null : compositeFocusOf(editor.state);
|
|
23
27
|
const onTransaction = () => {
|
|
@@ -120,6 +124,12 @@ function createTreeExecutor(host) {
|
|
|
120
124
|
const editor = liveEditor();
|
|
121
125
|
return !!editor && editor.commands.focusComposite(command.id);
|
|
122
126
|
}
|
|
127
|
+
case "mergeBlock": {
|
|
128
|
+
const editor = liveEditor();
|
|
129
|
+
if (!editor) return false;
|
|
130
|
+
const found = findBlockById(editor.state.doc, command.id);
|
|
131
|
+
return !!found && editor.commands.mergeCompositeWithNext(found.pos);
|
|
132
|
+
}
|
|
123
133
|
}
|
|
124
134
|
};
|
|
125
135
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-host.js","names":[],"sources":["../../src/tree/tree-host.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { findBlockById } from '../core/schema'\nimport { compositeBasketsOf, compositeFocusOf, compositeJSON, compositeKindsOf, moveBlockInto, moveIntoVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { pickFiles } from '../features/media'\nimport { revealBlockInEditor } from '../outline'\nimport type { DocumentStore } from './store'\nimport type { TreeCommand, TreeEditor, TreeExecutor } from './tree-model'\nimport { readOutline } from './tree-items'\n\n// What connects a `TreeModel` to the world, shared by the Vue and the React `DocumentTree`: the open editor as the model\n// sees it, and the executor of the commands the model emits.\n\n/** The model's view of `editor`: its Baustein kinds, outline and move verdicts (worded in the language of `t`), read live. Create it once per editor. */\nexport function treeEditorOf(editor: Editor, t: Translate): TreeEditor {\n return {\n kinds: compositeKindsOf(editor),\n baskets: compositeBasketsOf(editor),\n outline: structure => readOutline(editor, structure, t),\n moveVerdict: (id, targetId) => (editor.isDestroyed ? { ok: false, reason: t('tree.drop.editorClosed') } : moveIntoVerdict(editor, id, targetId, t)),\n canTransfer: () => !editor.isDestroyed && editor.storage.blockDropTarget !== undefined && typeof editor.commands.transferBlock === 'function',\n focusedId: () => (editor.isDestroyed ? null : compositeFocusOf(editor.state)),\n onChange(listener) {\n // A focus change is a transaction without a document change (the focus chip, a block's „Fokussieren“), so `update` does not see it.\n let focused = editor.isDestroyed ? null : compositeFocusOf(editor.state)\n const onTransaction = () => {\n if (editor.isDestroyed) return\n const now = compositeFocusOf(editor.state)\n if (now === focused) return\n focused = now\n listener()\n }\n editor.on('update', listener)\n editor.on('transaction', onTransaction)\n return () => {\n editor.off('update', listener)\n editor.off('transaction', onTransaction)\n }\n },\n }\n}\n\n/** What the executor needs from its framework binding. Every function reads the current value (props change). */\nexport interface TreeHost {\n store: () => DocumentStore\n editor: () => Editor | undefined\n /** The framework's file import (`useFileImport`). */\n importFiles: (editor: Editor, files: File[], pos: number) => Promise<unknown>\n /** Shows a warning (a toast). */\n warn: (title: string, description: string) => void\n /** Shows that a document was deleted, with an undo. */\n deleted: (title: string, undo: () => void) => void\n}\n\n/** The open composite file: end of composite `blockId`'s body, else (`null`) the end of the document. */\nfunction compositeInsertPos(editor: Editor, blockId: string | null): number {\n const { doc } = editor.state\n const target = blockId ? findBlockById(doc, blockId) : null\n return target?.node.type.name === 'composite' ? target.pos + target.node.nodeSize - 1 : doc.content.size\n}\n\n/**\n * Runs the model's commands against the host's store and the editor. Every `store` method reports its own failure\n * before it rejects, so a rejection only ends the command (`false`); the executor itself never rejects.\n */\nexport function createTreeExecutor(host: TreeHost): TreeExecutor {\n const succeeded = () => true\n const failed = () => false\n const liveEditor = () => {\n const editor = host.editor()\n return editor && !editor.isDestroyed ? editor : undefined\n }\n\n async function removeDocument(id: string, folder: string) {\n // The store leaves an open document and waits for its page teardown (which destroys its collaboration provider)\n // before it deletes.\n const store = host.store()\n const removed = await store.remove(id, folder).catch(() => undefined)\n if (!removed) return false\n host.deleted(removed.title, () => {\n void removed.restore().then(restored => store.open(restored.id)).catch(() => undefined)\n })\n return true\n }\n\n async function importIntoComposite(targetId: string | null) {\n const files = await pickFiles('*/*')\n const editor = liveEditor()\n if (!files.length || !editor) return false\n await host.importFiles(editor, files, compositeInsertPos(editor, targetId))\n return true\n }\n\n return (command: TreeCommand) => {\n const store = host.store()\n switch (command.type) {\n case 'openDocument':\n return store.open(command.id).then(succeeded, failed)\n case 'createDocument':\n return store.create(undefined, command.folder).then(async (doc) => {\n await store.open(doc.id).catch(() => undefined)\n return true\n }, failed)\n case 'moveDocument':\n return store.move(command.id, command.folder).then(succeeded, failed)\n case 'removeDocument':\n return removeDocument(command.id, command.folder)\n case 'createFolder':\n return store.createFolder(command.path).then(succeeded, failed)\n case 'moveFolder':\n return store.moveFolder(command.from, command.to).then(succeeded, failed)\n case 'removeFolder':\n // The server refuses non-empty folders; the toast then reads „Ordner ist nicht leer“.\n return store.removeFolder(command.path).then(succeeded, failed)\n case 'moveBlock': {\n const editor = liveEditor()\n return !!editor && moveBlockInto(editor, command.id, command.targetId)\n }\n case 'revealBlock': {\n const editor = liveEditor()\n if (editor) revealBlockInEditor(editor, { id: command.id, pos: command.pos })\n return !!editor\n }\n case 'createBausteinFolder': {\n const editor = liveEditor()\n if (!editor) return false\n const pos = compositeInsertPos(editor, command.targetId)\n // The caret goes into the new composite's title: one level in, then the text inside it.\n editor.chain().insertContentAt(pos, compositeJSON('folder')).setTextSelection(pos + 2).focus().scrollIntoView().run()\n return true\n }\n case 'importFiles':\n return importIntoComposite(command.targetId)\n case 'transferBlock': {\n const editor = liveEditor()\n // An editor without the transfer feature has no such command: the drop is refused (`false`), never thrown.\n if (!editor || typeof editor.commands.transferBlock !== 'function') return false\n return editor.commands.transferBlock({ payload: command.payload, pos: compositeInsertPos(editor, command.targetId), menu: command.menu, x: command.x, y: command.y })\n }\n case 'warn':\n host.warn(command.title, command.description)\n return true\n case 'focusBlock': {\n const editor = liveEditor()\n return !!editor && editor.commands.focusComposite(command.id)\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tree-host.js","names":[],"sources":["../../src/tree/tree-host.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { findBlockById } from '../core/schema'\nimport { compositeBasketsOf, compositeFocusOf, compositeJSON, compositeKindsOf, moveBlockInto, moveIntoVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { pickFiles } from '../features/media'\nimport { revealBlockInEditor } from '../outline'\nimport type { DocumentStore } from './store'\nimport type { TreeCommand, TreeEditor, TreeExecutor } from './tree-model'\nimport { readOutline } from './tree-items'\n\n// What connects a `TreeModel` to the world, shared by the Vue and the React `DocumentTree`: the open editor as the model\n// sees it, and the executor of the commands the model emits.\n\n/** The model's view of `editor`: its Baustein kinds, outline and move verdicts (worded in the language of `t`), read live. Create it once per editor. */\nexport function treeEditorOf(editor: Editor, t: Translate): TreeEditor {\n return {\n kinds: compositeKindsOf(editor),\n baskets: compositeBasketsOf(editor),\n outline: structure => readOutline(editor, structure, t),\n moveVerdict: (id, targetId) => (editor.isDestroyed ? { ok: false, reason: t('tree.drop.editorClosed') } : moveIntoVerdict(editor, id, targetId, t)),\n canTransfer: () => !editor.isDestroyed && editor.storage.blockDropTarget !== undefined && typeof editor.commands.transferBlock === 'function',\n focusedId: () => (editor.isDestroyed ? null : compositeFocusOf(editor.state)),\n canMerge: (id) => {\n const found = editor.isDestroyed ? undefined : findBlockById(editor.state.doc, id)\n return !!found && editor.can().mergeCompositeWithNext(found.pos)\n },\n onChange(listener) {\n // A focus change is a transaction without a document change (the focus chip, a block's „Fokussieren“), so `update` does not see it.\n let focused = editor.isDestroyed ? null : compositeFocusOf(editor.state)\n const onTransaction = () => {\n if (editor.isDestroyed) return\n const now = compositeFocusOf(editor.state)\n if (now === focused) return\n focused = now\n listener()\n }\n editor.on('update', listener)\n editor.on('transaction', onTransaction)\n return () => {\n editor.off('update', listener)\n editor.off('transaction', onTransaction)\n }\n },\n }\n}\n\n/** What the executor needs from its framework binding. Every function reads the current value (props change). */\nexport interface TreeHost {\n store: () => DocumentStore\n editor: () => Editor | undefined\n /** The framework's file import (`useFileImport`). */\n importFiles: (editor: Editor, files: File[], pos: number) => Promise<unknown>\n /** Shows a warning (a toast). */\n warn: (title: string, description: string) => void\n /** Shows that a document was deleted, with an undo. */\n deleted: (title: string, undo: () => void) => void\n}\n\n/** The open composite file: end of composite `blockId`'s body, else (`null`) the end of the document. */\nfunction compositeInsertPos(editor: Editor, blockId: string | null): number {\n const { doc } = editor.state\n const target = blockId ? findBlockById(doc, blockId) : null\n return target?.node.type.name === 'composite' ? target.pos + target.node.nodeSize - 1 : doc.content.size\n}\n\n/**\n * Runs the model's commands against the host's store and the editor. Every `store` method reports its own failure\n * before it rejects, so a rejection only ends the command (`false`); the executor itself never rejects.\n */\nexport function createTreeExecutor(host: TreeHost): TreeExecutor {\n const succeeded = () => true\n const failed = () => false\n const liveEditor = () => {\n const editor = host.editor()\n return editor && !editor.isDestroyed ? editor : undefined\n }\n\n async function removeDocument(id: string, folder: string) {\n // The store leaves an open document and waits for its page teardown (which destroys its collaboration provider)\n // before it deletes.\n const store = host.store()\n const removed = await store.remove(id, folder).catch(() => undefined)\n if (!removed) return false\n host.deleted(removed.title, () => {\n void removed.restore().then(restored => store.open(restored.id)).catch(() => undefined)\n })\n return true\n }\n\n async function importIntoComposite(targetId: string | null) {\n const files = await pickFiles('*/*')\n const editor = liveEditor()\n if (!files.length || !editor) return false\n await host.importFiles(editor, files, compositeInsertPos(editor, targetId))\n return true\n }\n\n return (command: TreeCommand) => {\n const store = host.store()\n switch (command.type) {\n case 'openDocument':\n return store.open(command.id).then(succeeded, failed)\n case 'createDocument':\n return store.create(undefined, command.folder).then(async (doc) => {\n await store.open(doc.id).catch(() => undefined)\n return true\n }, failed)\n case 'moveDocument':\n return store.move(command.id, command.folder).then(succeeded, failed)\n case 'removeDocument':\n return removeDocument(command.id, command.folder)\n case 'createFolder':\n return store.createFolder(command.path).then(succeeded, failed)\n case 'moveFolder':\n return store.moveFolder(command.from, command.to).then(succeeded, failed)\n case 'removeFolder':\n // The server refuses non-empty folders; the toast then reads „Ordner ist nicht leer“.\n return store.removeFolder(command.path).then(succeeded, failed)\n case 'moveBlock': {\n const editor = liveEditor()\n return !!editor && moveBlockInto(editor, command.id, command.targetId)\n }\n case 'revealBlock': {\n const editor = liveEditor()\n if (editor) revealBlockInEditor(editor, { id: command.id, pos: command.pos })\n return !!editor\n }\n case 'createBausteinFolder': {\n const editor = liveEditor()\n if (!editor) return false\n const pos = compositeInsertPos(editor, command.targetId)\n // The caret goes into the new composite's title: one level in, then the text inside it.\n editor.chain().insertContentAt(pos, compositeJSON('folder')).setTextSelection(pos + 2).focus().scrollIntoView().run()\n return true\n }\n case 'importFiles':\n return importIntoComposite(command.targetId)\n case 'transferBlock': {\n const editor = liveEditor()\n // An editor without the transfer feature has no such command: the drop is refused (`false`), never thrown.\n if (!editor || typeof editor.commands.transferBlock !== 'function') return false\n return editor.commands.transferBlock({ payload: command.payload, pos: compositeInsertPos(editor, command.targetId), menu: command.menu, x: command.x, y: command.y })\n }\n case 'warn':\n host.warn(command.title, command.description)\n return true\n case 'focusBlock': {\n const editor = liveEditor()\n return !!editor && editor.commands.focusComposite(command.id)\n }\n case 'mergeBlock': {\n const editor = liveEditor()\n if (!editor) return false\n const found = findBlockById(editor.state.doc, command.id)\n return !!found && editor.commands.mergeCompositeWithNext(found.pos)\n }\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/tree/tree-items.js
CHANGED
|
@@ -63,6 +63,7 @@ function outlineItems(nodes, kinds, select) {
|
|
|
63
63
|
pos: node.pos,
|
|
64
64
|
kind: composite ? node.kind : void 0,
|
|
65
65
|
hasSummary: composite ? node.hasSummary : void 0,
|
|
66
|
+
tone: composite ? compositeKind(kinds, node.kind).chrome?.tone || void 0 : void 0,
|
|
66
67
|
basket: composite ? node.basket : void 0,
|
|
67
68
|
children: node.children.length ? outlineItems(node.children, kinds, select) : void 0,
|
|
68
69
|
onSelect: () => select(item),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-items.js","names":[],"sources":["../../src/tree/tree-items.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type Basket, basketById, compositeKind, type CompositeKind, compositeOutline, defaultCompositeKinds, kindAccepts, type OutlineNode } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type HeadingOutlineNode, headingOutline } from '../outline'\nimport { folderKey } from './folder-paths'\nimport type { TreeFolder } from './types'\nimport type { NodeFilters } from './tree-view-model'\n\n// What the Vue and the React `DocumentTree` share: the items of the file browser (folders and documents of the store,\n// the open document's outline), their kind filters, what can be dragged and created, and the path arithmetic. No\n// framework, no state; the drop rules are `drop-rules.ts`, the text filter and the visible rows `tree-rows.ts`.\n\n/** One row of the file browser: a store folder or document, or an entry of the open document's outline. Which fields are set depends on `type`. */\nexport interface TreeItem {\n /** Unique within the tree: `root`, `folder:<path>`, `doc:<id>`, `outline:<block key>` */\n key: string\n label: string\n icon: string\n type: 'root' | 'folder' | 'document' | 'composite' | 'heading'\n /** Folder path of a root/folder item (`''` = root) */\n path?: string\n /** Document id */\n id?: string\n /** Folder path a document item lives in */\n folder?: string\n /** Outline items: block ID of the composite/heading (`null` while it has none) and its position when the outline was read */\n blockId?: string | null\n pos?: number\n /** Composite items: the composite's kind, and whether it has a summary (description) */\n kind?: string\n hasSummary?: boolean\n /** Composite items: the basket id (`meta.basket`), `null`/absent without one */\n basket?: string | null\n children?: TreeItem[]\n onSelect?: (e?: Event) => void\n onToggle?: (e?: Event) => void\n}\n\n/**\n * What a drag carries (serialized under `DRAG_MIME`): a document or folder of the store, or a composite of the open document.\n * `foreign-block` is a Baustein from another editor surface or document tree (it travels under `BLOCK_MIME`, not `DRAG_MIME`).\n */\nexport type DragPayload\n = | {\n type: 'document' | 'folder'\n /** Document id */\n id?: string\n /** Folder path of a folder item, or the folder a document currently lives in */\n path: string\n }\n /** Virtual mode: a composite of the open document, by block ID */\n | { type: 'composite', blockId: string }\n /** A Baustein dragged in from outside this tree; only its verdict is known until the drop reads the payload. */\n | { type: 'foreign-block' }\n\n/** Key of the virtual-mode root item (the open document). */\nexport const VIRTUAL_ROOT = 'vroot'\n\nexport const DRAG_MIME = 'application/x-tip-tree'\n\nexport const parentPath = (folder: string) => folder.slice(0, Math.max(folder.lastIndexOf('/'), 0))\nexport const joinPath = (folder: string, name: string) => (folder ? `${folder}/${name}` : name)\n\n/** Item keys from the root down to `folder`. */\nexport function ancestorKeys(folder: string): string[] {\n const keys = ['root']\n let path = ''\n for (const segment of folder.split('/').filter(Boolean)) {\n path = joinPath(path, segment)\n keys.push(folderKey(path))\n }\n return keys\n}\n\n/** The folder at `path` below (or at) `from`; undefined when there is none. */\nexport function findFolder(path: string, from: TreeFolder | null | undefined): TreeFolder | undefined {\n if (!from) return undefined\n if (from.path === path) return from\n for (const sub of from.folders) {\n const found = path === sub.path || path.startsWith(`${sub.path}/`) ? findFolder(path, sub) : undefined\n if (found) return found\n }\n return undefined\n}\n\n/** Names of the folders directly in `parent`, without the folder at path `except`. */\nexport const folderNamesIn = (tree: TreeFolder | null | undefined, parent: string, except?: string) =>\n (findFolder(parent, tree)?.folders ?? []).filter(sub => sub.path !== except).map(sub => sub.name)\n\nexport function collectFolderPaths(folder: TreeFolder, into: Set<string>) {\n into.add(folder.path)\n folder.folders.forEach(sub => collectFolderPaths(sub, into))\n}\n\n// --- outline of the open document ------------------------------------------------------------------------------\n\nexport type Outline = { structure: 'composite', nodes: OutlineNode[] } | { structure: 'flat', nodes: HeadingOutlineNode[] }\n\nexport const EMPTY_OUTLINE: Outline = { structure: 'composite', nodes: [] }\n\n/** The outline of the editor's document: the composites in composite structure, else the headings. */\nexport function readOutline(editor: Editor | undefined, structure: string, t: Translate): Outline {\n if (!editor || editor.isDestroyed) return EMPTY_OUTLINE\n const { doc } = editor.state\n return structure === 'composite'\n ? { structure: 'composite', nodes: compositeOutline(doc, new Set(), t('tree.untitled')) }\n : { structure: 'flat', nodes: headingOutline(doc, t('tree.untitled')) }\n}\n\n/** The outline entries as items; `select` runs when one is chosen. */\nexport function outlineItems(nodes: readonly (OutlineNode | HeadingOutlineNode)[], kinds: readonly CompositeKind[], select: (item: TreeItem) => void): TreeItem[] {\n return nodes.map((node) => {\n const composite = 'kind' in node\n const item: TreeItem = {\n key: `outline:${node.key}`,\n label: composite ? node.title : node.text,\n icon: composite ? compositeKind(kinds, node.kind).icon : `i-lucide-heading-${node.level}`,\n type: composite ? 'composite' : 'heading',\n blockId: node.id,\n pos: node.pos,\n kind: composite ? node.kind : undefined,\n hasSummary: composite ? node.hasSummary : undefined,\n basket: composite ? node.basket : undefined,\n children: node.children.length ? outlineItems(node.children as (OutlineNode | HeadingOutlineNode)[], kinds, select) : undefined,\n onSelect: () => select(item),\n onToggle: e => e?.preventDefault(), // the chevron toggles, a click reveals\n }\n return item\n })\n}\n\n/** The basket of a composite item among `baskets`; `undefined` without one (or when the host no longer offers it). */\nexport const itemBasket = (item: TreeItem, baskets: readonly Basket[]): Basket | undefined => basketById(baskets, item.basket)\n\n// --- items -----------------------------------------------------------------------------------------------------\n\n/** The store's folders and documents as items; the open document (`activeId`) lists `outline` below it. */\nexport function folderItem(folder: TreeFolder, activeId: string | undefined, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const root = folder.path === ''\n const item: TreeItem = {\n key: root ? 'root' : folderKey(folder.path),\n label: folder.name,\n icon: root ? 'i-lucide-hard-drive' : 'i-lucide-folder',\n type: root ? 'root' : 'folder',\n path: folder.path,\n children: [\n ...folder.folders.map(sub => folderItem(sub, activeId, outline, select)),\n ...folder.documents.map((doc) => {\n const document: TreeItem = {\n key: `doc:${doc.id}`,\n label: doc.title,\n icon: 'i-lucide-file',\n type: 'document',\n id: doc.id,\n folder: doc.folder,\n // The open document lists its outline; its chevron (not a row click) opens and closes it.\n children: doc.id === activeId && outline.length ? outline : undefined,\n onSelect: () => select(document),\n onToggle: e => e?.preventDefault(),\n }\n return document\n }),\n ],\n onSelect: () => select(item),\n }\n return item\n}\n\n/** Virtual mode: the open document is the root, its `folder`/`document` composites (and all others) the tree below it. */\nexport function virtualRootItem(label: string, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const item: TreeItem = {\n key: VIRTUAL_ROOT,\n label,\n icon: 'i-lucide-package',\n type: 'root',\n children: outline.length ? outline : undefined,\n onSelect: () => select(item),\n }\n return item\n}\n\n/** An item of the unfiltered tree and the key of its parent (`null` for a top-level item). */\nexport interface IndexedItem {\n item: TreeItem\n parent: string | null\n}\n\n/** Adds `list` and all items below it to `into` by key; returns `into`. */\nexport function indexItems(list: readonly TreeItem[], into: Map<string, IndexedItem>, parent: string | null = null) {\n for (const item of list) {\n into.set(item.key, { item, parent })\n if (item.children) indexItems(item.children, into, item.key)\n }\n return into\n}\n\n/** The items from the top-level one down to the item at `key` (empty when `key` is not in `index`). */\nexport function pathTo(index: ReadonlyMap<string, IndexedItem>, key: string): TreeItem[] {\n const path: TreeItem[] = []\n let entry = index.get(key)\n if (!entry) return path\n for (;;) {\n path.unshift(entry.item)\n if (entry.parent === null) return path\n entry = index.get(entry.parent)!\n }\n}\n\n/**\n * The breadcrumb of a focus (`TreeState.focusPath`): `document` is the open document's own row (the virtual root, or the\n * document row of the store; its label words the first crumb, which leaves focus), `crumbs` the Bausteine from there\n * down to the focused one, which is last.\n */\nexport function focusCrumbs(path: readonly TreeItem[]): { document: TreeItem | undefined, crumbs: TreeItem[] } {\n return {\n document: path.find(item => item.type === 'document' || item.key === VIRTUAL_ROOT),\n crumbs: path.filter(item => item.type === 'composite'),\n }\n}\n\n// --- filters ---------------------------------------------------------------------------------------------------\n\nconst fileKindsOf = new WeakMap<Translate, CompositeKind[]>()\n\n/** The kinds a folder lists: Ordner and Dokumente (in the language of `t`; one list per translator). */\nexport function fileKinds(t: Translate): CompositeKind[] {\n let kinds = fileKindsOf.get(t)\n if (!kinds) {\n const builtIn = defaultCompositeKinds(t)\n kinds = ['folder', 'document'].map(id => compositeKind(builtIn, id))\n fileKindsOf.set(t, kinds)\n }\n return kinds\n}\n\n/** What an item is filtered by: the composite's kind, or Ordner/Dokument for the store's folders and documents. */\nfunction itemKind(item: TreeItem): string | undefined {\n return item.kind ?? (item.type === 'folder' || item.type === 'document' ? item.type : undefined)\n}\n\n/** The kinds `item`'s filter can hide below it (`kinds` = those of the open editor). */\nexport function filterKinds(item: TreeItem, env: { structure: string, activeId: string | undefined, kinds: readonly CompositeKind[], t: Translate }): readonly CompositeKind[] {\n const composite = env.structure === 'composite'\n if (item.key === VIRTUAL_ROOT || item.type === 'composite') return env.kinds\n if (item.type === 'document') return composite && item.id === env.activeId ? env.kinds : []\n if (item.type === 'root' || item.type === 'folder') return fileKinds(env.t)\n return []\n}\n\n/**\n * Drops the items of kinds an ancestor's filter hides. Like the outline's own kind filter, their visible descendants\n * move up into the parent's list. A node's own filter applies to everything below it.\n */\nexport function applyNodeFilters(list: readonly TreeItem[], filters: NodeFilters, inherited: ReadonlySet<string> = new Set()): TreeItem[] {\n return list.flatMap((item) => {\n const own = filters[item.key]\n const children = item.children ? applyNodeFilters(item.children, filters, own?.length ? new Set([...inherited, ...own]) : inherited) : undefined\n const kind = itemKind(item)\n if (kind && inherited.has(kind)) return children ?? []\n return [{ ...item, children: children?.length ? children : undefined }]\n })\n}\n\n/**\n * Hides the composites that are not in a `shown` basket and have no visible descendant; ancestors that stay only\n * because of a visible descendant are collected in `dimmed`. Documents, folders, headings and the roots are never hidden.\n */\nexport function applyBasketFilter(list: readonly TreeItem[], shown: ReadonlySet<string>, dimmed: Set<string>): TreeItem[] {\n return list.flatMap((item) => {\n const children = item.children ? applyBasketFilter(item.children, shown, dimmed) : undefined\n if (item.type === 'composite') {\n const matches = !!item.basket && shown.has(item.basket)\n if (!matches && !children?.length) return []\n if (!matches) dimmed.add(item.key)\n }\n return [{ ...item, children: children?.length ? children : undefined }]\n })\n}\n\n// --- creating ----------------------------------------------------------------------------------------------------\n\n/** What can be created below an item of the open file: Ordner-Bausteine, imported Dokument-Bausteine. */\nexport interface Creatable {\n folder: boolean\n document: boolean\n}\n\n/**\n * What `item` takes as new Bausteine of the open file: the file itself (the virtual root, or its document row) takes\n * every kind, a Baustein what its kind accepts as children (`kindAccepts`). Nothing in a flat structure or without an editor.\n */\nexport function creatableBelow(item: TreeItem, env: { structure: string, hasEditor: boolean, activeId: string | undefined, kinds: readonly CompositeKind[] }): Creatable {\n const none: Creatable = { folder: false, document: false }\n if (env.structure !== 'composite' || !env.hasEditor) return none\n if (item.key === VIRTUAL_ROOT || (item.type === 'document' && item.id === env.activeId)) return { folder: true, document: true }\n if (item.type !== 'composite' || !item.blockId || !item.kind) return none\n return { folder: kindAccepts(env.kinds, item.kind, 'folder'), document: kindAccepts(env.kinds, item.kind, 'document') }\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\n/** What a dragged item carries: its document or folder, or in virtual mode its composite's block ID. */\nexport function dragPayloadOf(item: TreeItem): DragPayload {\n return item.type === 'composite'\n ? { type: 'composite', blockId: item.blockId! }\n : item.type === 'document'\n ? { type: 'document', id: item.id, path: item.folder ?? '' }\n : { type: 'folder', path: item.path! }\n}\n\n/** Whether `item` may be dragged. Virtual mode moves composites of the open document; blocks without an ID cannot be addressed. */\nexport function isDraggable(item: TreeItem, virtual: boolean): boolean {\n if (virtual) return item.type === 'composite' && !!item.blockId\n return item.type === 'document' || item.type === 'folder'\n}\n\n"],"mappings":";;;;;;;AAwDA,MAAa,eAAe;AAE5B,MAAa,YAAY;AAEzB,MAAa,cAAc,WAAmB,OAAO,MAAM,GAAG,KAAK,IAAI,OAAO,YAAY,GAAG,GAAG,CAAC,CAAC;AAClG,MAAa,YAAY,QAAgB,SAAkB,SAAS,GAAG,OAAO,GAAG,SAAS;;AAG1F,SAAgB,aAAa,QAA0B;CACrD,MAAM,OAAO,CAAC,MAAM;CACpB,IAAI,OAAO;CACX,KAAK,MAAM,WAAW,OAAO,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,GAAG;EACvD,OAAO,SAAS,MAAM,OAAO;EAC7B,KAAK,KAAK,UAAU,IAAI,CAAC;CAC3B;CACA,OAAO;AACT;;AAGA,SAAgB,WAAW,MAAc,MAA6D;CACpG,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,IAAI,KAAK,SAAS,MAAM,OAAO;CAC/B,KAAK,MAAM,OAAO,KAAK,SAAS;EAC9B,MAAM,QAAQ,SAAS,IAAI,QAAQ,KAAK,WAAW,GAAG,IAAI,KAAK,EAAE,IAAI,WAAW,MAAM,GAAG,IAAI,KAAA;EAC7F,IAAI,OAAO,OAAO;CACpB;AAEF;;AAGA,MAAa,iBAAiB,MAAqC,QAAgB,YAChF,WAAW,QAAQ,IAAI,CAAC,EAAE,WAAW,CAAC,EAAA,CAAG,QAAO,QAAO,IAAI,SAAS,MAAM,CAAC,CAAC,KAAI,QAAO,IAAI,IAAI;AAElG,SAAgB,mBAAmB,QAAoB,MAAmB;CACxE,KAAK,IAAI,OAAO,IAAI;CACpB,OAAO,QAAQ,SAAQ,QAAO,mBAAmB,KAAK,IAAI,CAAC;AAC7D;AAMA,MAAa,gBAAyB;CAAE,WAAW;CAAa,OAAO,CAAC;AAAE;;AAG1E,SAAgB,YAAY,QAA4B,WAAmB,GAAuB;CAChG,IAAI,CAAC,UAAU,OAAO,aAAa,OAAO;CAC1C,MAAM,EAAE,QAAQ,OAAO;CACvB,OAAO,cAAc,cACjB;EAAE,WAAW;EAAa,OAAO,iBAAiB,qBAAK,IAAI,IAAI,GAAG,EAAE,eAAe,CAAC;CAAE,IACtF;EAAE,WAAW;EAAQ,OAAO,eAAe,KAAK,EAAE,eAAe,CAAC;CAAE;AAC1E;;AAGA,SAAgB,aAAa,OAAsD,OAAiC,QAA8C;CAChK,OAAO,MAAM,KAAK,SAAS;EACzB,MAAM,YAAY,UAAU;EAC5B,MAAM,OAAiB;GACrB,KAAK,WAAW,KAAK;GACrB,OAAO,YAAY,KAAK,QAAQ,KAAK;GACrC,MAAM,YAAY,cAAc,OAAO,KAAK,IAAI,CAAC,CAAC,OAAO,oBAAoB,KAAK;GAClF,MAAM,YAAY,cAAc;GAChC,SAAS,KAAK;GACd,KAAK,KAAK;GACV,MAAM,YAAY,KAAK,OAAO,KAAA;GAC9B,YAAY,YAAY,KAAK,aAAa,KAAA;GAC1C,QAAQ,YAAY,KAAK,SAAS,KAAA;GAClC,UAAU,KAAK,SAAS,SAAS,aAAa,KAAK,UAAkD,OAAO,MAAM,IAAI,KAAA;GACtH,gBAAgB,OAAO,IAAI;GAC3B,WAAU,MAAK,GAAG,eAAe;EACnC;EACA,OAAO;CACT,CAAC;AACH;;AAGA,MAAa,cAAc,MAAgB,YAAmD,WAAW,SAAS,KAAK,MAAM;;AAK7H,SAAgB,WAAW,QAAoB,UAA8B,SAAqB,QAA4C;CAC5I,MAAM,OAAO,OAAO,SAAS;CAC7B,MAAM,OAAiB;EACrB,KAAK,OAAO,SAAS,UAAU,OAAO,IAAI;EAC1C,OAAO,OAAO;EACd,MAAM,OAAO,wBAAwB;EACrC,MAAM,OAAO,SAAS;EACtB,MAAM,OAAO;EACb,UAAU,CACR,GAAG,OAAO,QAAQ,KAAI,QAAO,WAAW,KAAK,UAAU,SAAS,MAAM,CAAC,GACvE,GAAG,OAAO,UAAU,KAAK,QAAQ;GAC/B,MAAM,WAAqB;IACzB,KAAK,OAAO,IAAI;IAChB,OAAO,IAAI;IACX,MAAM;IACN,MAAM;IACN,IAAI,IAAI;IACR,QAAQ,IAAI;IAEZ,UAAU,IAAI,OAAO,YAAY,QAAQ,SAAS,UAAU,KAAA;IAC5D,gBAAgB,OAAO,QAAQ;IAC/B,WAAU,MAAK,GAAG,eAAe;GACnC;GACA,OAAO;EACT,CAAC,CACH;EACA,gBAAgB,OAAO,IAAI;CAC7B;CACA,OAAO;AACT;;AAGA,SAAgB,gBAAgB,OAAe,SAAqB,QAA4C;CAC9G,MAAM,OAAiB;EACrB,KAAK;EACL;EACA,MAAM;EACN,MAAM;EACN,UAAU,QAAQ,SAAS,UAAU,KAAA;EACrC,gBAAgB,OAAO,IAAI;CAC7B;CACA,OAAO;AACT;;AASA,SAAgB,WAAW,MAA2B,MAAgC,SAAwB,MAAM;CAClH,KAAK,MAAM,QAAQ,MAAM;EACvB,KAAK,IAAI,KAAK,KAAK;GAAE;GAAM;EAAO,CAAC;EACnC,IAAI,KAAK,UAAU,WAAW,KAAK,UAAU,MAAM,KAAK,GAAG;CAC7D;CACA,OAAO;AACT;;AAGA,SAAgB,OAAO,OAAyC,KAAyB;CACvF,MAAM,OAAmB,CAAC;CAC1B,IAAI,QAAQ,MAAM,IAAI,GAAG;CACzB,IAAI,CAAC,OAAO,OAAO;CACnB,SAAS;EACP,KAAK,QAAQ,MAAM,IAAI;EACvB,IAAI,MAAM,WAAW,MAAM,OAAO;EAClC,QAAQ,MAAM,IAAI,MAAM,MAAM;CAChC;AACF;;;;;;AAOA,SAAgB,YAAY,MAAmF;CAC7G,OAAO;EACL,UAAU,KAAK,MAAK,SAAQ,KAAK,SAAS,cAAc,KAAK,QAAA,OAAoB;EACjF,QAAQ,KAAK,QAAO,SAAQ,KAAK,SAAS,WAAW;CACvD;AACF;AAIA,MAAM,8BAAc,IAAI,QAAoC;;AAG5D,SAAgB,UAAU,GAA+B;CACvD,IAAI,QAAQ,YAAY,IAAI,CAAC;CAC7B,IAAI,CAAC,OAAO;EACV,MAAM,UAAU,sBAAsB,CAAC;EACvC,QAAQ,CAAC,UAAU,UAAU,CAAC,CAAC,KAAI,OAAM,cAAc,SAAS,EAAE,CAAC;EACnE,YAAY,IAAI,GAAG,KAAK;CAC1B;CACA,OAAO;AACT;;AAGA,SAAS,SAAS,MAAoC;CACpD,OAAO,KAAK,SAAS,KAAK,SAAS,YAAY,KAAK,SAAS,aAAa,KAAK,OAAO,KAAA;AACxF;;AAGA,SAAgB,YAAY,MAAgB,KAAmI;CAC7K,MAAM,YAAY,IAAI,cAAc;CACpC,IAAI,KAAK,QAAA,WAAwB,KAAK,SAAS,aAAa,OAAO,IAAI;CACvE,IAAI,KAAK,SAAS,YAAY,OAAO,aAAa,KAAK,OAAO,IAAI,WAAW,IAAI,QAAQ,CAAC;CAC1F,IAAI,KAAK,SAAS,UAAU,KAAK,SAAS,UAAU,OAAO,UAAU,IAAI,CAAC;CAC1E,OAAO,CAAC;AACV;;;;;AAMA,SAAgB,iBAAiB,MAA2B,SAAsB,4BAAiC,IAAI,IAAI,GAAe;CACxI,OAAO,KAAK,SAAS,SAAS;EAC5B,MAAM,MAAM,QAAQ,KAAK;EACzB,MAAM,WAAW,KAAK,WAAW,iBAAiB,KAAK,UAAU,SAAS,KAAK,yBAAS,IAAI,IAAI,CAAC,GAAG,WAAW,GAAG,GAAG,CAAC,IAAI,SAAS,IAAI,KAAA;EACvI,MAAM,OAAO,SAAS,IAAI;EAC1B,IAAI,QAAQ,UAAU,IAAI,IAAI,GAAG,OAAO,YAAY,CAAC;EACrD,OAAO,CAAC;GAAE,GAAG;GAAM,UAAU,UAAU,SAAS,WAAW,KAAA;EAAU,CAAC;CACxE,CAAC;AACH;;;;;AAMA,SAAgB,kBAAkB,MAA2B,OAA4B,QAAiC;CACxH,OAAO,KAAK,SAAS,SAAS;EAC5B,MAAM,WAAW,KAAK,WAAW,kBAAkB,KAAK,UAAU,OAAO,MAAM,IAAI,KAAA;EACnF,IAAI,KAAK,SAAS,aAAa;GAC7B,MAAM,UAAU,CAAC,CAAC,KAAK,UAAU,MAAM,IAAI,KAAK,MAAM;GACtD,IAAI,CAAC,WAAW,CAAC,UAAU,QAAQ,OAAO,CAAC;GAC3C,IAAI,CAAC,SAAS,OAAO,IAAI,KAAK,GAAG;EACnC;EACA,OAAO,CAAC;GAAE,GAAG;GAAM,UAAU,UAAU,SAAS,WAAW,KAAA;EAAU,CAAC;CACxE,CAAC;AACH;;;;;AAcA,SAAgB,eAAe,MAAgB,KAA0H;CACvK,MAAM,OAAkB;EAAE,QAAQ;EAAO,UAAU;CAAM;CACzD,IAAI,IAAI,cAAc,eAAe,CAAC,IAAI,WAAW,OAAO;CAC5D,IAAI,KAAK,QAAA,WAAyB,KAAK,SAAS,cAAc,KAAK,OAAO,IAAI,UAAW,OAAO;EAAE,QAAQ;EAAM,UAAU;CAAK;CAC/H,IAAI,KAAK,SAAS,eAAe,CAAC,KAAK,WAAW,CAAC,KAAK,MAAM,OAAO;CACrE,OAAO;EAAE,QAAQ,YAAY,IAAI,OAAO,KAAK,MAAM,QAAQ;EAAG,UAAU,YAAY,IAAI,OAAO,KAAK,MAAM,UAAU;CAAE;AACxH;;AAKA,SAAgB,cAAc,MAA6B;CACzD,OAAO,KAAK,SAAS,cACjB;EAAE,MAAM;EAAa,SAAS,KAAK;CAAS,IAC5C,KAAK,SAAS,aACZ;EAAE,MAAM;EAAY,IAAI,KAAK;EAAI,MAAM,KAAK,UAAU;CAAG,IACzD;EAAE,MAAM;EAAU,MAAM,KAAK;CAAM;AAC3C;;AAGA,SAAgB,YAAY,MAAgB,SAA2B;CACrE,IAAI,SAAS,OAAO,KAAK,SAAS,eAAe,CAAC,CAAC,KAAK;CACxD,OAAO,KAAK,SAAS,cAAc,KAAK,SAAS;AACnD"}
|
|
1
|
+
{"version":3,"file":"tree-items.js","names":[],"sources":["../../src/tree/tree-items.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type Basket, basketById, compositeKind, type CompositeKind, compositeOutline, defaultCompositeKinds, kindAccepts, type OutlineNode } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type HeadingOutlineNode, headingOutline } from '../outline'\nimport { folderKey } from './folder-paths'\nimport type { TreeFolder } from './types'\nimport type { NodeFilters } from './tree-view-model'\n\n// What the Vue and the React `DocumentTree` share: the items of the file browser (folders and documents of the store,\n// the open document's outline), their kind filters, what can be dragged and created, and the path arithmetic. No\n// framework, no state; the drop rules are `drop-rules.ts`, the text filter and the visible rows `tree-rows.ts`.\n\n/** One row of the file browser: a store folder or document, or an entry of the open document's outline. Which fields are set depends on `type`. */\nexport interface TreeItem {\n /** Unique within the tree: `root`, `folder:<path>`, `doc:<id>`, `outline:<block key>` */\n key: string\n label: string\n icon: string\n type: 'root' | 'folder' | 'document' | 'composite' | 'heading'\n /** Folder path of a root/folder item (`''` = root) */\n path?: string\n /** Document id */\n id?: string\n /** Folder path a document item lives in */\n folder?: string\n /** Outline items: block ID of the composite/heading (`null` while it has none) and its position when the outline was read */\n blockId?: string | null\n pos?: number\n /** Composite items: the composite's kind, and whether it has a summary (description) */\n kind?: string\n hasSummary?: boolean\n /** Composite items: the tone of the kind (`CompositeKind.chrome.tone`) that tints the kind icon; absent for a kind without one */\n tone?: string\n /** Composite items: the basket id (`meta.basket`), `null`/absent without one */\n basket?: string | null\n children?: TreeItem[]\n onSelect?: (e?: Event) => void\n onToggle?: (e?: Event) => void\n}\n\n/**\n * What a drag carries (serialized under `DRAG_MIME`): a document or folder of the store, or a composite of the open document.\n * `foreign-block` is a Baustein from another editor surface or document tree (it travels under `BLOCK_MIME`, not `DRAG_MIME`).\n */\nexport type DragPayload\n = | {\n type: 'document' | 'folder'\n /** Document id */\n id?: string\n /** Folder path of a folder item, or the folder a document currently lives in */\n path: string\n }\n /** Virtual mode: a composite of the open document, by block ID */\n | { type: 'composite', blockId: string }\n /** A Baustein dragged in from outside this tree; only its verdict is known until the drop reads the payload. */\n | { type: 'foreign-block' }\n\n/** Key of the virtual-mode root item (the open document). */\nexport const VIRTUAL_ROOT = 'vroot'\n\nexport const DRAG_MIME = 'application/x-tip-tree'\n\nexport const parentPath = (folder: string) => folder.slice(0, Math.max(folder.lastIndexOf('/'), 0))\nexport const joinPath = (folder: string, name: string) => (folder ? `${folder}/${name}` : name)\n\n/** Item keys from the root down to `folder`. */\nexport function ancestorKeys(folder: string): string[] {\n const keys = ['root']\n let path = ''\n for (const segment of folder.split('/').filter(Boolean)) {\n path = joinPath(path, segment)\n keys.push(folderKey(path))\n }\n return keys\n}\n\n/** The folder at `path` below (or at) `from`; undefined when there is none. */\nexport function findFolder(path: string, from: TreeFolder | null | undefined): TreeFolder | undefined {\n if (!from) return undefined\n if (from.path === path) return from\n for (const sub of from.folders) {\n const found = path === sub.path || path.startsWith(`${sub.path}/`) ? findFolder(path, sub) : undefined\n if (found) return found\n }\n return undefined\n}\n\n/** Names of the folders directly in `parent`, without the folder at path `except`. */\nexport const folderNamesIn = (tree: TreeFolder | null | undefined, parent: string, except?: string) =>\n (findFolder(parent, tree)?.folders ?? []).filter(sub => sub.path !== except).map(sub => sub.name)\n\nexport function collectFolderPaths(folder: TreeFolder, into: Set<string>) {\n into.add(folder.path)\n folder.folders.forEach(sub => collectFolderPaths(sub, into))\n}\n\n// --- outline of the open document ------------------------------------------------------------------------------\n\nexport type Outline = { structure: 'composite', nodes: OutlineNode[] } | { structure: 'flat', nodes: HeadingOutlineNode[] }\n\nexport const EMPTY_OUTLINE: Outline = { structure: 'composite', nodes: [] }\n\n/** The outline of the editor's document: the composites in composite structure, else the headings. */\nexport function readOutline(editor: Editor | undefined, structure: string, t: Translate): Outline {\n if (!editor || editor.isDestroyed) return EMPTY_OUTLINE\n const { doc } = editor.state\n return structure === 'composite'\n ? { structure: 'composite', nodes: compositeOutline(doc, new Set(), t('tree.untitled')) }\n : { structure: 'flat', nodes: headingOutline(doc, t('tree.untitled')) }\n}\n\n/** The outline entries as items; `select` runs when one is chosen. */\nexport function outlineItems(nodes: readonly (OutlineNode | HeadingOutlineNode)[], kinds: readonly CompositeKind[], select: (item: TreeItem) => void): TreeItem[] {\n return nodes.map((node) => {\n const composite = 'kind' in node\n const item: TreeItem = {\n key: `outline:${node.key}`,\n label: composite ? node.title : node.text,\n icon: composite ? compositeKind(kinds, node.kind).icon : `i-lucide-heading-${node.level}`,\n type: composite ? 'composite' : 'heading',\n blockId: node.id,\n pos: node.pos,\n kind: composite ? node.kind : undefined,\n hasSummary: composite ? node.hasSummary : undefined,\n tone: composite ? compositeKind(kinds, node.kind).chrome?.tone || undefined : undefined,\n basket: composite ? node.basket : undefined,\n children: node.children.length ? outlineItems(node.children as (OutlineNode | HeadingOutlineNode)[], kinds, select) : undefined,\n onSelect: () => select(item),\n onToggle: e => e?.preventDefault(), // the chevron toggles, a click reveals\n }\n return item\n })\n}\n\n/** The basket of a composite item among `baskets`; `undefined` without one (or when the host no longer offers it). */\nexport const itemBasket = (item: TreeItem, baskets: readonly Basket[]): Basket | undefined => basketById(baskets, item.basket)\n\n// --- items -----------------------------------------------------------------------------------------------------\n\n/** The store's folders and documents as items; the open document (`activeId`) lists `outline` below it. */\nexport function folderItem(folder: TreeFolder, activeId: string | undefined, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const root = folder.path === ''\n const item: TreeItem = {\n key: root ? 'root' : folderKey(folder.path),\n label: folder.name,\n icon: root ? 'i-lucide-hard-drive' : 'i-lucide-folder',\n type: root ? 'root' : 'folder',\n path: folder.path,\n children: [\n ...folder.folders.map(sub => folderItem(sub, activeId, outline, select)),\n ...folder.documents.map((doc) => {\n const document: TreeItem = {\n key: `doc:${doc.id}`,\n label: doc.title,\n icon: 'i-lucide-file',\n type: 'document',\n id: doc.id,\n folder: doc.folder,\n // The open document lists its outline; its chevron (not a row click) opens and closes it.\n children: doc.id === activeId && outline.length ? outline : undefined,\n onSelect: () => select(document),\n onToggle: e => e?.preventDefault(),\n }\n return document\n }),\n ],\n onSelect: () => select(item),\n }\n return item\n}\n\n/** Virtual mode: the open document is the root, its `folder`/`document` composites (and all others) the tree below it. */\nexport function virtualRootItem(label: string, outline: TreeItem[], select: (item: TreeItem) => void): TreeItem {\n const item: TreeItem = {\n key: VIRTUAL_ROOT,\n label,\n icon: 'i-lucide-package',\n type: 'root',\n children: outline.length ? outline : undefined,\n onSelect: () => select(item),\n }\n return item\n}\n\n/** An item of the unfiltered tree and the key of its parent (`null` for a top-level item). */\nexport interface IndexedItem {\n item: TreeItem\n parent: string | null\n}\n\n/** Adds `list` and all items below it to `into` by key; returns `into`. */\nexport function indexItems(list: readonly TreeItem[], into: Map<string, IndexedItem>, parent: string | null = null) {\n for (const item of list) {\n into.set(item.key, { item, parent })\n if (item.children) indexItems(item.children, into, item.key)\n }\n return into\n}\n\n/** The items from the top-level one down to the item at `key` (empty when `key` is not in `index`). */\nexport function pathTo(index: ReadonlyMap<string, IndexedItem>, key: string): TreeItem[] {\n const path: TreeItem[] = []\n let entry = index.get(key)\n if (!entry) return path\n for (;;) {\n path.unshift(entry.item)\n if (entry.parent === null) return path\n entry = index.get(entry.parent)!\n }\n}\n\n/**\n * The breadcrumb of a focus (`TreeState.focusPath`): `document` is the open document's own row (the virtual root, or the\n * document row of the store; its label words the first crumb, which leaves focus), `crumbs` the Bausteine from there\n * down to the focused one, which is last.\n */\nexport function focusCrumbs(path: readonly TreeItem[]): { document: TreeItem | undefined, crumbs: TreeItem[] } {\n return {\n document: path.find(item => item.type === 'document' || item.key === VIRTUAL_ROOT),\n crumbs: path.filter(item => item.type === 'composite'),\n }\n}\n\n// --- filters ---------------------------------------------------------------------------------------------------\n\nconst fileKindsOf = new WeakMap<Translate, CompositeKind[]>()\n\n/** The kinds a folder lists: Ordner and Dokumente (in the language of `t`; one list per translator). */\nexport function fileKinds(t: Translate): CompositeKind[] {\n let kinds = fileKindsOf.get(t)\n if (!kinds) {\n const builtIn = defaultCompositeKinds(t)\n kinds = ['folder', 'document'].map(id => compositeKind(builtIn, id))\n fileKindsOf.set(t, kinds)\n }\n return kinds\n}\n\n/** What an item is filtered by: the composite's kind, or Ordner/Dokument for the store's folders and documents. */\nfunction itemKind(item: TreeItem): string | undefined {\n return item.kind ?? (item.type === 'folder' || item.type === 'document' ? item.type : undefined)\n}\n\n/** The kinds `item`'s filter can hide below it (`kinds` = those of the open editor). */\nexport function filterKinds(item: TreeItem, env: { structure: string, activeId: string | undefined, kinds: readonly CompositeKind[], t: Translate }): readonly CompositeKind[] {\n const composite = env.structure === 'composite'\n if (item.key === VIRTUAL_ROOT || item.type === 'composite') return env.kinds\n if (item.type === 'document') return composite && item.id === env.activeId ? env.kinds : []\n if (item.type === 'root' || item.type === 'folder') return fileKinds(env.t)\n return []\n}\n\n/**\n * Drops the items of kinds an ancestor's filter hides. Like the outline's own kind filter, their visible descendants\n * move up into the parent's list. A node's own filter applies to everything below it.\n */\nexport function applyNodeFilters(list: readonly TreeItem[], filters: NodeFilters, inherited: ReadonlySet<string> = new Set()): TreeItem[] {\n return list.flatMap((item) => {\n const own = filters[item.key]\n const children = item.children ? applyNodeFilters(item.children, filters, own?.length ? new Set([...inherited, ...own]) : inherited) : undefined\n const kind = itemKind(item)\n if (kind && inherited.has(kind)) return children ?? []\n return [{ ...item, children: children?.length ? children : undefined }]\n })\n}\n\n/**\n * Hides the composites that are not in a `shown` basket and have no visible descendant; ancestors that stay only\n * because of a visible descendant are collected in `dimmed`. Documents, folders, headings and the roots are never hidden.\n */\nexport function applyBasketFilter(list: readonly TreeItem[], shown: ReadonlySet<string>, dimmed: Set<string>): TreeItem[] {\n return list.flatMap((item) => {\n const children = item.children ? applyBasketFilter(item.children, shown, dimmed) : undefined\n if (item.type === 'composite') {\n const matches = !!item.basket && shown.has(item.basket)\n if (!matches && !children?.length) return []\n if (!matches) dimmed.add(item.key)\n }\n return [{ ...item, children: children?.length ? children : undefined }]\n })\n}\n\n// --- creating ----------------------------------------------------------------------------------------------------\n\n/** What can be created below an item of the open file: Ordner-Bausteine, imported Dokument-Bausteine. */\nexport interface Creatable {\n folder: boolean\n document: boolean\n}\n\n/**\n * What `item` takes as new Bausteine of the open file: the file itself (the virtual root, or its document row) takes\n * every kind, a Baustein what its kind accepts as children (`kindAccepts`). Nothing in a flat structure or without an editor.\n */\nexport function creatableBelow(item: TreeItem, env: { structure: string, hasEditor: boolean, activeId: string | undefined, kinds: readonly CompositeKind[] }): Creatable {\n const none: Creatable = { folder: false, document: false }\n if (env.structure !== 'composite' || !env.hasEditor) return none\n if (item.key === VIRTUAL_ROOT || (item.type === 'document' && item.id === env.activeId)) return { folder: true, document: true }\n if (item.type !== 'composite' || !item.blockId || !item.kind) return none\n return { folder: kindAccepts(env.kinds, item.kind, 'folder'), document: kindAccepts(env.kinds, item.kind, 'document') }\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\n/** What a dragged item carries: its document or folder, or in virtual mode its composite's block ID. */\nexport function dragPayloadOf(item: TreeItem): DragPayload {\n return item.type === 'composite'\n ? { type: 'composite', blockId: item.blockId! }\n : item.type === 'document'\n ? { type: 'document', id: item.id, path: item.folder ?? '' }\n : { type: 'folder', path: item.path! }\n}\n\n/** Whether `item` may be dragged. Virtual mode moves composites of the open document; blocks without an ID cannot be addressed. */\nexport function isDraggable(item: TreeItem, virtual: boolean): boolean {\n if (virtual) return item.type === 'composite' && !!item.blockId\n return item.type === 'document' || item.type === 'folder'\n}\n\n"],"mappings":"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"}
|
package/dist/tree/tree-model.js
CHANGED
|
@@ -349,6 +349,13 @@ function createTreeModel(options) {
|
|
|
349
349
|
id: null
|
|
350
350
|
});
|
|
351
351
|
}
|
|
352
|
+
const canMerge = (item) => item.type === "composite" && !!item.blockId && !!editor?.canMerge(item.blockId);
|
|
353
|
+
function merge(item) {
|
|
354
|
+
if (canMerge(item)) execute({
|
|
355
|
+
type: "mergeBlock",
|
|
356
|
+
id: item.blockId
|
|
357
|
+
});
|
|
358
|
+
}
|
|
352
359
|
function setSelectedFolder(path) {
|
|
353
360
|
patch({ selectedFolder: path });
|
|
354
361
|
}
|
|
@@ -624,6 +631,8 @@ function createTreeModel(options) {
|
|
|
624
631
|
clearBasketFilter: () => shared.clearBasketFilter(),
|
|
625
632
|
focus,
|
|
626
633
|
defocus,
|
|
634
|
+
canMerge,
|
|
635
|
+
merge,
|
|
627
636
|
select: selectItem,
|
|
628
637
|
activate(item) {
|
|
629
638
|
selectItem(item);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-model.js","names":[],"sources":["../../src/tree/tree-model.ts"],"sourcesContent":["import type { BlockTransfer } from '../core/block-transfer'\nimport { createStore, type ReadonlyStore } from '../core/store'\nimport { type Basket, basketById, type CompositeKind, kindAccepts } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type DropEnv, dropVerdict, type DropVerdict } from './drop-rules'\nimport { remapFolderPath } from './folder-paths'\nimport type { DocumentStore } from './store'\nimport {\n ancestorKeys,\n collectFolderPaths,\n creatableBelow,\n type DragPayload,\n dragPayloadOf,\n EMPTY_OUTLINE,\n filterKinds,\n folderNamesIn,\n isDraggable,\n joinPath,\n type Outline,\n parentPath,\n type TreeItem,\n VIRTUAL_ROOT,\n} from './tree-items'\nimport { deriveTree, filterQuery, type TreeRow, type TreeView } from './tree-rows'\nimport type { NodeFilters, TreeViewModel } from './tree-view-model'\nimport type { DocumentSummary, TreeFolder } from './types'\n\n// One framework-free model per `DocumentTree`, rendered by the Vue and the React component alike. It owns what the\n// tree remembers (open rows, kind filters, the title filter, the selection, a drag, a folder being renamed or created)\n// and checks all of it against the tree after every change of the store, the outline or the view state, so a key of\n// something that moved or vanished never lingers. It never calls the store or the editor to change something: it emits\n// `TreeCommand`s to the `execute` it was given and follows their result.\n\n/** What the model asks its host to do; the host reports whether it happened. */\nexport type TreeCommand\n = | { type: 'openDocument', id: string }\n | { type: 'createDocument', folder: string }\n | { type: 'moveDocument', id: string, folder: string }\n | { type: 'removeDocument', id: string, folder: string }\n | { type: 'createFolder', path: string }\n | { type: 'moveFolder', from: string, to: string }\n | { type: 'removeFolder', path: string }\n /** Moves the block into the Baustein `targetId` (`null` = the end of the document). */\n | { type: 'moveBlock', id: string, targetId: string | null }\n | { type: 'revealBlock', id: string | null, pos: number }\n /** Adds an Ordner-Baustein to the Baustein `targetId` (`null` = the end of the document). */\n | { type: 'createBausteinFolder', targetId: string | null }\n /** Lets the user pick files and imports them into the Baustein `targetId` (`null` = the end of the document); `false` when none were imported. */\n | { type: 'importFiles', targetId: string | null }\n | { type: 'warn', title: string, description: string }\n /** Inserts a Baustein dragged in from another surface or tree at the end of the Baustein `targetId` (`null` = the end of the document); `menu`: offer the transforms at `x`, `y` first. */\n | { type: 'transferBlock', payload: BlockTransfer, targetId: string | null, menu: boolean, x: number, y: number }\n /** Puts the editor into focus mode on the Baustein `id` (`null` = leaves focus mode); sent after the tree's own focus changed. */\n | { type: 'focusBlock', id: string | null }\n\n/** Runs a command and resolves whether it happened. Never rejects: the host reports its own failures. */\nexport type TreeExecutor = (command: TreeCommand) => boolean | Promise<boolean>\n\n/** What the model needs from the open document's editor. */\nexport interface TreeEditor {\n kinds: readonly CompositeKind[]\n /** The baskets a Baustein can be put into (colour dots on the rows). */\n baskets: readonly Basket[]\n /** The outline in `structure`: Bausteine in `'composite'` structure, else the headings. Empty once the editor is gone. */\n outline: (structure: string) => Outline\n /** Whether a block may move into a Baustein, judged on the live document. */\n moveVerdict: NonNullable<DropEnv['moveVerdict']>\n /** Whether the editor takes blocks from outside: it has the transfer feature's drop target and its `transferBlock` command. */\n canTransfer: () => boolean\n /** The block id of the Baustein the editor is focused on (focus mode), `null` without focus. The model mirrors it into the tree. */\n focusedId: () => string | null\n /** `listener` runs after the document or the editor's focus changed; returns the unsubscribe function. */\n onChange: (listener: () => void) => () => void\n}\n\n/** What the surrounding app decides: the open editor and the `blocks.structure` / `blocks.treeRoot` settings. */\nexport interface TreeContext {\n editor: TreeEditor | undefined\n structure: string\n treeRoot: string\n}\n\nexport interface TreeModelOptions {\n /** Words the menus, the drop refusals and the label of an untitled open document; the framework layer passes the context's `t`. */\n t: Translate\n store: DocumentStore\n /** The view state shared by the trees of one app: open store items and kind filters (persisted). */\n view: TreeViewModel\n execute: TreeExecutor\n context: TreeContext\n /** The folder new documents and folders go to; default the root. */\n selectedFolder?: string\n /** How long the outline waits for the document to settle before it is read again; default 150 ms. */\n outlineDebounceMs?: number\n}\n\n/** A drag in progress. */\nexport interface TreeDrag {\n source: DragPayload\n /** Key of the dragged row (`''` for a `foreign-block`, which has no row here) */\n sourceKey: string\n /** Key of the row under the pointer */\n hover: string | null\n /** What dropping on `hover` would do; `null` without a `hover` */\n verdict: DropVerdict | null\n}\n\n/** A folder being renamed. */\nexport interface TreeRename {\n /** Key of the folder's row */\n key: string\n path: string\n /** Its name when renaming started */\n base: string\n draft: string\n error: string | null\n}\n\n/** A folder being named. */\nexport interface TreeCreateFolder {\n /** Folder path the folder is created in */\n parent: string\n draft: string\n error: string | null\n}\n\n/** Everything a renderer needs. Fields below `view` are what the derived `view` was computed from and what `rowOf` reads. */\nexport interface TreeState {\n /** The loaded folder tree: `undefined` = not loaded yet, `null` = loading failed. */\n tree: TreeFolder | null | undefined\n documents: readonly DocumentSummary[]\n activeId: string | undefined\n /** The open document is the root (virtual mode) instead of the store's folders. */\n virtual: boolean\n structure: string\n kinds: readonly CompositeKind[]\n /** The open editor's baskets (colours of the row dots). */\n baskets: readonly Basket[]\n hasEditor: boolean\n outline: Outline\n nodeFilters: NodeFilters\n /** The basket ids the tree shows (`[]` = all). */\n basketFilter: readonly string[]\n /** Key of the focused row: the tree lists only its children. `null` = no focus. */\n focusKey: string | null\n expanded: readonly string[]\n collapsedOutline: readonly string[]\n /** What the title filter input holds. */\n filterText: string\n filterOverrides: Readonly<Record<string, boolean>>\n /** The folder new documents, folders and host imports go to. */\n selectedFolder: string\n /** Virtual mode: block ID of the selected Baustein that takes Bausteine; `null` = the document itself. */\n virtualTarget: string | null\n drag: TreeDrag | null\n renaming: TreeRename | null\n creatingFolder: TreeCreateFolder | null\n /** Key of the row whose deletion is running. */\n removing: string | null\n view: TreeView\n /** The unfiltered items from the top down to the focused one (the breadcrumb); empty without a focus. Derived with `view`. */\n focusPath: readonly TreeItem[]\n}\n\n/** One row of a row's menu. */\nexport interface TreeMenuEntry {\n label: string\n icon: string\n run: () => void\n}\n\n/** How a renderer draws one row. */\nexport interface TreeRowState extends TreeRow {\n /** The row's document is the open one. */\n active: boolean\n /** The row is the selected folder, or (virtual mode) the selected Baustein. */\n selected: boolean\n /** What dropping the drag on this row would do, while the pointer is over it. */\n drop: DropVerdict | null\n renaming: TreeRename | null\n removing: boolean\n /** The kinds the row's kind filter can hide below it, and the ones it hides. */\n filterKinds: readonly CompositeKind[]\n hiddenKinds: readonly string[]\n /** The row offers a kind filter. */\n showFilter: boolean\n}\n\nexport interface TreeModel {\n readonly state: ReadonlyStore<TreeState>\n /** The open editor or a setting changed. */\n setContext: (context: TreeContext) => void\n /** The host selected another folder (it controls the value). */\n setSelectedFolder: (path: string) => void\n\n // --- what is open, filtered, typed\n /** Opens or closes a row; while the title filter is set the choice is discarded when it is cleared. */\n setOpen: (key: string, open: boolean) => void\n toggle: (key: string) => void\n /** Opens the rows (also during a title filter); closed rows are left alone otherwise. */\n expand: (keys: readonly string[]) => void\n /** Filters the rows by title (case-insensitive substring); `''` clears it and restores the opened rows from before. */\n setFilterText: (text: string) => void\n setKindHidden: (key: string, kind: string, hidden: boolean) => void\n clearFilter: (key: string) => void\n clearAllFilters: () => void\n /** Shows (`shown`) or stops showing the Bausteine of the basket; no basket shown = no filter. */\n setBasketShown: (id: string, shown: boolean) => void\n clearBasketFilter: () => void\n /** Focuses a Baustein row: the tree lists only its children and the editor hides everything else. */\n focus: (key: string) => void\n /** Leaves focus mode (tree and editor). */\n defocus: () => void\n\n // --- rows\n /** A row was chosen (click, Enter). */\n select: (item: TreeItem) => void\n /** `select`, and a folder or the root toggles. */\n activate: (item: TreeItem) => void\n addMenu: (item: TreeItem) => TreeMenuEntry[][]\n remove: (item: TreeItem) => Promise<void>\n\n // --- drag and drop\n isDraggable: (item: TreeItem) => boolean\n /** Starts dragging `item`; returns what the drag carries, `null` when `item` cannot be dragged. */\n dragStart: (item: TreeItem) => DragPayload | null\n /** The pointer is over `item`; returns the verdict (`null` without a drag). Only an `ok` one is a drop target. */\n dragOver: (item: TreeItem) => DropVerdict | null\n /** The pointer left `item` for good (not into one of its parts). */\n dragLeave: (item: TreeItem) => void\n dragEnd: () => void\n drop: (item: TreeItem) => Promise<void>\n /**\n * A Baustein from another surface or tree (`BLOCK_MIME`) is over `item`; returns the verdict, `null` while this\n * tree's own drag is running. Only an `ok` one is a drop target.\n */\n foreignDragOver: (item: TreeItem) => DropVerdict | null\n /** Drops the foreign Baustein `payload` on `item`: inserts a copy at the end of that Baustein (`menu`: offers the transforms at `x`, `y` instead). */\n dropForeign: (item: TreeItem, payload: BlockTransfer, menu: boolean, x: number, y: number) => Promise<void>\n\n // --- renaming and creating folders\n startRename: (item: TreeItem) => void\n setRenameDraft: (text: string) => void\n cancelRename: () => void\n commitRename: () => Promise<void>\n /** The rename field lost focus: commit, or give up with a warning when the name is invalid. */\n blurRename: () => Promise<void>\n startCreateFolder: (parent: string) => void\n setFolderDraft: (text: string) => void\n cancelCreateFolder: () => void\n commitCreateFolder: () => Promise<void>\n\n dispose: () => void\n}\n\nconst NO_DOCUMENTS: readonly DocumentSummary[] = []\nconst NO_KINDS: readonly CompositeKind[] = []\nconst NO_BASKETS: readonly Basket[] = []\nconst NO_FILTERS: Readonly<Record<string, boolean>> = {}\nconst UNSET = Symbol('unset')\n\nconst isOutlineKey = (key: string) => key.startsWith('outline:')\n\n/** The state `core` holds: everything of `TreeState` but what is derived from the view. */\ntype TreeCore = Omit<TreeState, 'view' | 'focusPath'>\n\nconst withView = (c: TreeCore, view: TreeView): TreeState => ({ ...c, view, focusPath: view.focusPath })\n\nfunction sameVerdict(a: DropVerdict | null, b: DropVerdict | null): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return a.ok ? b.ok : !b.ok && a.reason === b.reason\n}\n\n/** A row's state in `state` (`item` is one of `state.view.items`). */\nexport function rowOf(state: TreeState, item: TreeItem, t: Translate): TreeRowState {\n const hidden = state.nodeFilters[item.key] ?? []\n const kinds = filterKinds(item, { structure: state.structure, activeId: state.activeId, kinds: state.kinds, t })\n const virtualTarget = state.virtual && (item.key === VIRTUAL_ROOT ? state.virtualTarget === null : !!item.blockId && item.blockId === state.virtualTarget)\n return {\n ...state.view.rows.get(item.key)!,\n active: item.id !== undefined && item.id === state.activeId,\n selected: (item.type !== 'document' && item.path === state.selectedFolder) || virtualTarget,\n drop: state.drag?.hover === item.key ? state.drag.verdict : null,\n renaming: state.renaming?.key === item.key ? state.renaming : null,\n removing: state.removing === item.key,\n filterKinds: kinds,\n hiddenKinds: hidden,\n showFilter: kinds.length > 0 && (item.type === 'root' || item.type === 'folder' || !!item.children?.length || hidden.length > 0),\n }\n}\n\n/** A tree model over `options.store`; call `dispose` when its tree goes away. */\nexport function createTreeModel(options: TreeModelOptions): TreeModel {\n const { store, view: shared, execute, t } = options\n const debounceMs = options.outlineDebounceMs ?? 150\n\n let editor = options.context.editor\n let structure = options.context.structure\n let treeRoot = options.context.treeRoot\n let unwatchEditor = editor?.onChange(onEditorChange)\n /** The focus the editor last showed that the tree took over (or already had); a change of the editor's focus is told apart from the tree's own by comparing against it. */\n let seenFocus: string | null = null\n let outlineTimer: ReturnType<typeof setTimeout> | undefined\n /** A folder move the host is running: until it is done, a selected folder at the old path follows to the new one. */\n let pendingMove: { from: string, to: string } | undefined\n let disposed = false\n\n const isVirtual = () => treeRoot === 'file' && structure === 'composite' && !!editor\n\n let core: TreeCore = {\n tree: store.tree.get(),\n documents: store.documents.get() ?? NO_DOCUMENTS,\n activeId: store.activeId.get(),\n virtual: isVirtual(),\n structure,\n kinds: editor?.kinds ?? NO_KINDS,\n baskets: editor?.baskets ?? NO_BASKETS,\n hasEditor: !!editor,\n outline: editor ? editor.outline(structure) : EMPTY_OUTLINE,\n nodeFilters: shared.nodeFilters.get(),\n basketFilter: shared.basketFilter.get(),\n focusKey: null,\n expanded: shared.expanded.get(),\n collapsedOutline: [],\n filterText: '',\n filterOverrides: NO_FILTERS,\n selectedFolder: options.selectedFolder ?? '',\n virtualTarget: null,\n drag: null,\n renaming: null,\n creatingFolder: null,\n removing: null,\n }\n let lastView: { deps: readonly unknown[], view: TreeView } | undefined\n let lastTree: TreeFolder | null | undefined | typeof UNSET = UNSET\n let folderPaths = new Set<string>()\n let lastActiveFolder: string | undefined | typeof UNSET = UNSET\n let lastActiveId: string | undefined | typeof UNSET = UNSET\n\n const dropEnv = (): DropEnv => ({ virtual: core.virtual, moveVerdict: editor?.moveVerdict, canTransfer: editor?.canTransfer() ?? false, t })\n\n function viewOf(c: TreeCore): TreeView {\n const deps = [c.tree, c.documents, c.activeId, c.virtual, c.outline, c.kinds, c.nodeFilters, c.basketFilter, c.baskets, c.focusKey, c.expanded, c.collapsedOutline, c.filterText, c.filterOverrides]\n if (lastView && deps.every((dep, i) => Object.is(dep, lastView!.deps[i]))) return lastView.view\n // A basket the open editor does not offer cannot be unchecked in the filter menu, so it never filters.\n const basketFilter = c.basketFilter.filter(id => basketById(c.baskets, id) !== undefined)\n const view = deriveTree({ ...c, basketFilter, t, select: selectItem })\n lastView = { deps, view }\n return view\n }\n\n const renameError = (draft: string, path: string, base: string, tree: TreeFolder | null | undefined) =>\n draft.trim() === base ? null : store.validateName(draft, folderNamesIn(tree, parentPath(path), path))\n const folderError = (draft: string, parent: string, tree: TreeFolder | null | undefined) =>\n draft.trim() ? store.validateName(draft, folderNamesIn(tree, parent)) : null\n\n /** Measures everything the tree remembers against the current tree: what is gone is dropped, what depends on the tree is recomputed. */\n function reconcile(c: TreeCore, view: TreeView): TreeCore {\n let next = c\n if (c.tree !== lastTree) {\n lastTree = c.tree\n folderPaths = new Set<string>()\n if (c.tree) collectFolderPaths(c.tree, folderPaths)\n if (c.tree && !folderPaths.has(next.selectedFolder)) {\n const followed = pendingMove ? remapFolderPath(next.selectedFolder, pendingMove.from, pendingMove.to) : ''\n next = { ...next, selectedFolder: folderPaths.has(followed) ? followed : '' }\n }\n }\n if (next.virtualTarget !== null && (!next.virtual || !view.keys.has(`outline:${next.virtualTarget}`))) next = { ...next, virtualTarget: null }\n if (next.focusKey !== null && !view.keys.has(next.focusKey)) next = { ...next, focusKey: null }\n if (next.renaming) {\n const r = next.renaming\n if (!view.keys.has(r.key)) next = { ...next, renaming: null }\n else {\n const error = renameError(r.draft, r.path, r.base, next.tree)\n if (error !== r.error) next = { ...next, renaming: { ...r, error } }\n }\n }\n if (next.creatingFolder) {\n const f = next.creatingFolder\n if (next.tree && !folderPaths.has(f.parent)) next = { ...next, creatingFolder: null }\n else {\n const error = folderError(f.draft, f.parent, next.tree)\n if (error !== f.error) next = { ...next, creatingFolder: { ...f, error } }\n }\n }\n if (next.drag) {\n const d = next.drag\n if ((d.source.type !== 'foreign-block' && !view.keys.has(d.sourceKey)) || (d.hover !== null && !view.keys.has(d.hover))) next = { ...next, drag: null }\n else if (d.hover !== null) {\n const target = view.rows.get(d.hover)?.item\n const verdict = target ? dropVerdict(d.source, target, dropEnv()) : d.verdict\n if (!sameVerdict(verdict, d.verdict)) next = { ...next, drag: { ...d, verdict } }\n }\n }\n return next\n }\n\n const state = createStore<TreeState>(withView(core, viewOf(core)))\n\n function publish(c: TreeCore) {\n if (disposed) return\n const view = viewOf(c)\n core = reconcile(c, view)\n const next = withView(core, view)\n const current = state.get()\n if ((Object.keys(next) as (keyof TreeState)[]).every(key => Object.is(next[key], current[key]))) return\n state.set(next)\n }\n\n const patch = (changes: Partial<TreeCore>) => publish({ ...core, ...changes })\n\n // --- sources ---------------------------------------------------------------------------------------------------\n\n /** Reads the store and the shared view state; what opening a document shows is opened. */\n function sync() {\n if (disposed) return\n const documents = store.documents.get() ?? NO_DOCUMENTS\n const activeId = store.activeId.get()\n publish({ ...core, tree: store.tree.get(), documents, activeId, nodeFilters: shared.nodeFilters.get(), basketFilter: shared.basketFilter.get(), expanded: shared.expanded.get() })\n // Opening a document (also by URL, or after a move) shows it: its folders open and its folder becomes the selected one.\n const activeFolder = documents.find(d => d.id === activeId)?.folder\n if (activeFolder !== lastActiveFolder) {\n lastActiveFolder = activeFolder\n expand(ancestorKeys(activeFolder ?? ''))\n if (activeFolder !== undefined) setSelectedFolder(activeFolder)\n }\n if (activeId !== lastActiveId) {\n lastActiveId = activeId\n if (activeId) expand([`doc:${activeId}`])\n }\n }\n\n const unsubscribe = [\n store.tree.subscribe(sync),\n store.documents.subscribe(sync),\n store.activeId.subscribe(sync),\n shared.nodeFilters.subscribe(sync),\n shared.basketFilter.subscribe(sync),\n shared.expanded.subscribe(sync),\n ]\n\n function readOutline() {\n patch({ outline: editor ? editor.outline(structure) : EMPTY_OUTLINE })\n mirrorFocus()\n }\n\n /**\n * The editor's focus mode changed on its own (the focus chip, a block's „Fokussieren“, the focused Baustein vanished):\n * the tree follows without a command. A Baustein the outline does not know yet is retried after the next outline read.\n */\n function mirrorFocus() {\n const id = editor?.focusedId() ?? null\n if (id === seenFocus) return\n if (id === null) {\n seenFocus = null\n if (core.focusKey !== null) patch({ focusKey: null })\n return\n }\n const key = `outline:${id}`\n if (core.focusKey !== key) {\n const item = state.get().view.index.get(key)?.item\n if (item?.type !== 'composite') return\n patch({ focusKey: key })\n }\n seenFocus = id\n }\n\n // The outline is read when the document changed and has been quiet for a moment; a drag in progress is checked at once, and so is the editor's focus.\n function onEditorChange() {\n clearTimeout(outlineTimer)\n outlineTimer = setTimeout(readOutline, debounceMs)\n if (core.drag) publish(core)\n mirrorFocus()\n }\n\n function setContext(context: TreeContext) {\n if (context.editor === editor && context.structure === structure && context.treeRoot === treeRoot) return\n const editorChanged = context.editor !== editor\n const wasVirtual = core.virtual\n editor = context.editor\n structure = context.structure\n treeRoot = context.treeRoot\n clearTimeout(outlineTimer)\n if (editorChanged) {\n seenFocus = null\n unwatchEditor?.()\n unwatchEditor = editor?.onChange(onEditorChange)\n }\n patch({\n virtual: isVirtual(),\n structure,\n kinds: editor?.kinds ?? NO_KINDS,\n baskets: editor?.baskets ?? NO_BASKETS,\n ...(editorChanged ? { focusKey: null } : {}),\n hasEditor: !!editor,\n outline: editor ? editor.outline(structure) : EMPTY_OUTLINE,\n })\n if (core.virtual && !wasVirtual) expand([VIRTUAL_ROOT])\n mirrorFocus()\n }\n\n // --- open, filtered, typed -------------------------------------------------------------------------------------\n\n const filtering = () => filterQuery(core.filterText) !== ''\n\n function setOpen(key: string, open: boolean) {\n if (filtering()) patch({ filterOverrides: { ...core.filterOverrides, [key]: open } })\n else if (isOutlineKey(key)) patch({ collapsedOutline: open ? core.collapsedOutline.filter(k => k !== key) : [...core.collapsedOutline.filter(k => k !== key), key] })\n else if (open) shared.expand([key])\n else shared.expanded.set(shared.expanded.get().filter(k => k !== key))\n }\n\n function expand(keys: readonly string[]) {\n const outlineKeys = keys.filter(isOutlineKey)\n const { filterOverrides } = core\n const overridden = keys.some(key => key in filterOverrides)\n if (overridden || outlineKeys.some(key => core.collapsedOutline.includes(key))) {\n patch({\n collapsedOutline: core.collapsedOutline.filter(key => !outlineKeys.includes(key)),\n filterOverrides: overridden ? Object.fromEntries(Object.entries(filterOverrides).filter(([key]) => !keys.includes(key))) : filterOverrides,\n })\n }\n shared.expand(keys.filter(key => !isOutlineKey(key)))\n }\n\n function setFilterText(text: string) {\n patch({ filterText: text, filterOverrides: filterQuery(text) ? core.filterOverrides : NO_FILTERS })\n }\n\n // --- focus -----------------------------------------------------------------------------------------------------\n\n /** Only a Baustein the editor can address (it has a block ID) can be focused. */\n function focus(key: string) {\n const item = state.get().view.index.get(key)?.item\n if (item?.type !== 'composite' || !item.blockId) return\n patch({ focusKey: key })\n void execute({ type: 'focusBlock', id: item.blockId })\n }\n\n function defocus() {\n if (core.focusKey === null) return\n patch({ focusKey: null })\n void execute({ type: 'focusBlock', id: null })\n }\n\n // --- selection -------------------------------------------------------------------------------------------------\n\n function setSelectedFolder(path: string) {\n patch({ selectedFolder: path })\n }\n\n function selectItem(item: TreeItem) {\n if (core.renaming?.key === item.key) return\n if (item.key === VIRTUAL_ROOT) return patch({ virtualTarget: null })\n if (item.type === 'document') {\n setSelectedFolder(item.folder ?? '')\n void execute({ type: 'openDocument', id: item.id! })\n }\n else if (item.type === 'composite' || item.type === 'heading') {\n void execute({ type: 'revealBlock', id: item.blockId ?? null, pos: item.pos! })\n const takesBausteine = !!item.kind && core.kinds.some(kind => kindAccepts(core.kinds, item.kind!, kind.id))\n if (core.virtual && item.blockId && takesBausteine) patch({ virtualTarget: item.blockId })\n }\n else {\n setSelectedFolder(item.path ?? '')\n }\n }\n\n function toggle(key: string) {\n setOpen(key, !state.get().view.rows.get(key)?.open)\n }\n\n /** A folder moved from `from` to `to`: its open/selected state follows it. */\n function followFolderMove(from: string, to: string) {\n shared.followFolderMove(from, to)\n patch({\n selectedFolder: remapFolderPath(core.selectedFolder, from, to),\n filterOverrides: Object.fromEntries(Object.entries(core.filterOverrides).map(([key, open]) => [key.startsWith('folder:') ? `folder:${remapFolderPath(key.slice(7), from, to)}` : key, open])),\n })\n expand(ancestorKeys(parentPath(to)))\n }\n\n async function moveFolder(from: string, to: string) {\n pendingMove = { from, to }\n const moved = await execute({ type: 'moveFolder', from, to })\n pendingMove = undefined\n if (moved) followFolderMove(from, to)\n }\n\n // --- drag and drop ---------------------------------------------------------------------------------------------\n\n function dragStart(item: TreeItem) {\n if (core.renaming?.key === item.key || !isDraggable(item, core.virtual)) return null\n const source = dragPayloadOf(item)\n patch({ drag: { source, sourceKey: item.key, hover: null, verdict: null } })\n return source\n }\n\n function dragOver(item: TreeItem) {\n const drag = core.drag\n if (!drag) return null\n const verdict = dropVerdict(drag.source, item, dropEnv())\n if (drag.hover !== item.key || !sameVerdict(drag.verdict, verdict)) patch({ drag: { ...drag, hover: item.key, verdict } })\n return verdict\n }\n\n function foreignDragOver(item: TreeItem) {\n const drag = core.drag\n if (drag && drag.source.type !== 'foreign-block') return null\n const source: DragPayload = { type: 'foreign-block' }\n const verdict = dropVerdict(source, item, dropEnv())\n if (!drag || drag.hover !== item.key || !sameVerdict(drag.verdict, verdict)) patch({ drag: { source, sourceKey: '', hover: item.key, verdict } })\n return verdict\n }\n\n async function dropForeign(item: TreeItem, payload: BlockTransfer, menu: boolean, x: number, y: number) {\n // This tree's own drag (a row also writes the block payload) is a move, not a transfer.\n if (core.drag && core.drag.source.type !== 'foreign-block') return\n patch({ drag: null })\n if (!dropVerdict({ type: 'foreign-block' }, item, dropEnv()).ok) return\n const targetId = item.key === VIRTUAL_ROOT ? null : item.blockId!\n // A throwing executor (the contract says it never does) must not leave the drop handler with an unhandled rejection.\n let done = false\n try {\n done = await execute({ type: 'transferBlock', payload, targetId, menu, x, y })\n }\n catch {\n return\n }\n if (done && targetId && !menu) expand([`outline:${targetId}`])\n }\n\n async function drop(item: TreeItem) {\n const drag = core.drag\n patch({ drag: null })\n if (!drag || !dropVerdict(drag.source, item, dropEnv()).ok) return\n const { source } = drag\n if (source.type === 'composite') {\n const targetId = item.key === VIRTUAL_ROOT ? null : item.blockId!\n if (await execute({ type: 'moveBlock', id: source.blockId, targetId }) && targetId) expand([`outline:${targetId}`])\n return\n }\n // A foreign Baustein is dropped through `dropForeign`, which reads its payload; it is no move of this tree.\n if (source.type === 'foreign-block') return\n const targetPath = item.path!\n if (source.type === 'document') {\n if (await execute({ type: 'moveDocument', id: source.id!, folder: targetPath })) expand(ancestorKeys(targetPath))\n return\n }\n await moveFolder(source.path, joinPath(targetPath, source.path.slice(source.path.lastIndexOf('/') + 1)))\n }\n\n // --- renaming, creating ----------------------------------------------------------------------------------------\n\n function startRename(item: TreeItem) {\n if (item.type !== 'folder') return\n patch({ renaming: { key: item.key, path: item.path!, base: item.label, draft: item.label, error: null } })\n }\n\n async function commitRename() {\n const r = core.renaming\n // An invalid name keeps the field open with its inline error.\n if (!r || r.error) return\n const name = r.draft.trim()\n patch({ renaming: null })\n if (name !== r.base) await moveFolder(r.path, joinPath(parentPath(r.path), name))\n }\n\n async function blurRename() {\n const r = core.renaming\n if (!r) return\n if (r.error) {\n void execute({ type: 'warn', title: t('tree.folder.renameFailed'), description: r.error })\n patch({ renaming: null })\n return\n }\n await commitRename()\n }\n\n function startCreateFolder(parent: string) {\n patch({ selectedFolder: parent, creatingFolder: { parent, draft: '', error: null } })\n }\n\n async function commitCreateFolder() {\n const f = core.creatingFolder\n if (!f) return\n if (!f.draft.trim()) return patch({ creatingFolder: null })\n if (f.error) return\n patch({ creatingFolder: null })\n if (await execute({ type: 'createFolder', path: joinPath(f.parent, f.draft.trim()) })) expand(ancestorKeys(f.parent))\n }\n\n // --- menus, deleting -------------------------------------------------------------------------------------------\n\n async function createDocumentIn(folder: string) {\n setSelectedFolder(folder)\n if (await execute({ type: 'createDocument', folder })) expand(ancestorKeys(folder))\n }\n\n /** Runs `command` for the Baustein of `item` (the file itself when it has none) and shows the result. */\n async function inComposite(item: TreeItem, command: TreeCommand) {\n if (await execute(command)) expand([item.blockId ? `outline:${item.blockId}` : item.key])\n }\n\n /**\n * What can be created below `item`: folders of the store take documents and sub-folders, the open file (as the root or\n * as a document row) and its Bausteine take what their kind accepts. Other rows take nothing. Folders also list\n * „Umbenennen“ here: renaming a document happens in the editor's title field.\n */\n function addMenu(item: TreeItem): TreeMenuEntry[][] {\n const groups: TreeMenuEntry[][] = []\n if (item.key !== VIRTUAL_ROOT && (item.type === 'root' || item.type === 'folder')) {\n const path = item.path!\n groups.push([\n { label: t('tree.menu.newDocument'), icon: 'i-lucide-file-plus', run: () => void createDocumentIn(path) },\n { label: item.type === 'root' ? t('tree.menu.newFolder') : t('tree.menu.newSubfolder'), icon: 'i-lucide-folder-plus', run: () => startCreateFolder(path) },\n ])\n }\n else {\n const can = creatableBelow(item, { structure: core.structure, hasEditor: core.hasEditor, activeId: core.activeId, kinds: core.kinds })\n const targetId = item.blockId ?? null\n const entries: TreeMenuEntry[] = []\n if (can.folder) entries.push({ label: t('tree.menu.newCompositeFolder'), icon: 'i-lucide-folder-plus', run: () => void inComposite(item, { type: 'createBausteinFolder', targetId }) })\n if (can.document) entries.push({ label: t('tree.menu.importFile'), icon: 'i-lucide-file-up', run: () => void inComposite(item, { type: 'importFiles', targetId }) })\n if (entries.length) groups.push(entries)\n }\n if (item.type === 'folder') groups.push([{ label: t('tree.menu.rename'), icon: 'i-lucide-pencil', run: () => startRename(item) }])\n return groups\n }\n\n async function remove(item: TreeItem) {\n patch({ removing: item.key })\n try {\n await execute(item.type === 'document' ? { type: 'removeDocument', id: item.id!, folder: item.folder ?? '' } : { type: 'removeFolder', path: item.path! })\n }\n finally {\n patch({ removing: null })\n }\n }\n\n sync()\n mirrorFocus()\n\n return {\n state,\n setContext,\n setSelectedFolder,\n setOpen,\n toggle,\n expand,\n setFilterText,\n setKindHidden: (key, kind, hidden) => shared.setKindHidden(key, kind, hidden),\n clearFilter: key => shared.clearFilter(key),\n clearAllFilters: () => shared.clearAllFilters(),\n setBasketShown: (id, shown) => shared.setBasketShown(id, shown),\n clearBasketFilter: () => shared.clearBasketFilter(),\n focus,\n defocus,\n select: selectItem,\n activate(item) {\n selectItem(item)\n if (item.type === 'root' || item.type === 'folder') toggle(item.key)\n },\n addMenu,\n remove,\n isDraggable: item => core.renaming?.key !== item.key && isDraggable(item, core.virtual),\n dragStart,\n dragOver,\n dragLeave(item) {\n // A foreign drag only exists while it is over this tree (no `dragend` reaches here), so leaving a row ends it; the next row's `foreignDragOver` starts it again.\n if (core.drag?.source.type === 'foreign-block') patch({ drag: null })\n else if (core.drag?.hover === item.key) patch({ drag: { ...core.drag, hover: null, verdict: null } })\n },\n dragEnd: () => patch({ drag: null }),\n drop,\n foreignDragOver,\n dropForeign,\n startRename,\n setRenameDraft(text) {\n const r = core.renaming\n if (r) patch({ renaming: { ...r, draft: text, error: renameError(text, r.path, r.base, core.tree) } })\n },\n cancelRename: () => patch({ renaming: null }),\n commitRename,\n blurRename,\n startCreateFolder,\n setFolderDraft(text) {\n const f = core.creatingFolder\n if (f) patch({ creatingFolder: { ...f, draft: text, error: folderError(text, f.parent, core.tree) } })\n },\n cancelCreateFolder: () => patch({ creatingFolder: null }),\n commitCreateFolder,\n dispose() {\n disposed = true\n clearTimeout(outlineTimer)\n unwatchEditor?.()\n for (const stop of unsubscribe) stop()\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tree-model.js","names":[],"sources":["../../src/tree/tree-model.ts"],"sourcesContent":["import type { BlockTransfer } from '../core/block-transfer'\nimport { createStore, type ReadonlyStore } from '../core/store'\nimport { type Basket, basketById, type CompositeKind, kindAccepts } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type DropEnv, dropVerdict, type DropVerdict } from './drop-rules'\nimport { remapFolderPath } from './folder-paths'\nimport type { DocumentStore } from './store'\nimport {\n ancestorKeys,\n collectFolderPaths,\n creatableBelow,\n type DragPayload,\n dragPayloadOf,\n EMPTY_OUTLINE,\n filterKinds,\n folderNamesIn,\n isDraggable,\n joinPath,\n type Outline,\n parentPath,\n type TreeItem,\n VIRTUAL_ROOT,\n} from './tree-items'\nimport { deriveTree, filterQuery, type TreeRow, type TreeView } from './tree-rows'\nimport type { NodeFilters, TreeViewModel } from './tree-view-model'\nimport type { DocumentSummary, TreeFolder } from './types'\n\n// One framework-free model per `DocumentTree`, rendered by the Vue and the React component alike. It owns what the\n// tree remembers (open rows, kind filters, the title filter, the selection, a drag, a folder being renamed or created)\n// and checks all of it against the tree after every change of the store, the outline or the view state, so a key of\n// something that moved or vanished never lingers. It never calls the store or the editor to change something: it emits\n// `TreeCommand`s to the `execute` it was given and follows their result.\n\n/** What the model asks its host to do; the host reports whether it happened. */\nexport type TreeCommand\n = | { type: 'openDocument', id: string }\n | { type: 'createDocument', folder: string }\n | { type: 'moveDocument', id: string, folder: string }\n | { type: 'removeDocument', id: string, folder: string }\n | { type: 'createFolder', path: string }\n | { type: 'moveFolder', from: string, to: string }\n | { type: 'removeFolder', path: string }\n /** Moves the block into the Baustein `targetId` (`null` = the end of the document). */\n | { type: 'moveBlock', id: string, targetId: string | null }\n | { type: 'revealBlock', id: string | null, pos: number }\n /** Adds an Ordner-Baustein to the Baustein `targetId` (`null` = the end of the document). */\n | { type: 'createBausteinFolder', targetId: string | null }\n /** Lets the user pick files and imports them into the Baustein `targetId` (`null` = the end of the document); `false` when none were imported. */\n | { type: 'importFiles', targetId: string | null }\n | { type: 'warn', title: string, description: string }\n /** Inserts a Baustein dragged in from another surface or tree at the end of the Baustein `targetId` (`null` = the end of the document); `menu`: offer the transforms at `x`, `y` first. */\n | { type: 'transferBlock', payload: BlockTransfer, targetId: string | null, menu: boolean, x: number, y: number }\n /** Puts the editor into focus mode on the Baustein `id` (`null` = leaves focus mode); sent after the tree's own focus changed. */\n | { type: 'focusBlock', id: string | null }\n /** The Baustein `id` absorbs the Baustein that follows it (`mergeCompositeWithNext`); `false` when the editor refuses. */\n | { type: 'mergeBlock', id: string }\n\n/** Runs a command and resolves whether it happened. Never rejects: the host reports its own failures. */\nexport type TreeExecutor = (command: TreeCommand) => boolean | Promise<boolean>\n\n/** What the model needs from the open document's editor. */\nexport interface TreeEditor {\n kinds: readonly CompositeKind[]\n /** The baskets a Baustein can be put into (colour dots on the rows). */\n baskets: readonly Basket[]\n /** The outline in `structure`: Bausteine in `'composite'` structure, else the headings. Empty once the editor is gone. */\n outline: (structure: string) => Outline\n /** Whether a block may move into a Baustein, judged on the live document. */\n moveVerdict: NonNullable<DropEnv['moveVerdict']>\n /** Whether the editor takes blocks from outside: it has the transfer feature's drop target and its `transferBlock` command. */\n canTransfer: () => boolean\n /** The block id of the Baustein the editor is focused on (focus mode), `null` without focus. The model mirrors it into the tree. */\n focusedId: () => string | null\n /** Whether the Baustein `id` can absorb the Baustein that follows it, judged on the live document. */\n canMerge: (id: string) => boolean\n /** `listener` runs after the document or the editor's focus changed; returns the unsubscribe function. */\n onChange: (listener: () => void) => () => void\n}\n\n/** What the surrounding app decides: the open editor and the `blocks.structure` / `blocks.treeRoot` settings. */\nexport interface TreeContext {\n editor: TreeEditor | undefined\n structure: string\n treeRoot: string\n}\n\nexport interface TreeModelOptions {\n /** Words the menus, the drop refusals and the label of an untitled open document; the framework layer passes the context's `t`. */\n t: Translate\n store: DocumentStore\n /** The view state shared by the trees of one app: open store items and kind filters (persisted). */\n view: TreeViewModel\n execute: TreeExecutor\n context: TreeContext\n /** The folder new documents and folders go to; default the root. */\n selectedFolder?: string\n /** How long the outline waits for the document to settle before it is read again; default 150 ms. */\n outlineDebounceMs?: number\n}\n\n/** A drag in progress. */\nexport interface TreeDrag {\n source: DragPayload\n /** Key of the dragged row (`''` for a `foreign-block`, which has no row here) */\n sourceKey: string\n /** Key of the row under the pointer */\n hover: string | null\n /** What dropping on `hover` would do; `null` without a `hover` */\n verdict: DropVerdict | null\n}\n\n/** A folder being renamed. */\nexport interface TreeRename {\n /** Key of the folder's row */\n key: string\n path: string\n /** Its name when renaming started */\n base: string\n draft: string\n error: string | null\n}\n\n/** A folder being named. */\nexport interface TreeCreateFolder {\n /** Folder path the folder is created in */\n parent: string\n draft: string\n error: string | null\n}\n\n/** Everything a renderer needs. Fields below `view` are what the derived `view` was computed from and what `rowOf` reads. */\nexport interface TreeState {\n /** The loaded folder tree: `undefined` = not loaded yet, `null` = loading failed. */\n tree: TreeFolder | null | undefined\n documents: readonly DocumentSummary[]\n activeId: string | undefined\n /** The open document is the root (virtual mode) instead of the store's folders. */\n virtual: boolean\n structure: string\n kinds: readonly CompositeKind[]\n /** The open editor's baskets (colours of the row dots). */\n baskets: readonly Basket[]\n hasEditor: boolean\n outline: Outline\n nodeFilters: NodeFilters\n /** The basket ids the tree shows (`[]` = all). */\n basketFilter: readonly string[]\n /** Key of the focused row: the tree lists only its children. `null` = no focus. */\n focusKey: string | null\n expanded: readonly string[]\n collapsedOutline: readonly string[]\n /** What the title filter input holds. */\n filterText: string\n filterOverrides: Readonly<Record<string, boolean>>\n /** The folder new documents, folders and host imports go to. */\n selectedFolder: string\n /** Virtual mode: block ID of the selected Baustein that takes Bausteine; `null` = the document itself. */\n virtualTarget: string | null\n drag: TreeDrag | null\n renaming: TreeRename | null\n creatingFolder: TreeCreateFolder | null\n /** Key of the row whose deletion is running. */\n removing: string | null\n view: TreeView\n /** The unfiltered items from the top down to the focused one (the breadcrumb); empty without a focus. Derived with `view`. */\n focusPath: readonly TreeItem[]\n}\n\n/** One row of a row's menu. */\nexport interface TreeMenuEntry {\n label: string\n icon: string\n run: () => void\n}\n\n/** How a renderer draws one row. */\nexport interface TreeRowState extends TreeRow {\n /** The row's document is the open one. */\n active: boolean\n /** The row is the selected folder, or (virtual mode) the selected Baustein. */\n selected: boolean\n /** What dropping the drag on this row would do, while the pointer is over it. */\n drop: DropVerdict | null\n renaming: TreeRename | null\n removing: boolean\n /** The kinds the row's kind filter can hide below it, and the ones it hides. */\n filterKinds: readonly CompositeKind[]\n hiddenKinds: readonly string[]\n /** The row offers a kind filter. */\n showFilter: boolean\n}\n\nexport interface TreeModel {\n readonly state: ReadonlyStore<TreeState>\n /** The open editor or a setting changed. */\n setContext: (context: TreeContext) => void\n /** The host selected another folder (it controls the value). */\n setSelectedFolder: (path: string) => void\n\n // --- what is open, filtered, typed\n /** Opens or closes a row; while the title filter is set the choice is discarded when it is cleared. */\n setOpen: (key: string, open: boolean) => void\n toggle: (key: string) => void\n /** Opens the rows (also during a title filter); closed rows are left alone otherwise. */\n expand: (keys: readonly string[]) => void\n /** Filters the rows by title (case-insensitive substring); `''` clears it and restores the opened rows from before. */\n setFilterText: (text: string) => void\n setKindHidden: (key: string, kind: string, hidden: boolean) => void\n clearFilter: (key: string) => void\n clearAllFilters: () => void\n /** Shows (`shown`) or stops showing the Bausteine of the basket; no basket shown = no filter. */\n setBasketShown: (id: string, shown: boolean) => void\n clearBasketFilter: () => void\n /** Focuses a Baustein row: the tree lists only its children and the editor hides everything else. */\n focus: (key: string) => void\n /** Leaves focus mode (tree and editor). */\n defocus: () => void\n /** Whether the Baustein row can be merged with the Baustein after it (it has a block ID and the editor accepts the merge). */\n canMerge: (item: TreeItem) => boolean\n /** Merges the Baustein row with the Baustein after it: the row keeps its title, id and meta and takes over the other one's body and sub-Bausteine. */\n merge: (item: TreeItem) => void\n\n // --- rows\n /** A row was chosen (click, Enter). */\n select: (item: TreeItem) => void\n /** `select`, and a folder or the root toggles. */\n activate: (item: TreeItem) => void\n addMenu: (item: TreeItem) => TreeMenuEntry[][]\n remove: (item: TreeItem) => Promise<void>\n\n // --- drag and drop\n isDraggable: (item: TreeItem) => boolean\n /** Starts dragging `item`; returns what the drag carries, `null` when `item` cannot be dragged. */\n dragStart: (item: TreeItem) => DragPayload | null\n /** The pointer is over `item`; returns the verdict (`null` without a drag). Only an `ok` one is a drop target. */\n dragOver: (item: TreeItem) => DropVerdict | null\n /** The pointer left `item` for good (not into one of its parts). */\n dragLeave: (item: TreeItem) => void\n dragEnd: () => void\n drop: (item: TreeItem) => Promise<void>\n /**\n * A Baustein from another surface or tree (`BLOCK_MIME`) is over `item`; returns the verdict, `null` while this\n * tree's own drag is running. Only an `ok` one is a drop target.\n */\n foreignDragOver: (item: TreeItem) => DropVerdict | null\n /** Drops the foreign Baustein `payload` on `item`: inserts a copy at the end of that Baustein (`menu`: offers the transforms at `x`, `y` instead). */\n dropForeign: (item: TreeItem, payload: BlockTransfer, menu: boolean, x: number, y: number) => Promise<void>\n\n // --- renaming and creating folders\n startRename: (item: TreeItem) => void\n setRenameDraft: (text: string) => void\n cancelRename: () => void\n commitRename: () => Promise<void>\n /** The rename field lost focus: commit, or give up with a warning when the name is invalid. */\n blurRename: () => Promise<void>\n startCreateFolder: (parent: string) => void\n setFolderDraft: (text: string) => void\n cancelCreateFolder: () => void\n commitCreateFolder: () => Promise<void>\n\n dispose: () => void\n}\n\nconst NO_DOCUMENTS: readonly DocumentSummary[] = []\nconst NO_KINDS: readonly CompositeKind[] = []\nconst NO_BASKETS: readonly Basket[] = []\nconst NO_FILTERS: Readonly<Record<string, boolean>> = {}\nconst UNSET = Symbol('unset')\n\nconst isOutlineKey = (key: string) => key.startsWith('outline:')\n\n/** The state `core` holds: everything of `TreeState` but what is derived from the view. */\ntype TreeCore = Omit<TreeState, 'view' | 'focusPath'>\n\nconst withView = (c: TreeCore, view: TreeView): TreeState => ({ ...c, view, focusPath: view.focusPath })\n\nfunction sameVerdict(a: DropVerdict | null, b: DropVerdict | null): boolean {\n if (a === b) return true\n if (!a || !b) return false\n return a.ok ? b.ok : !b.ok && a.reason === b.reason\n}\n\n/** A row's state in `state` (`item` is one of `state.view.items`). */\nexport function rowOf(state: TreeState, item: TreeItem, t: Translate): TreeRowState {\n const hidden = state.nodeFilters[item.key] ?? []\n const kinds = filterKinds(item, { structure: state.structure, activeId: state.activeId, kinds: state.kinds, t })\n const virtualTarget = state.virtual && (item.key === VIRTUAL_ROOT ? state.virtualTarget === null : !!item.blockId && item.blockId === state.virtualTarget)\n return {\n ...state.view.rows.get(item.key)!,\n active: item.id !== undefined && item.id === state.activeId,\n selected: (item.type !== 'document' && item.path === state.selectedFolder) || virtualTarget,\n drop: state.drag?.hover === item.key ? state.drag.verdict : null,\n renaming: state.renaming?.key === item.key ? state.renaming : null,\n removing: state.removing === item.key,\n filterKinds: kinds,\n hiddenKinds: hidden,\n showFilter: kinds.length > 0 && (item.type === 'root' || item.type === 'folder' || !!item.children?.length || hidden.length > 0),\n }\n}\n\n/** A tree model over `options.store`; call `dispose` when its tree goes away. */\nexport function createTreeModel(options: TreeModelOptions): TreeModel {\n const { store, view: shared, execute, t } = options\n const debounceMs = options.outlineDebounceMs ?? 150\n\n let editor = options.context.editor\n let structure = options.context.structure\n let treeRoot = options.context.treeRoot\n let unwatchEditor = editor?.onChange(onEditorChange)\n /** The focus the editor last showed that the tree took over (or already had); a change of the editor's focus is told apart from the tree's own by comparing against it. */\n let seenFocus: string | null = null\n let outlineTimer: ReturnType<typeof setTimeout> | undefined\n /** A folder move the host is running: until it is done, a selected folder at the old path follows to the new one. */\n let pendingMove: { from: string, to: string } | undefined\n let disposed = false\n\n const isVirtual = () => treeRoot === 'file' && structure === 'composite' && !!editor\n\n let core: TreeCore = {\n tree: store.tree.get(),\n documents: store.documents.get() ?? NO_DOCUMENTS,\n activeId: store.activeId.get(),\n virtual: isVirtual(),\n structure,\n kinds: editor?.kinds ?? NO_KINDS,\n baskets: editor?.baskets ?? NO_BASKETS,\n hasEditor: !!editor,\n outline: editor ? editor.outline(structure) : EMPTY_OUTLINE,\n nodeFilters: shared.nodeFilters.get(),\n basketFilter: shared.basketFilter.get(),\n focusKey: null,\n expanded: shared.expanded.get(),\n collapsedOutline: [],\n filterText: '',\n filterOverrides: NO_FILTERS,\n selectedFolder: options.selectedFolder ?? '',\n virtualTarget: null,\n drag: null,\n renaming: null,\n creatingFolder: null,\n removing: null,\n }\n let lastView: { deps: readonly unknown[], view: TreeView } | undefined\n let lastTree: TreeFolder | null | undefined | typeof UNSET = UNSET\n let folderPaths = new Set<string>()\n let lastActiveFolder: string | undefined | typeof UNSET = UNSET\n let lastActiveId: string | undefined | typeof UNSET = UNSET\n\n const dropEnv = (): DropEnv => ({ virtual: core.virtual, moveVerdict: editor?.moveVerdict, canTransfer: editor?.canTransfer() ?? false, t })\n\n function viewOf(c: TreeCore): TreeView {\n const deps = [c.tree, c.documents, c.activeId, c.virtual, c.outline, c.kinds, c.nodeFilters, c.basketFilter, c.baskets, c.focusKey, c.expanded, c.collapsedOutline, c.filterText, c.filterOverrides]\n if (lastView && deps.every((dep, i) => Object.is(dep, lastView!.deps[i]))) return lastView.view\n // A basket the open editor does not offer cannot be unchecked in the filter menu, so it never filters.\n const basketFilter = c.basketFilter.filter(id => basketById(c.baskets, id) !== undefined)\n const view = deriveTree({ ...c, basketFilter, t, select: selectItem })\n lastView = { deps, view }\n return view\n }\n\n const renameError = (draft: string, path: string, base: string, tree: TreeFolder | null | undefined) =>\n draft.trim() === base ? null : store.validateName(draft, folderNamesIn(tree, parentPath(path), path))\n const folderError = (draft: string, parent: string, tree: TreeFolder | null | undefined) =>\n draft.trim() ? store.validateName(draft, folderNamesIn(tree, parent)) : null\n\n /** Measures everything the tree remembers against the current tree: what is gone is dropped, what depends on the tree is recomputed. */\n function reconcile(c: TreeCore, view: TreeView): TreeCore {\n let next = c\n if (c.tree !== lastTree) {\n lastTree = c.tree\n folderPaths = new Set<string>()\n if (c.tree) collectFolderPaths(c.tree, folderPaths)\n if (c.tree && !folderPaths.has(next.selectedFolder)) {\n const followed = pendingMove ? remapFolderPath(next.selectedFolder, pendingMove.from, pendingMove.to) : ''\n next = { ...next, selectedFolder: folderPaths.has(followed) ? followed : '' }\n }\n }\n if (next.virtualTarget !== null && (!next.virtual || !view.keys.has(`outline:${next.virtualTarget}`))) next = { ...next, virtualTarget: null }\n if (next.focusKey !== null && !view.keys.has(next.focusKey)) next = { ...next, focusKey: null }\n if (next.renaming) {\n const r = next.renaming\n if (!view.keys.has(r.key)) next = { ...next, renaming: null }\n else {\n const error = renameError(r.draft, r.path, r.base, next.tree)\n if (error !== r.error) next = { ...next, renaming: { ...r, error } }\n }\n }\n if (next.creatingFolder) {\n const f = next.creatingFolder\n if (next.tree && !folderPaths.has(f.parent)) next = { ...next, creatingFolder: null }\n else {\n const error = folderError(f.draft, f.parent, next.tree)\n if (error !== f.error) next = { ...next, creatingFolder: { ...f, error } }\n }\n }\n if (next.drag) {\n const d = next.drag\n if ((d.source.type !== 'foreign-block' && !view.keys.has(d.sourceKey)) || (d.hover !== null && !view.keys.has(d.hover))) next = { ...next, drag: null }\n else if (d.hover !== null) {\n const target = view.rows.get(d.hover)?.item\n const verdict = target ? dropVerdict(d.source, target, dropEnv()) : d.verdict\n if (!sameVerdict(verdict, d.verdict)) next = { ...next, drag: { ...d, verdict } }\n }\n }\n return next\n }\n\n const state = createStore<TreeState>(withView(core, viewOf(core)))\n\n function publish(c: TreeCore) {\n if (disposed) return\n const view = viewOf(c)\n core = reconcile(c, view)\n const next = withView(core, view)\n const current = state.get()\n if ((Object.keys(next) as (keyof TreeState)[]).every(key => Object.is(next[key], current[key]))) return\n state.set(next)\n }\n\n const patch = (changes: Partial<TreeCore>) => publish({ ...core, ...changes })\n\n // --- sources ---------------------------------------------------------------------------------------------------\n\n /** Reads the store and the shared view state; what opening a document shows is opened. */\n function sync() {\n if (disposed) return\n const documents = store.documents.get() ?? NO_DOCUMENTS\n const activeId = store.activeId.get()\n publish({ ...core, tree: store.tree.get(), documents, activeId, nodeFilters: shared.nodeFilters.get(), basketFilter: shared.basketFilter.get(), expanded: shared.expanded.get() })\n // Opening a document (also by URL, or after a move) shows it: its folders open and its folder becomes the selected one.\n const activeFolder = documents.find(d => d.id === activeId)?.folder\n if (activeFolder !== lastActiveFolder) {\n lastActiveFolder = activeFolder\n expand(ancestorKeys(activeFolder ?? ''))\n if (activeFolder !== undefined) setSelectedFolder(activeFolder)\n }\n if (activeId !== lastActiveId) {\n lastActiveId = activeId\n if (activeId) expand([`doc:${activeId}`])\n }\n }\n\n const unsubscribe = [\n store.tree.subscribe(sync),\n store.documents.subscribe(sync),\n store.activeId.subscribe(sync),\n shared.nodeFilters.subscribe(sync),\n shared.basketFilter.subscribe(sync),\n shared.expanded.subscribe(sync),\n ]\n\n function readOutline() {\n patch({ outline: editor ? editor.outline(structure) : EMPTY_OUTLINE })\n mirrorFocus()\n }\n\n /**\n * The editor's focus mode changed on its own (the focus chip, a block's „Fokussieren“, the focused Baustein vanished):\n * the tree follows without a command. A Baustein the outline does not know yet is retried after the next outline read.\n */\n function mirrorFocus() {\n const id = editor?.focusedId() ?? null\n if (id === seenFocus) return\n if (id === null) {\n seenFocus = null\n if (core.focusKey !== null) patch({ focusKey: null })\n return\n }\n const key = `outline:${id}`\n if (core.focusKey !== key) {\n const item = state.get().view.index.get(key)?.item\n if (item?.type !== 'composite') return\n patch({ focusKey: key })\n }\n seenFocus = id\n }\n\n // The outline is read when the document changed and has been quiet for a moment; a drag in progress is checked at once, and so is the editor's focus.\n function onEditorChange() {\n clearTimeout(outlineTimer)\n outlineTimer = setTimeout(readOutline, debounceMs)\n if (core.drag) publish(core)\n mirrorFocus()\n }\n\n function setContext(context: TreeContext) {\n if (context.editor === editor && context.structure === structure && context.treeRoot === treeRoot) return\n const editorChanged = context.editor !== editor\n const wasVirtual = core.virtual\n editor = context.editor\n structure = context.structure\n treeRoot = context.treeRoot\n clearTimeout(outlineTimer)\n if (editorChanged) {\n seenFocus = null\n unwatchEditor?.()\n unwatchEditor = editor?.onChange(onEditorChange)\n }\n patch({\n virtual: isVirtual(),\n structure,\n kinds: editor?.kinds ?? NO_KINDS,\n baskets: editor?.baskets ?? NO_BASKETS,\n ...(editorChanged ? { focusKey: null } : {}),\n hasEditor: !!editor,\n outline: editor ? editor.outline(structure) : EMPTY_OUTLINE,\n })\n if (core.virtual && !wasVirtual) expand([VIRTUAL_ROOT])\n mirrorFocus()\n }\n\n // --- open, filtered, typed -------------------------------------------------------------------------------------\n\n const filtering = () => filterQuery(core.filterText) !== ''\n\n function setOpen(key: string, open: boolean) {\n if (filtering()) patch({ filterOverrides: { ...core.filterOverrides, [key]: open } })\n else if (isOutlineKey(key)) patch({ collapsedOutline: open ? core.collapsedOutline.filter(k => k !== key) : [...core.collapsedOutline.filter(k => k !== key), key] })\n else if (open) shared.expand([key])\n else shared.expanded.set(shared.expanded.get().filter(k => k !== key))\n }\n\n function expand(keys: readonly string[]) {\n const outlineKeys = keys.filter(isOutlineKey)\n const { filterOverrides } = core\n const overridden = keys.some(key => key in filterOverrides)\n if (overridden || outlineKeys.some(key => core.collapsedOutline.includes(key))) {\n patch({\n collapsedOutline: core.collapsedOutline.filter(key => !outlineKeys.includes(key)),\n filterOverrides: overridden ? Object.fromEntries(Object.entries(filterOverrides).filter(([key]) => !keys.includes(key))) : filterOverrides,\n })\n }\n shared.expand(keys.filter(key => !isOutlineKey(key)))\n }\n\n function setFilterText(text: string) {\n patch({ filterText: text, filterOverrides: filterQuery(text) ? core.filterOverrides : NO_FILTERS })\n }\n\n // --- focus -----------------------------------------------------------------------------------------------------\n\n /** Only a Baustein the editor can address (it has a block ID) can be focused. */\n function focus(key: string) {\n const item = state.get().view.index.get(key)?.item\n if (item?.type !== 'composite' || !item.blockId) return\n patch({ focusKey: key })\n void execute({ type: 'focusBlock', id: item.blockId })\n }\n\n function defocus() {\n if (core.focusKey === null) return\n patch({ focusKey: null })\n void execute({ type: 'focusBlock', id: null })\n }\n\n // --- merge -----------------------------------------------------------------------------------------------------\n\n const canMerge = (item: TreeItem) => item.type === 'composite' && !!item.blockId && !!editor?.canMerge(item.blockId)\n\n function merge(item: TreeItem) {\n if (canMerge(item)) void execute({ type: 'mergeBlock', id: item.blockId! })\n }\n\n // --- selection -------------------------------------------------------------------------------------------------\n\n function setSelectedFolder(path: string) {\n patch({ selectedFolder: path })\n }\n\n function selectItem(item: TreeItem) {\n if (core.renaming?.key === item.key) return\n if (item.key === VIRTUAL_ROOT) return patch({ virtualTarget: null })\n if (item.type === 'document') {\n setSelectedFolder(item.folder ?? '')\n void execute({ type: 'openDocument', id: item.id! })\n }\n else if (item.type === 'composite' || item.type === 'heading') {\n void execute({ type: 'revealBlock', id: item.blockId ?? null, pos: item.pos! })\n const takesBausteine = !!item.kind && core.kinds.some(kind => kindAccepts(core.kinds, item.kind!, kind.id))\n if (core.virtual && item.blockId && takesBausteine) patch({ virtualTarget: item.blockId })\n }\n else {\n setSelectedFolder(item.path ?? '')\n }\n }\n\n function toggle(key: string) {\n setOpen(key, !state.get().view.rows.get(key)?.open)\n }\n\n /** A folder moved from `from` to `to`: its open/selected state follows it. */\n function followFolderMove(from: string, to: string) {\n shared.followFolderMove(from, to)\n patch({\n selectedFolder: remapFolderPath(core.selectedFolder, from, to),\n filterOverrides: Object.fromEntries(Object.entries(core.filterOverrides).map(([key, open]) => [key.startsWith('folder:') ? `folder:${remapFolderPath(key.slice(7), from, to)}` : key, open])),\n })\n expand(ancestorKeys(parentPath(to)))\n }\n\n async function moveFolder(from: string, to: string) {\n pendingMove = { from, to }\n const moved = await execute({ type: 'moveFolder', from, to })\n pendingMove = undefined\n if (moved) followFolderMove(from, to)\n }\n\n // --- drag and drop ---------------------------------------------------------------------------------------------\n\n function dragStart(item: TreeItem) {\n if (core.renaming?.key === item.key || !isDraggable(item, core.virtual)) return null\n const source = dragPayloadOf(item)\n patch({ drag: { source, sourceKey: item.key, hover: null, verdict: null } })\n return source\n }\n\n function dragOver(item: TreeItem) {\n const drag = core.drag\n if (!drag) return null\n const verdict = dropVerdict(drag.source, item, dropEnv())\n if (drag.hover !== item.key || !sameVerdict(drag.verdict, verdict)) patch({ drag: { ...drag, hover: item.key, verdict } })\n return verdict\n }\n\n function foreignDragOver(item: TreeItem) {\n const drag = core.drag\n if (drag && drag.source.type !== 'foreign-block') return null\n const source: DragPayload = { type: 'foreign-block' }\n const verdict = dropVerdict(source, item, dropEnv())\n if (!drag || drag.hover !== item.key || !sameVerdict(drag.verdict, verdict)) patch({ drag: { source, sourceKey: '', hover: item.key, verdict } })\n return verdict\n }\n\n async function dropForeign(item: TreeItem, payload: BlockTransfer, menu: boolean, x: number, y: number) {\n // This tree's own drag (a row also writes the block payload) is a move, not a transfer.\n if (core.drag && core.drag.source.type !== 'foreign-block') return\n patch({ drag: null })\n if (!dropVerdict({ type: 'foreign-block' }, item, dropEnv()).ok) return\n const targetId = item.key === VIRTUAL_ROOT ? null : item.blockId!\n // A throwing executor (the contract says it never does) must not leave the drop handler with an unhandled rejection.\n let done = false\n try {\n done = await execute({ type: 'transferBlock', payload, targetId, menu, x, y })\n }\n catch {\n return\n }\n if (done && targetId && !menu) expand([`outline:${targetId}`])\n }\n\n async function drop(item: TreeItem) {\n const drag = core.drag\n patch({ drag: null })\n if (!drag || !dropVerdict(drag.source, item, dropEnv()).ok) return\n const { source } = drag\n if (source.type === 'composite') {\n const targetId = item.key === VIRTUAL_ROOT ? null : item.blockId!\n if (await execute({ type: 'moveBlock', id: source.blockId, targetId }) && targetId) expand([`outline:${targetId}`])\n return\n }\n // A foreign Baustein is dropped through `dropForeign`, which reads its payload; it is no move of this tree.\n if (source.type === 'foreign-block') return\n const targetPath = item.path!\n if (source.type === 'document') {\n if (await execute({ type: 'moveDocument', id: source.id!, folder: targetPath })) expand(ancestorKeys(targetPath))\n return\n }\n await moveFolder(source.path, joinPath(targetPath, source.path.slice(source.path.lastIndexOf('/') + 1)))\n }\n\n // --- renaming, creating ----------------------------------------------------------------------------------------\n\n function startRename(item: TreeItem) {\n if (item.type !== 'folder') return\n patch({ renaming: { key: item.key, path: item.path!, base: item.label, draft: item.label, error: null } })\n }\n\n async function commitRename() {\n const r = core.renaming\n // An invalid name keeps the field open with its inline error.\n if (!r || r.error) return\n const name = r.draft.trim()\n patch({ renaming: null })\n if (name !== r.base) await moveFolder(r.path, joinPath(parentPath(r.path), name))\n }\n\n async function blurRename() {\n const r = core.renaming\n if (!r) return\n if (r.error) {\n void execute({ type: 'warn', title: t('tree.folder.renameFailed'), description: r.error })\n patch({ renaming: null })\n return\n }\n await commitRename()\n }\n\n function startCreateFolder(parent: string) {\n patch({ selectedFolder: parent, creatingFolder: { parent, draft: '', error: null } })\n }\n\n async function commitCreateFolder() {\n const f = core.creatingFolder\n if (!f) return\n if (!f.draft.trim()) return patch({ creatingFolder: null })\n if (f.error) return\n patch({ creatingFolder: null })\n if (await execute({ type: 'createFolder', path: joinPath(f.parent, f.draft.trim()) })) expand(ancestorKeys(f.parent))\n }\n\n // --- menus, deleting -------------------------------------------------------------------------------------------\n\n async function createDocumentIn(folder: string) {\n setSelectedFolder(folder)\n if (await execute({ type: 'createDocument', folder })) expand(ancestorKeys(folder))\n }\n\n /** Runs `command` for the Baustein of `item` (the file itself when it has none) and shows the result. */\n async function inComposite(item: TreeItem, command: TreeCommand) {\n if (await execute(command)) expand([item.blockId ? `outline:${item.blockId}` : item.key])\n }\n\n /**\n * What can be created below `item`: folders of the store take documents and sub-folders, the open file (as the root or\n * as a document row) and its Bausteine take what their kind accepts. Other rows take nothing. Folders also list\n * „Umbenennen“ here: renaming a document happens in the editor's title field.\n */\n function addMenu(item: TreeItem): TreeMenuEntry[][] {\n const groups: TreeMenuEntry[][] = []\n if (item.key !== VIRTUAL_ROOT && (item.type === 'root' || item.type === 'folder')) {\n const path = item.path!\n groups.push([\n { label: t('tree.menu.newDocument'), icon: 'i-lucide-file-plus', run: () => void createDocumentIn(path) },\n { label: item.type === 'root' ? t('tree.menu.newFolder') : t('tree.menu.newSubfolder'), icon: 'i-lucide-folder-plus', run: () => startCreateFolder(path) },\n ])\n }\n else {\n const can = creatableBelow(item, { structure: core.structure, hasEditor: core.hasEditor, activeId: core.activeId, kinds: core.kinds })\n const targetId = item.blockId ?? null\n const entries: TreeMenuEntry[] = []\n if (can.folder) entries.push({ label: t('tree.menu.newCompositeFolder'), icon: 'i-lucide-folder-plus', run: () => void inComposite(item, { type: 'createBausteinFolder', targetId }) })\n if (can.document) entries.push({ label: t('tree.menu.importFile'), icon: 'i-lucide-file-up', run: () => void inComposite(item, { type: 'importFiles', targetId }) })\n if (entries.length) groups.push(entries)\n }\n if (item.type === 'folder') groups.push([{ label: t('tree.menu.rename'), icon: 'i-lucide-pencil', run: () => startRename(item) }])\n return groups\n }\n\n async function remove(item: TreeItem) {\n patch({ removing: item.key })\n try {\n await execute(item.type === 'document' ? { type: 'removeDocument', id: item.id!, folder: item.folder ?? '' } : { type: 'removeFolder', path: item.path! })\n }\n finally {\n patch({ removing: null })\n }\n }\n\n sync()\n mirrorFocus()\n\n return {\n state,\n setContext,\n setSelectedFolder,\n setOpen,\n toggle,\n expand,\n setFilterText,\n setKindHidden: (key, kind, hidden) => shared.setKindHidden(key, kind, hidden),\n clearFilter: key => shared.clearFilter(key),\n clearAllFilters: () => shared.clearAllFilters(),\n setBasketShown: (id, shown) => shared.setBasketShown(id, shown),\n clearBasketFilter: () => shared.clearBasketFilter(),\n focus,\n defocus,\n canMerge,\n merge,\n select: selectItem,\n activate(item) {\n selectItem(item)\n if (item.type === 'root' || item.type === 'folder') toggle(item.key)\n },\n addMenu,\n remove,\n isDraggable: item => core.renaming?.key !== item.key && isDraggable(item, core.virtual),\n dragStart,\n dragOver,\n dragLeave(item) {\n // A foreign drag only exists while it is over this tree (no `dragend` reaches here), so leaving a row ends it; the next row's `foreignDragOver` starts it again.\n if (core.drag?.source.type === 'foreign-block') patch({ drag: null })\n else if (core.drag?.hover === item.key) patch({ drag: { ...core.drag, hover: null, verdict: null } })\n },\n dragEnd: () => patch({ drag: null }),\n drop,\n foreignDragOver,\n dropForeign,\n startRename,\n setRenameDraft(text) {\n const r = core.renaming\n if (r) patch({ renaming: { ...r, draft: text, error: renameError(text, r.path, r.base, core.tree) } })\n },\n cancelRename: () => patch({ renaming: null }),\n commitRename,\n blurRename,\n startCreateFolder,\n setFolderDraft(text) {\n const f = core.creatingFolder\n if (f) patch({ creatingFolder: { ...f, draft: text, error: folderError(text, f.parent, core.tree) } })\n },\n cancelCreateFolder: () => patch({ creatingFolder: null }),\n commitCreateFolder,\n dispose() {\n disposed = true\n clearTimeout(outlineTimer)\n unwatchEditor?.()\n for (const stop of unsubscribe) stop()\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -71,21 +71,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
71
71
|
editor: import("vue").ComputedRef<Editor | undefined>;
|
|
72
72
|
preset: ComposedEditor<Component>;
|
|
73
73
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
74
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
75
74
|
"update:sidebarOpen": (value: boolean) => any;
|
|
76
75
|
"update:commentsOpen": (value: boolean) => any;
|
|
77
|
-
"update:modelValue": (content: JSONContent) => any;
|
|
78
76
|
ready: (editor: Editor) => any;
|
|
77
|
+
"update:modelValue": (content: JSONContent) => any;
|
|
79
78
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
80
79
|
contentError: (error: Error) => any;
|
|
80
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
81
81
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
82
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
83
82
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
84
83
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
85
|
-
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
86
84
|
onReady?: ((editor: Editor) => any) | undefined;
|
|
85
|
+
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
87
86
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
88
87
|
onContentError?: ((error: Error) => any) | undefined;
|
|
88
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
89
89
|
}>, {
|
|
90
90
|
content: ContentLevel | readonly FeatureId[];
|
|
91
91
|
ai: boolean;
|
|
@@ -41,21 +41,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
41
41
|
editor: import("vue").ComputedRef<Editor | undefined>;
|
|
42
42
|
preset: EditorPreset<Component>;
|
|
43
43
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
44
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
45
44
|
"update:sidebarOpen": (value: boolean) => any;
|
|
46
45
|
"update:commentsOpen": (value: boolean) => any;
|
|
47
|
-
"update:modelValue": (content: JSONContent) => any;
|
|
48
46
|
ready: (editor: Editor) => any;
|
|
47
|
+
"update:modelValue": (content: JSONContent) => any;
|
|
49
48
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
50
49
|
contentError: (error: Error) => any;
|
|
50
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
51
51
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
52
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
53
52
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
54
53
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
55
|
-
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
56
54
|
onReady?: ((editor: Editor) => any) | undefined;
|
|
55
|
+
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
57
56
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
58
57
|
onContentError?: ((error: Error) => any) | undefined;
|
|
58
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
59
59
|
}>, {
|
|
60
60
|
editable: boolean;
|
|
61
61
|
settingsUi: "internal" | "external";
|
|
@@ -43,21 +43,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
43
43
|
settingsOpen: import("vue").Ref<boolean, boolean>;
|
|
44
44
|
settingsTab: import("vue").Ref<string | undefined, string | undefined>;
|
|
45
45
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
46
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
47
46
|
"update:sidebarOpen": (value: boolean) => any;
|
|
48
47
|
"update:commentsOpen": (value: boolean) => any;
|
|
49
|
-
"update:modelValue": (content: JSONContent) => any;
|
|
50
48
|
ready: (editor: Editor) => any;
|
|
49
|
+
"update:modelValue": (content: JSONContent) => any;
|
|
51
50
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
52
51
|
contentError: (error: Error) => any;
|
|
52
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
53
53
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
54
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
55
54
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
56
55
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
57
|
-
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
58
56
|
onReady?: ((editor: Editor) => any) | undefined;
|
|
57
|
+
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
59
58
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
60
59
|
onContentError?: ((error: Error) => any) | undefined;
|
|
60
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
61
61
|
}>, {
|
|
62
62
|
editable: boolean;
|
|
63
63
|
settingsUi: "internal" | "external";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecsplico/editor",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vue and React editor library on TipTap v3 (Vue: Nuxt UI; React: shadcn/ui): framework-free features with Vue and React bindings, simple and advanced presets, tree-shakeable entries. Built ESM with type declarations.",
|
|
6
6
|
"license": "MIT",
|