@skaile/forge-editor 6.4.0 → 6.4.1

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.4.1 - 2026-11-02
4
+
5
+ ### Fixed
6
+
7
+ - `document_outline` titles a heading or Typed block exactly as the Block index does: a hard break in the title is a line break, no longer a space, and the title is no longer trimmed.
8
+
9
+ ### Changed
10
+
11
+ - Tests of the chat agent seam and the document tools check behaviour instead of the wording of tool results; the glossary (`CONTEXT.md`) names the Chat agent and how `document_outline` relates to the Block outline.
12
+
3
13
  ## 6.4.0 - 2026-11-02
4
14
 
5
15
  ### Added
@@ -11,17 +11,17 @@ type __VLS_Props = {
11
11
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
12
12
  view: import("vue").ShallowRef<EditorView | undefined, EditorView | undefined>;
13
13
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
- ready: (view: EditorView) => any;
15
14
  "update:modelValue": (text: string) => any;
16
15
  save: (text: string) => any;
16
+ ready: (view: EditorView) => any;
17
17
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
18
- onReady?: ((view: EditorView) => any) | undefined;
19
18
  "onUpdate:modelValue"?: ((text: string) => any) | undefined;
20
19
  onSave?: ((text: string) => any) | undefined;
20
+ onReady?: ((view: EditorView) => any) | undefined;
21
21
  }>, {
22
- modelValue: string;
23
22
  editable: boolean;
24
23
  wordWrap: boolean;
24
+ modelValue: string;
25
25
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
26
  declare const _default: typeof __VLS_export;
27
27
  //#endregion
@@ -50,7 +50,7 @@ const INLINE = /* @__PURE__ */ new Set([
50
50
  CITE_NODE
51
51
  ]);
52
52
  const attrString = (node, key) => typeof node.attrs?.[key] === "string" ? node.attrs[key] : "";
53
- /** The plain text of an inline node or a textblock's inline content. */
53
+ /** The plain text of an inline node or a textblock's inline content. Internal: `document_outline` counts and titles Blocks with it. */
54
54
  function inlineText(node) {
55
55
  switch (node.type) {
56
56
  case TEXT_NODE: return node.text ?? "";
@@ -68,6 +68,7 @@ function plainText(node) {
68
68
  if (holdsInline(node)) return inlineText(node);
69
69
  return (node.content ?? []).filter((child) => !(child.type !== void 0 && NOT_TEXT.has(child.type))).map(plainText).filter(Boolean).join("\n");
70
70
  }
71
+ /** The title of a Block: a Typed block's title row, a heading's text, `''` for any other Block. Internal, like `inlineText`. */
71
72
  const titleOf = (node) => {
72
73
  if (isTypedBlock(node)) return (node.content ?? []).filter((child) => child.type === TYPED_BLOCK_TITLE).map(inlineText).join("");
73
74
  return node.type === "heading" ? inlineText(node) : "";
@@ -274,6 +275,6 @@ function findBlock(node, blockId) {
274
275
  return null;
275
276
  }
276
277
  //#endregion
277
- export { META_EDGES_KEY, REFERENCE_NODE, addressKey, authoredEdgesOf, blockContentOf, blockEdgesOf, edgesInto, isTagEdgeTarget, projectDocument, sameAddress };
278
+ export { META_EDGES_KEY, REFERENCE_NODE, addressKey, authoredEdgesOf, blockContentOf, blockEdgesOf, edgesInto, inlineText, isTagEdgeTarget, projectDocument, sameAddress, titleOf };
278
279
 
279
280
  //# sourceMappingURL=block-index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"block-index.js","names":[],"sources":["../../src/core/block-index.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport { sha256Hex } from './hash'\nimport { DERIVATION_KEY, derivationOf, isTypedBlock, metaOf, TYPED_BLOCK_DESCRIPTION, TYPED_BLOCK_TAGS, TYPED_BLOCK_TITLE, typeIdOf } from './typed-block'\n\n// Framework-free and server-importable: a Document's blocks as rows of the workspace's Block index. `projectDocument` is pure\n// (JSON in, rows out); where the rows live is the `BlockIndex` port (`server/ports`) and its adapters.\n\n// Same names as `features/mentions` and `features/composites`; copied because core must not import features.\nconst TAG_NODE = 'tag'\nconst MENTION_NODE = 'mention'\n/** Node name of a reference chip; `features/composites` spells it the same. */\nexport const REFERENCE_NODE = 'reference'\nconst CITE_NODE = 'cite'\nconst HARD_BREAK_NODE = 'hardBreak'\nconst TEXT_NODE = 'text'\n\n/** Where a Block is: the Document and the block ID inside it. Block IDs are unique per Document only. */\nexport interface BlockAddress {\n documentId: string\n blockId: string\n}\n\n/** A string that names `address` and nothing else, for a `Map` or `Set` key; an address without a Document ID (`''`, or missing) is the same key. */\nexport const addressKey = (address: BlockAddress): string => `${address.documentId ?? ''}\\u0000${address.blockId}`\n\n/** Whether two addresses name the same Block; an address without a Document ID (`''`, or missing) equals another one without. */\nexport const sameAddress = (a: BlockAddress, b: BlockAddress): boolean => (a.documentId ?? '') === (b.documentId ?? '') && a.blockId === b.blockId\n\n/** A reference chip in a Block: the Block it points at (`documentId` resolved: a chip without one points into its own Document) and its locator. */\nexport interface BlockReferenceRow extends BlockAddress {\n /** Position inside the target's source (`S. 12`, `03:14`); `null` = the whole Block. */\n locator: string | null\n}\n\n/** What a Derived block was made from, as an address: another Block (`documentId` resolved like a chip's) or an uploaded asset by name. */\nexport type BlockDerivationRow = BlockAddress | { asset: string }\n\n/** The Block property that holds a Block's authored edges (`AuthoredEdge[]`); `projectDocument` turns them into `BlockEdgeRow`s of origin `meta`. */\nexport const META_EDGES_KEY = 'edges'\n\n// Same node name as `features/composites/embed`; copied because core must not import features.\nconst EMBED_NODE = 'blockEmbed'\n\n/** What an Edge says about its two ends. The four built-in kinds are projected from the content; any other string is an authored kind (`supports`, `contradicts`, …). */\nexport type EdgeKind = 'reference' | 'derivation' | 'embed' | 'tag' | (string & {})\n\n/** Where an Edge was written: in the Block's content (a chip, an embed, a tag, the Derivation) or by hand in its `meta.edges`. */\nexport type EdgeOrigin = 'content' | 'meta'\n\n/** One outgoing Edge of a Block as its row holds it: the Block it points at (`documentId` resolved), or a tag name for a `tag` Edge. */\nexport interface BlockEdgeRow {\n to: BlockAddress | { tag: string }\n kind: EdgeKind\n origin: EdgeOrigin\n}\n\n/** An Edge with both ends: what `BlockIndex.edges` answers. `from` is the Block the Edge is written in. */\nexport interface BlockEdge extends BlockEdgeRow {\n from: BlockAddress\n}\n\n/** An Edge a person added to a Typed block (`meta.edges`): the target Block (`documentId` absent = this Document) and its kind. */\nexport interface AuthoredEdge {\n to: { documentId?: string, blockId: string }\n kind: string\n}\n\n/** `true` for the Edge that points at a tag name instead of a Block. */\nexport const isTagEdgeTarget = (to: BlockEdgeRow['to']): to is { tag: string } => 'tag' in to\n\n/** The authored edges in a `meta.edges` value; entries without a block ID or a kind are dropped, anything but an array is `[]`. */\nexport function authoredEdgesOf(value: unknown): AuthoredEdge[] {\n if (!Array.isArray(value)) return []\n const edges: AuthoredEdge[] = []\n for (const entry of value as unknown[]) {\n const { to, kind } = (entry ?? {}) as { to?: { documentId?: unknown, blockId?: unknown }, kind?: unknown }\n if (typeof to?.blockId !== 'string' || !to.blockId || typeof kind !== 'string' || !kind) continue\n edges.push({ to: { ...(typeof to.documentId === 'string' && to.documentId && { documentId: to.documentId }), blockId: to.blockId }, kind })\n }\n return edges\n}\n\n/**\n * One Block of a Document as the index holds it. Every node that carries a block ID is a Block (the `id` attribute of the\n * stored JSON): Typed blocks, paragraphs, headings, lists, media. A Block without an ID has no address and no row.\n * The content fields (`text`, `tags`, `references`) cover the Block's whole content, nested Blocks included: a Typed block's row\n * answers for its body, and the nested Blocks have rows of their own.\n */\nexport interface BlockRow extends BlockAddress {\n /** Block type id (`note`, `paper`, …) of a Typed block, the node type (`paragraph`, `heading`, …) of any other Block. */\n type: string\n /** The title of a Typed block, the text of a heading; `''` for any other Block. */\n title: string\n /**\n * The Block properties of a Typed block: its `meta` without the derivation (which is `derivedFrom`); `{}` for any other Block.\n * A Block that holds an uploaded file (a media block, or a Typed block whose first body block is one) also has `sha256`,\n * the file's SHA-256 hex, which the import check compares dropped files with.\n */\n properties: Record<string, unknown>\n /** Tag names in the Block: its tags row (Typed block) and every `#` chip in its content, in document order, without repeats. */\n tags: string[]\n /** The reference chips in the Block's content, in document order, without repeats. */\n references: BlockReferenceRow[]\n /**\n * The Edges that leave the Block, without repeats: a `reference` per chip target, a `derivation` for a Derivation from another\n * Block, an `embed` per embedded Block, a `tag` per tag name, then the authored `meta.edges` (origin `meta`). Like the other\n * content fields it covers the Block's whole content, nested Blocks included.\n */\n edges: BlockEdgeRow[]\n /** What the Block was derived from; `null` for a block that carries no Derivation. */\n derivedFrom: BlockDerivationRow | null\n /** SHA-256 hex of the Block's content, the `pruefungHash` formula (see `ProjectOptions.markdown`). */\n hash: string\n /**\n * SHA-256 hex of what a Derivation made from this Block records as its `inputHash`: the Markdown of a Typed block's body\n * (without the title, unlike `hash`), the Markdown of any other Block (then equal to `hash`). A Derived block in another\n * Document compares it to tell whether its original changed.\n */\n sourceHash: string\n /** The plain text of the Block's content (a Typed block: description and body, the title is `title`); for search. */\n text: string\n}\n\n/** What of a Block `blockGet` can answer with besides the summary: the Block itself (`block`) or the Blocks nested in it (`children`). */\nexport type BlockContentMode = 'block' | 'children'\n\n/** The part of a row the block route answers with, and what the client's `index.blockGet` resolves to. */\nexport interface BlockSummary {\n title: string\n type: string\n hash: string\n /** The row's `sourceHash`: what a Derivation made from the Block records as `inputHash`. */\n sourceHash: string\n /** `true` when no such Block exists (a client turns the `404` into it); the server answers `false`. */\n gone: boolean\n /** The Block's nodes as TipTap JSON (`blockContentOf`), only when the request asked for a `BlockContentMode`; absent otherwise. */\n content?: JSONContent[]\n}\n\n/**\n * One hit of `BlockIndex.search`: the Block that matched, with what a result list shows of it. A Block without a title (a\n * paragraph, a list) has `title: ''`; `type` then says what it is.\n */\nexport interface BlockHit extends BlockAddress {\n title: string\n type: string\n /** How well the Block matched, higher is better. Comparable within one result only; the search route leaves it out. */\n score: number\n}\n\n/** A hit as the search route answers it and a client gets it: where the Block is, its title and type, in the order of the ranking. */\nexport type BlockSearchHit = Omit<BlockHit, 'score'>\n\n/** Markdown of `nodes` serialised as the content of one document. */\nexport type BlockMarkdown = (nodes: readonly JSONContent[]) => string\n\nexport interface ProjectOptions {\n /**\n * The Markdown serializer of the hash: `pruefungHash` is the SHA-256 of the title line, a blank line and the Markdown of the\n * body (of a Typed block), and the hash of any other Block is that of its Markdown. Core has no serializer (it needs the\n * schema's Markdown handlers), so a host passes one; without it the plain text takes its place (`title`, a blank line, the\n * body text) and the hash is only comparable with other hashes of the same projection.\n */\n markdown?: BlockMarkdown\n}\n\nconst CHROME: ReadonlySet<string> = new Set([TYPED_BLOCK_TITLE, TYPED_BLOCK_DESCRIPTION, TYPED_BLOCK_TAGS])\n/** The chrome of a Typed block that is not in its text: the title is its own field, the tags row is data. */\nconst NOT_TEXT: ReadonlySet<string> = new Set([TYPED_BLOCK_TITLE, TYPED_BLOCK_TAGS])\nconst INLINE: ReadonlySet<string> = new Set([TEXT_NODE, HARD_BREAK_NODE, MENTION_NODE, TAG_NODE, REFERENCE_NODE, CITE_NODE])\n\nconst attrString = (node: JSONContent, key: string): string => typeof node.attrs?.[key] === 'string' ? node.attrs[key] as string : ''\n\n/** The plain text of an inline node or a textblock's inline content. */\nfunction inlineText(node: JSONContent): string {\n switch (node.type) {\n case TEXT_NODE: return node.text ?? ''\n case HARD_BREAK_NODE: return '\\n'\n case MENTION_NODE: return `@${attrString(node, 'label') || attrString(node, 'id')}`\n case TAG_NODE: return `#${attrString(node, 'name')}`\n case REFERENCE_NODE: return attrString(node, 'label')\n case CITE_NODE: return `[cite:${attrString(node, 'key')}]`\n default: return (node.content ?? []).map(inlineText).join('')\n }\n}\n\nconst holdsInline = (node: JSONContent): boolean => node.content?.some(child => child.type !== undefined && INLINE.has(child.type)) === true\n\n/** The plain text of `node`'s content: one line per textblock, chrome of a Typed block left out. */\nfunction plainText(node: JSONContent): string {\n if (holdsInline(node)) return inlineText(node)\n return (node.content ?? []).filter(child => !(child.type !== undefined && NOT_TEXT.has(child.type))).map(plainText).filter(Boolean).join('\\n')\n}\n\nconst titleOf = (node: JSONContent): string => {\n if (isTypedBlock(node)) return (node.content ?? []).filter(child => child.type === TYPED_BLOCK_TITLE).map(inlineText).join('')\n return node.type === 'heading' ? inlineText(node) : ''\n}\n\n/** Collects the tag names, reference chips and embedded Blocks (`embeds`, by `documentId/blockId`) of `node`'s whole subtree. */\nfunction collect(node: JSONContent, documentId: string, tags: Set<string>, references: Map<string, BlockReferenceRow>, embeds: Map<string, BlockAddress>): void {\n if (node.type === TAG_NODE) {\n const name = attrString(node, 'name')\n if (name) tags.add(name)\n }\n else if (node.type === TYPED_BLOCK_TAGS && Array.isArray(node.attrs?.tags)) {\n for (const name of node.attrs.tags) if (typeof name === 'string' && name) tags.add(name)\n }\n else if (node.type === REFERENCE_NODE) {\n const blockId = attrString(node, 'target')\n if (blockId) {\n const target = attrString(node, 'documentId') || documentId\n const locator = attrString(node, 'locator') || null\n references.set(`${addressKey({ documentId: target, blockId })}\\u0000${locator ?? ''}`, { documentId: target, blockId, locator })\n }\n }\n else if (node.type === EMBED_NODE) {\n const blockId = attrString(node, 'blockId')\n if (blockId) {\n const target = attrString(node, 'documentId') || documentId\n embeds.set(addressKey({ documentId: target, blockId }), { documentId: target, blockId })\n }\n }\n for (const child of node.content ?? []) collect(child, documentId, tags, references, embeds)\n}\n\n/** The Edges of a Block (`BlockRow.edges`): content Edges in the order reference, derivation, embed, tag, then the authored ones; one per kind and target. */\nfunction edgesOf(documentId: string, references: Iterable<BlockReferenceRow>, derivedFrom: BlockDerivationRow | null, embeds: Iterable<BlockAddress>, tags: Iterable<string>, authored: readonly AuthoredEdge[]): BlockEdgeRow[] {\n const edges = new Map<string, BlockEdgeRow>()\n const add = (edge: BlockEdgeRow): void => {\n const target = isTagEdgeTarget(edge.to) ? `tag\\n${edge.to.tag}` : addressKey(edge.to)\n const key = `${edge.origin}\\n${edge.kind}\\n${target}`\n if (!edges.has(key)) edges.set(key, edge)\n }\n for (const { documentId: target, blockId } of references) add({ to: { documentId: target, blockId }, kind: 'reference', origin: 'content' })\n if (derivedFrom && 'blockId' in derivedFrom) add({ to: { documentId: derivedFrom.documentId, blockId: derivedFrom.blockId }, kind: 'derivation', origin: 'content' })\n for (const { documentId: target, blockId } of embeds) add({ to: { documentId: target, blockId }, kind: 'embed', origin: 'content' })\n for (const tag of tags) add({ to: { tag }, kind: 'tag', origin: 'content' })\n for (const { to, kind } of authored) add({ to: { documentId: to.documentId ?? documentId, blockId: to.blockId }, kind, origin: 'meta' })\n return [...edges.values()]\n}\n\n/** The `hash` and the `sourceHash` of `node`: for a Typed block the title line plus the body's Markdown, and the body's Markdown alone; for any other Block both the Block's Markdown. */\nfunction hashesOf(node: JSONContent, title: string, text: string, markdown: BlockMarkdown | undefined): { hash: string, sourceHash: string } {\n if (!isTypedBlock(node)) {\n const hash = sha256Hex(markdown ? markdown([node]) : text)\n return { hash, sourceHash: hash }\n }\n const body = (node.content ?? []).filter(child => !(child.type !== undefined && CHROME.has(child.type)))\n const bodyText = markdown ? markdown(body) : body.map(plainText).filter(Boolean).join('\\n')\n return { hash: sha256Hex(`${title}\\n\\n${bodyText}`), sourceHash: sha256Hex(bodyText) }\n}\n\nconst FILE_HASH = /^[0-9a-f]{64}$/i\n\n/** The SHA-256 of the uploaded file a Block holds: the `hash` of a media node (image, audio, video, file) itself, or of the first body block of a Typed block (its source file); `''` for a Block without one. */\nfunction fileHashOf(node: JSONContent): string {\n const source = isTypedBlock(node) ? node.content?.find(child => !(child.type !== undefined && CHROME.has(child.type))) : node\n const hash = source?.attrs?.hash\n return typeof hash === 'string' && FILE_HASH.test(hash) ? hash.toLowerCase() : ''\n}\n\nfunction rowOf(node: JSONContent, blockId: string, documentId: string, markdown: BlockMarkdown | undefined): BlockRow {\n const typed = isTypedBlock(node)\n const title = titleOf(node)\n const text = plainText(node)\n const tags = new Set<string>()\n const references = new Map<string, BlockReferenceRow>()\n const embeds = new Map<string, BlockAddress>()\n collect(node, documentId, tags, references, embeds)\n const derivation = derivationOf(node)\n const from = derivation?.from\n const { [DERIVATION_KEY]: _derivation, ...meta } = typed ? metaOf(node) : {}\n const sha256 = fileHashOf(node)\n // The file's SHA-256 is a property of the row, so a drop of the same file is found (`core/import-check`); an authored `sha256` property wins.\n const properties = sha256 && !('sha256' in meta) ? { ...meta, sha256 } : meta\n const derivedFrom: BlockDerivationRow | null = !from ? null : 'block' in from ? { documentId: from.document || documentId, blockId: from.block } : { asset: from.asset }\n return {\n documentId,\n blockId,\n type: typed ? typeIdOf(node) : node.type ?? '',\n title,\n properties,\n tags: [...tags],\n references: [...references.values()],\n edges: edgesOf(documentId, references.values(), derivedFrom, embeds.values(), tags, authoredEdgesOf(properties[META_EDGES_KEY])),\n derivedFrom,\n ...hashesOf(node, title, text, markdown),\n text,\n }\n}\n\n/**\n * The rows of a Document's content: one per node with a block ID, in document order (a Block before the Blocks nested in it).\n * `content` is the Document's TipTap JSON in the current shape; nodes without an ID, and the inline nodes inside a textblock\n * (a mention's `id` is a user's, not a block's), have no row. Pure: the same content and options give the same rows.\n */\nexport function projectDocument(content: JSONContent, documentId: string, options: ProjectOptions = {}): BlockRow[] {\n const rows: BlockRow[] = []\n const walk = (node: JSONContent): void => {\n if (holdsInline(node)) return\n for (const child of node.content ?? []) {\n const id = child.attrs?.id\n if (typeof id === 'string' && id) rows.push(rowOf(child, id, documentId, options.markdown))\n walk(child)\n }\n }\n walk(content)\n return rows\n}\n\n/** The Edges that leave `row`, each with the Block it is written in. */\nexport const blockEdgesOf = (row: Pick<BlockRow, 'documentId' | 'blockId' | 'edges'>): BlockEdge[] =>\n row.edges.map(edge => ({ from: { documentId: row.documentId, blockId: row.blockId }, ...structuredClone(edge) }))\n\n/** The Edges of `rows` that point at the Block `address` (`tag` Edges never do), in the order of the rows. */\nexport function edgesInto(rows: Iterable<Pick<BlockRow, 'documentId' | 'blockId' | 'edges'>>, address: BlockAddress): BlockEdge[] {\n const found: BlockEdge[] = []\n for (const row of rows) {\n for (const edge of row.edges) {\n if (isTagEdgeTarget(edge.to) || !sameAddress(edge.to, address)) continue\n found.push({ from: { documentId: row.documentId, blockId: row.blockId }, ...structuredClone(edge) })\n }\n }\n return found\n}\n\n/**\n * The nodes of Block `blockId` in a Document's content, as `blockContentOf` of the block route: `block` = the Block itself,\n * `children` = what it holds (a Typed block: its body without the title, description and tags rows; a list or a quote: its\n * items; a Block that holds only text: nothing). `null` when the content has no Block with that ID. Pure and a copy-free view:\n * the nodes are those of `content`.\n */\nexport function blockContentOf(content: JSONContent, blockId: string, mode: BlockContentMode): JSONContent[] | null {\n const found = findBlock(content, blockId)\n if (!found) return null\n if (mode === 'block') return [found]\n if (holdsInline(found)) return []\n return (found.content ?? []).filter(child => !(isTypedBlock(found) && child.type !== undefined && CHROME.has(child.type)))\n}\n\nfunction findBlock(node: JSONContent, blockId: string): JSONContent | null {\n if (holdsInline(node)) return null\n for (const child of node.content ?? []) {\n if (child.attrs?.id === blockId) return child\n const nested = findBlock(child, blockId)\n if (nested) return nested\n }\n return null\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"block-index.js","names":[],"sources":["../../src/core/block-index.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport { sha256Hex } from './hash'\nimport { DERIVATION_KEY, derivationOf, isTypedBlock, metaOf, TYPED_BLOCK_DESCRIPTION, TYPED_BLOCK_TAGS, TYPED_BLOCK_TITLE, typeIdOf } from './typed-block'\n\n// Framework-free and server-importable: a Document's blocks as rows of the workspace's Block index. `projectDocument` is pure\n// (JSON in, rows out); where the rows live is the `BlockIndex` port (`server/ports`) and its adapters.\n\n// Same names as `features/mentions` and `features/composites`; copied because core must not import features.\nconst TAG_NODE = 'tag'\nconst MENTION_NODE = 'mention'\n/** Node name of a reference chip; `features/composites` spells it the same. */\nexport const REFERENCE_NODE = 'reference'\nconst CITE_NODE = 'cite'\nconst HARD_BREAK_NODE = 'hardBreak'\nconst TEXT_NODE = 'text'\n\n/** Where a Block is: the Document and the block ID inside it. Block IDs are unique per Document only. */\nexport interface BlockAddress {\n documentId: string\n blockId: string\n}\n\n/** A string that names `address` and nothing else, for a `Map` or `Set` key; an address without a Document ID (`''`, or missing) is the same key. */\nexport const addressKey = (address: BlockAddress): string => `${address.documentId ?? ''}\\u0000${address.blockId}`\n\n/** Whether two addresses name the same Block; an address without a Document ID (`''`, or missing) equals another one without. */\nexport const sameAddress = (a: BlockAddress, b: BlockAddress): boolean => (a.documentId ?? '') === (b.documentId ?? '') && a.blockId === b.blockId\n\n/** A reference chip in a Block: the Block it points at (`documentId` resolved: a chip without one points into its own Document) and its locator. */\nexport interface BlockReferenceRow extends BlockAddress {\n /** Position inside the target's source (`S. 12`, `03:14`); `null` = the whole Block. */\n locator: string | null\n}\n\n/** What a Derived block was made from, as an address: another Block (`documentId` resolved like a chip's) or an uploaded asset by name. */\nexport type BlockDerivationRow = BlockAddress | { asset: string }\n\n/** The Block property that holds a Block's authored edges (`AuthoredEdge[]`); `projectDocument` turns them into `BlockEdgeRow`s of origin `meta`. */\nexport const META_EDGES_KEY = 'edges'\n\n// Same node name as `features/composites/embed`; copied because core must not import features.\nconst EMBED_NODE = 'blockEmbed'\n\n/** What an Edge says about its two ends. The four built-in kinds are projected from the content; any other string is an authored kind (`supports`, `contradicts`, …). */\nexport type EdgeKind = 'reference' | 'derivation' | 'embed' | 'tag' | (string & {})\n\n/** Where an Edge was written: in the Block's content (a chip, an embed, a tag, the Derivation) or by hand in its `meta.edges`. */\nexport type EdgeOrigin = 'content' | 'meta'\n\n/** One outgoing Edge of a Block as its row holds it: the Block it points at (`documentId` resolved), or a tag name for a `tag` Edge. */\nexport interface BlockEdgeRow {\n to: BlockAddress | { tag: string }\n kind: EdgeKind\n origin: EdgeOrigin\n}\n\n/** An Edge with both ends: what `BlockIndex.edges` answers. `from` is the Block the Edge is written in. */\nexport interface BlockEdge extends BlockEdgeRow {\n from: BlockAddress\n}\n\n/** An Edge a person added to a Typed block (`meta.edges`): the target Block (`documentId` absent = this Document) and its kind. */\nexport interface AuthoredEdge {\n to: { documentId?: string, blockId: string }\n kind: string\n}\n\n/** `true` for the Edge that points at a tag name instead of a Block. */\nexport const isTagEdgeTarget = (to: BlockEdgeRow['to']): to is { tag: string } => 'tag' in to\n\n/** The authored edges in a `meta.edges` value; entries without a block ID or a kind are dropped, anything but an array is `[]`. */\nexport function authoredEdgesOf(value: unknown): AuthoredEdge[] {\n if (!Array.isArray(value)) return []\n const edges: AuthoredEdge[] = []\n for (const entry of value as unknown[]) {\n const { to, kind } = (entry ?? {}) as { to?: { documentId?: unknown, blockId?: unknown }, kind?: unknown }\n if (typeof to?.blockId !== 'string' || !to.blockId || typeof kind !== 'string' || !kind) continue\n edges.push({ to: { ...(typeof to.documentId === 'string' && to.documentId && { documentId: to.documentId }), blockId: to.blockId }, kind })\n }\n return edges\n}\n\n/**\n * One Block of a Document as the index holds it. Every node that carries a block ID is a Block (the `id` attribute of the\n * stored JSON): Typed blocks, paragraphs, headings, lists, media. A Block without an ID has no address and no row.\n * The content fields (`text`, `tags`, `references`) cover the Block's whole content, nested Blocks included: a Typed block's row\n * answers for its body, and the nested Blocks have rows of their own.\n */\nexport interface BlockRow extends BlockAddress {\n /** Block type id (`note`, `paper`, …) of a Typed block, the node type (`paragraph`, `heading`, …) of any other Block. */\n type: string\n /** The title of a Typed block, the text of a heading; `''` for any other Block. */\n title: string\n /**\n * The Block properties of a Typed block: its `meta` without the derivation (which is `derivedFrom`); `{}` for any other Block.\n * A Block that holds an uploaded file (a media block, or a Typed block whose first body block is one) also has `sha256`,\n * the file's SHA-256 hex, which the import check compares dropped files with.\n */\n properties: Record<string, unknown>\n /** Tag names in the Block: its tags row (Typed block) and every `#` chip in its content, in document order, without repeats. */\n tags: string[]\n /** The reference chips in the Block's content, in document order, without repeats. */\n references: BlockReferenceRow[]\n /**\n * The Edges that leave the Block, without repeats: a `reference` per chip target, a `derivation` for a Derivation from another\n * Block, an `embed` per embedded Block, a `tag` per tag name, then the authored `meta.edges` (origin `meta`). Like the other\n * content fields it covers the Block's whole content, nested Blocks included.\n */\n edges: BlockEdgeRow[]\n /** What the Block was derived from; `null` for a block that carries no Derivation. */\n derivedFrom: BlockDerivationRow | null\n /** SHA-256 hex of the Block's content, the `pruefungHash` formula (see `ProjectOptions.markdown`). */\n hash: string\n /**\n * SHA-256 hex of what a Derivation made from this Block records as its `inputHash`: the Markdown of a Typed block's body\n * (without the title, unlike `hash`), the Markdown of any other Block (then equal to `hash`). A Derived block in another\n * Document compares it to tell whether its original changed.\n */\n sourceHash: string\n /** The plain text of the Block's content (a Typed block: description and body, the title is `title`); for search. */\n text: string\n}\n\n/** What of a Block `blockGet` can answer with besides the summary: the Block itself (`block`) or the Blocks nested in it (`children`). */\nexport type BlockContentMode = 'block' | 'children'\n\n/** The part of a row the block route answers with, and what the client's `index.blockGet` resolves to. */\nexport interface BlockSummary {\n title: string\n type: string\n hash: string\n /** The row's `sourceHash`: what a Derivation made from the Block records as `inputHash`. */\n sourceHash: string\n /** `true` when no such Block exists (a client turns the `404` into it); the server answers `false`. */\n gone: boolean\n /** The Block's nodes as TipTap JSON (`blockContentOf`), only when the request asked for a `BlockContentMode`; absent otherwise. */\n content?: JSONContent[]\n}\n\n/**\n * One hit of `BlockIndex.search`: the Block that matched, with what a result list shows of it. A Block without a title (a\n * paragraph, a list) has `title: ''`; `type` then says what it is.\n */\nexport interface BlockHit extends BlockAddress {\n title: string\n type: string\n /** How well the Block matched, higher is better. Comparable within one result only; the search route leaves it out. */\n score: number\n}\n\n/** A hit as the search route answers it and a client gets it: where the Block is, its title and type, in the order of the ranking. */\nexport type BlockSearchHit = Omit<BlockHit, 'score'>\n\n/** Markdown of `nodes` serialised as the content of one document. */\nexport type BlockMarkdown = (nodes: readonly JSONContent[]) => string\n\nexport interface ProjectOptions {\n /**\n * The Markdown serializer of the hash: `pruefungHash` is the SHA-256 of the title line, a blank line and the Markdown of the\n * body (of a Typed block), and the hash of any other Block is that of its Markdown. Core has no serializer (it needs the\n * schema's Markdown handlers), so a host passes one; without it the plain text takes its place (`title`, a blank line, the\n * body text) and the hash is only comparable with other hashes of the same projection.\n */\n markdown?: BlockMarkdown\n}\n\nconst CHROME: ReadonlySet<string> = new Set([TYPED_BLOCK_TITLE, TYPED_BLOCK_DESCRIPTION, TYPED_BLOCK_TAGS])\n/** The chrome of a Typed block that is not in its text: the title is its own field, the tags row is data. */\nconst NOT_TEXT: ReadonlySet<string> = new Set([TYPED_BLOCK_TITLE, TYPED_BLOCK_TAGS])\nconst INLINE: ReadonlySet<string> = new Set([TEXT_NODE, HARD_BREAK_NODE, MENTION_NODE, TAG_NODE, REFERENCE_NODE, CITE_NODE])\n\nconst attrString = (node: JSONContent, key: string): string => typeof node.attrs?.[key] === 'string' ? node.attrs[key] as string : ''\n\n/** The plain text of an inline node or a textblock's inline content. Internal: `document_outline` counts and titles Blocks with it. */\nexport function inlineText(node: JSONContent): string {\n switch (node.type) {\n case TEXT_NODE: return node.text ?? ''\n case HARD_BREAK_NODE: return '\\n'\n case MENTION_NODE: return `@${attrString(node, 'label') || attrString(node, 'id')}`\n case TAG_NODE: return `#${attrString(node, 'name')}`\n case REFERENCE_NODE: return attrString(node, 'label')\n case CITE_NODE: return `[cite:${attrString(node, 'key')}]`\n default: return (node.content ?? []).map(inlineText).join('')\n }\n}\n\nconst holdsInline = (node: JSONContent): boolean => node.content?.some(child => child.type !== undefined && INLINE.has(child.type)) === true\n\n/** The plain text of `node`'s content: one line per textblock, chrome of a Typed block left out. */\nfunction plainText(node: JSONContent): string {\n if (holdsInline(node)) return inlineText(node)\n return (node.content ?? []).filter(child => !(child.type !== undefined && NOT_TEXT.has(child.type))).map(plainText).filter(Boolean).join('\\n')\n}\n\n/** The title of a Block: a Typed block's title row, a heading's text, `''` for any other Block. Internal, like `inlineText`. */\nexport const titleOf = (node: JSONContent): string => {\n if (isTypedBlock(node)) return (node.content ?? []).filter(child => child.type === TYPED_BLOCK_TITLE).map(inlineText).join('')\n return node.type === 'heading' ? inlineText(node) : ''\n}\n\n/** Collects the tag names, reference chips and embedded Blocks (`embeds`, by `documentId/blockId`) of `node`'s whole subtree. */\nfunction collect(node: JSONContent, documentId: string, tags: Set<string>, references: Map<string, BlockReferenceRow>, embeds: Map<string, BlockAddress>): void {\n if (node.type === TAG_NODE) {\n const name = attrString(node, 'name')\n if (name) tags.add(name)\n }\n else if (node.type === TYPED_BLOCK_TAGS && Array.isArray(node.attrs?.tags)) {\n for (const name of node.attrs.tags) if (typeof name === 'string' && name) tags.add(name)\n }\n else if (node.type === REFERENCE_NODE) {\n const blockId = attrString(node, 'target')\n if (blockId) {\n const target = attrString(node, 'documentId') || documentId\n const locator = attrString(node, 'locator') || null\n references.set(`${addressKey({ documentId: target, blockId })}\\u0000${locator ?? ''}`, { documentId: target, blockId, locator })\n }\n }\n else if (node.type === EMBED_NODE) {\n const blockId = attrString(node, 'blockId')\n if (blockId) {\n const target = attrString(node, 'documentId') || documentId\n embeds.set(addressKey({ documentId: target, blockId }), { documentId: target, blockId })\n }\n }\n for (const child of node.content ?? []) collect(child, documentId, tags, references, embeds)\n}\n\n/** The Edges of a Block (`BlockRow.edges`): content Edges in the order reference, derivation, embed, tag, then the authored ones; one per kind and target. */\nfunction edgesOf(documentId: string, references: Iterable<BlockReferenceRow>, derivedFrom: BlockDerivationRow | null, embeds: Iterable<BlockAddress>, tags: Iterable<string>, authored: readonly AuthoredEdge[]): BlockEdgeRow[] {\n const edges = new Map<string, BlockEdgeRow>()\n const add = (edge: BlockEdgeRow): void => {\n const target = isTagEdgeTarget(edge.to) ? `tag\\n${edge.to.tag}` : addressKey(edge.to)\n const key = `${edge.origin}\\n${edge.kind}\\n${target}`\n if (!edges.has(key)) edges.set(key, edge)\n }\n for (const { documentId: target, blockId } of references) add({ to: { documentId: target, blockId }, kind: 'reference', origin: 'content' })\n if (derivedFrom && 'blockId' in derivedFrom) add({ to: { documentId: derivedFrom.documentId, blockId: derivedFrom.blockId }, kind: 'derivation', origin: 'content' })\n for (const { documentId: target, blockId } of embeds) add({ to: { documentId: target, blockId }, kind: 'embed', origin: 'content' })\n for (const tag of tags) add({ to: { tag }, kind: 'tag', origin: 'content' })\n for (const { to, kind } of authored) add({ to: { documentId: to.documentId ?? documentId, blockId: to.blockId }, kind, origin: 'meta' })\n return [...edges.values()]\n}\n\n/** The `hash` and the `sourceHash` of `node`: for a Typed block the title line plus the body's Markdown, and the body's Markdown alone; for any other Block both the Block's Markdown. */\nfunction hashesOf(node: JSONContent, title: string, text: string, markdown: BlockMarkdown | undefined): { hash: string, sourceHash: string } {\n if (!isTypedBlock(node)) {\n const hash = sha256Hex(markdown ? markdown([node]) : text)\n return { hash, sourceHash: hash }\n }\n const body = (node.content ?? []).filter(child => !(child.type !== undefined && CHROME.has(child.type)))\n const bodyText = markdown ? markdown(body) : body.map(plainText).filter(Boolean).join('\\n')\n return { hash: sha256Hex(`${title}\\n\\n${bodyText}`), sourceHash: sha256Hex(bodyText) }\n}\n\nconst FILE_HASH = /^[0-9a-f]{64}$/i\n\n/** The SHA-256 of the uploaded file a Block holds: the `hash` of a media node (image, audio, video, file) itself, or of the first body block of a Typed block (its source file); `''` for a Block without one. */\nfunction fileHashOf(node: JSONContent): string {\n const source = isTypedBlock(node) ? node.content?.find(child => !(child.type !== undefined && CHROME.has(child.type))) : node\n const hash = source?.attrs?.hash\n return typeof hash === 'string' && FILE_HASH.test(hash) ? hash.toLowerCase() : ''\n}\n\nfunction rowOf(node: JSONContent, blockId: string, documentId: string, markdown: BlockMarkdown | undefined): BlockRow {\n const typed = isTypedBlock(node)\n const title = titleOf(node)\n const text = plainText(node)\n const tags = new Set<string>()\n const references = new Map<string, BlockReferenceRow>()\n const embeds = new Map<string, BlockAddress>()\n collect(node, documentId, tags, references, embeds)\n const derivation = derivationOf(node)\n const from = derivation?.from\n const { [DERIVATION_KEY]: _derivation, ...meta } = typed ? metaOf(node) : {}\n const sha256 = fileHashOf(node)\n // The file's SHA-256 is a property of the row, so a drop of the same file is found (`core/import-check`); an authored `sha256` property wins.\n const properties = sha256 && !('sha256' in meta) ? { ...meta, sha256 } : meta\n const derivedFrom: BlockDerivationRow | null = !from ? null : 'block' in from ? { documentId: from.document || documentId, blockId: from.block } : { asset: from.asset }\n return {\n documentId,\n blockId,\n type: typed ? typeIdOf(node) : node.type ?? '',\n title,\n properties,\n tags: [...tags],\n references: [...references.values()],\n edges: edgesOf(documentId, references.values(), derivedFrom, embeds.values(), tags, authoredEdgesOf(properties[META_EDGES_KEY])),\n derivedFrom,\n ...hashesOf(node, title, text, markdown),\n text,\n }\n}\n\n/**\n * The rows of a Document's content: one per node with a block ID, in document order (a Block before the Blocks nested in it).\n * `content` is the Document's TipTap JSON in the current shape; nodes without an ID, and the inline nodes inside a textblock\n * (a mention's `id` is a user's, not a block's), have no row. Pure: the same content and options give the same rows.\n */\nexport function projectDocument(content: JSONContent, documentId: string, options: ProjectOptions = {}): BlockRow[] {\n const rows: BlockRow[] = []\n const walk = (node: JSONContent): void => {\n if (holdsInline(node)) return\n for (const child of node.content ?? []) {\n const id = child.attrs?.id\n if (typeof id === 'string' && id) rows.push(rowOf(child, id, documentId, options.markdown))\n walk(child)\n }\n }\n walk(content)\n return rows\n}\n\n/** The Edges that leave `row`, each with the Block it is written in. */\nexport const blockEdgesOf = (row: Pick<BlockRow, 'documentId' | 'blockId' | 'edges'>): BlockEdge[] =>\n row.edges.map(edge => ({ from: { documentId: row.documentId, blockId: row.blockId }, ...structuredClone(edge) }))\n\n/** The Edges of `rows` that point at the Block `address` (`tag` Edges never do), in the order of the rows. */\nexport function edgesInto(rows: Iterable<Pick<BlockRow, 'documentId' | 'blockId' | 'edges'>>, address: BlockAddress): BlockEdge[] {\n const found: BlockEdge[] = []\n for (const row of rows) {\n for (const edge of row.edges) {\n if (isTagEdgeTarget(edge.to) || !sameAddress(edge.to, address)) continue\n found.push({ from: { documentId: row.documentId, blockId: row.blockId }, ...structuredClone(edge) })\n }\n }\n return found\n}\n\n/**\n * The nodes of Block `blockId` in a Document's content, as `blockContentOf` of the block route: `block` = the Block itself,\n * `children` = what it holds (a Typed block: its body without the title, description and tags rows; a list or a quote: its\n * items; a Block that holds only text: nothing). `null` when the content has no Block with that ID. Pure and a copy-free view:\n * the nodes are those of `content`.\n */\nexport function blockContentOf(content: JSONContent, blockId: string, mode: BlockContentMode): JSONContent[] | null {\n const found = findBlock(content, blockId)\n if (!found) return null\n if (mode === 'block') return [found]\n if (holdsInline(found)) return []\n return (found.content ?? []).filter(child => !(isTypedBlock(found) && child.type !== undefined && CHROME.has(child.type)))\n}\n\nfunction findBlock(node: JSONContent, blockId: string): JSONContent | null {\n if (holdsInline(node)) return null\n for (const child of node.content ?? []) {\n if (child.attrs?.id === blockId) return child\n const nested = findBlock(child, blockId)\n if (nested) return nested\n }\n return null\n}\n"],"mappings":"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"}
@@ -32,17 +32,17 @@ type __VLS_Slots = {} & { [K in NonNullable<typeof __VLS_13>]?: (props: typeof _
32
32
  binary?: (props: typeof __VLS_26) => any;
33
33
  };
34
34
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
- ready: (target: DocumentSurfaceTarget) => any;
36
35
  "update:modelValue": (text: string) => any;
37
36
  save: (text: string) => any;
37
+ ready: (target: DocumentSurfaceTarget) => any;
38
38
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
39
- onReady?: ((target: DocumentSurfaceTarget) => any) | undefined;
40
39
  "onUpdate:modelValue"?: ((text: string) => any) | undefined;
41
40
  onSave?: ((text: string) => any) | undefined;
41
+ onReady?: ((target: DocumentSurfaceTarget) => any) | undefined;
42
42
  }>, {
43
- modelValue: string;
44
43
  editable: boolean;
45
44
  wordWrap: boolean;
45
+ modelValue: string;
46
46
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
47
47
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
48
48
  declare const _default: typeof __VLS_export;
@@ -4,10 +4,10 @@ import { propertyHostOf } from "../properties.js";
4
4
  import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
5
5
  import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
6
6
  import { compositeFieldText, isStructuredField } from "../inspector-model.js";
7
- import _sfc_main$1 from "./DocumentBadges.js";
8
- import _sfc_main$2 from "./DerivationBadge.js";
9
- import _sfc_main$3 from "./CompositeFieldEditor.js";
10
- import _sfc_main$4 from "./InspectorSections.js";
7
+ import _sfc_main$1 from "./DerivationBadge.js";
8
+ import _sfc_main$2 from "./CompositeFieldEditor.js";
9
+ import _sfc_main$3 from "./InspectorSections.js";
10
+ import _sfc_main$4 from "./DocumentBadges.js";
11
11
  import { useCompositeInspector } from "../use-composite-inspector.js";
12
12
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, toDisplayString, unref, useId, withCtx } from "vue";
13
13
  //#region src/features/composites/components/CompositeInspector.vue
@@ -109,7 +109,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
109
109
  class: "text-sm font-semibold text-[var(--sk-fg-highlighted)]"
110
110
  }, toDisplayString(unref(t)("composites.inspector.document")), 9, _hoisted_4),
111
111
  createCommentVNode(" Chips of the whole document other features add (`EditorFeature.documentBadges`), e.g. the Freigabestatus. "),
112
- createVNode(_sfc_main$1, { editor: __props.editor }, null, 8, ["editor"])
112
+ createVNode(_sfc_main$4, { editor: __props.editor }, null, 8, ["editor"])
113
113
  ]), createElementVNode("div", _hoisted_5, [renderSlot(_ctx.$slots, "document"), createElementVNode("dl", _hoisted_6, [
114
114
  createElementVNode("dt", _hoisted_7, toDisplayString(unref(t)("composites.inspector.composites")), 1),
115
115
  unref(kinds).length ? (openBlock(), createElementBlock("dd", _hoisted_8, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(kinds), (kind) => {
@@ -267,7 +267,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
267
267
  label: field.label,
268
268
  size: "sm"
269
269
  }, {
270
- default: withCtx(() => [createCommentVNode(" Fields whose value is not text (reference, bibliography, derivation) have their own editors, as in the details card; a text input would overwrite the stored object with a string. "), unref(isStructuredField)(field) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.editor ? (openBlock(), createBlock(_sfc_main$3, {
270
+ default: withCtx(() => [createCommentVNode(" Fields whose value is not text (reference, bibliography, derivation) have their own editors, as in the details card; a text input would overwrite the stored object with a string. "), unref(isStructuredField)(field) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.editor ? (openBlock(), createBlock(_sfc_main$2, {
271
271
  key: 0,
272
272
  editor: __props.editor,
273
273
  row: unref(properties),
@@ -335,14 +335,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
335
335
  label: unref(t)("composites.inspector.derivation"),
336
336
  size: "sm"
337
337
  }, {
338
- default: withCtx(() => [createVNode(_sfc_main$2, {
338
+ default: withCtx(() => [createVNode(_sfc_main$1, {
339
339
  editor: __props.editor,
340
340
  node: unref(derivation).node
341
341
  }, null, 8, ["editor", "node"])]),
342
342
  _: 1
343
343
  }, 8, ["label"])) : createCommentVNode("v-if", true),
344
344
  createCommentVNode(" Sections other features add (`EditorFeature.inspectorSections`), e.g. the Prüfstand. "),
345
- __props.editor ? (openBlock(), createBlock(_sfc_main$4, {
345
+ __props.editor ? (openBlock(), createBlock(_sfc_main$3, {
346
346
  key: 3,
347
347
  editor: __props.editor,
348
348
  node: blockNode.value
@@ -1,7 +1,6 @@
1
1
  import { embedViewOptions } from "./embed-dom.js";
2
2
  import { typedBlockViewOptions } from "./node-views.js";
3
3
  import { compositeFeatureCore } from "./feature.js";
4
- import _sfc_main$13 from "./components/DocumentBadges.js";
5
4
  import _sfc_main from "./components/BlockEmbedView.js";
6
5
  import _sfc_main$1 from "./components/CiteView.js";
7
6
  import _sfc_main$11 from "./components/CompositeView.js";
@@ -21,6 +20,7 @@ import _sfc_main$14 from "./components/EdgesSection.js";
21
20
  import _sfc_main$16 from "./components/FocusChip.js";
22
21
  import _sfc_main$18 from "./components/ReferenceView.js";
23
22
  import _sfc_main$19 from "./components/SettingsEditingTab.js";
23
+ import _sfc_main$13 from "./components/DocumentBadges.js";
24
24
  import _sfc_main$6 from "./components/CompositeInspector.js";
25
25
  import _sfc_main$9 from "./components/CompositeTable.js";
26
26
  import { VueNodeViewRenderer } from "@tiptap/vue-3";
@@ -41,28 +41,28 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
41
41
  settingsOpen: import("vue").WritableComputedRef<boolean, boolean>;
42
42
  settingsTab: import("vue").WritableComputedRef<string | undefined, string | undefined>;
43
43
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
44
+ "update:modelValue": (markdown: string) => any;
45
+ ready: (editor: Editor) => any;
46
+ settingsOpen: (tab?: string | undefined) => any;
44
47
  "update:sidebarOpen": (value: boolean) => any;
45
48
  "update:commentsOpen": (value: boolean) => any;
46
- ready: (editor: Editor) => any;
47
- "update:modelValue": (markdown: string) => any;
49
+ frontmatter: (frontmatter: string | null) => any;
48
50
  metadataCreate: (list: string, item: MetadataItem) => any;
49
51
  contentError: (error: Error) => any;
50
- settingsOpen: (tab?: string | undefined) => any;
51
- frontmatter: (frontmatter: string | null) => any;
52
52
  diagnostics: (diagnostics: MarkdownDiagnostic[]) => any;
53
53
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
54
+ "onUpdate:modelValue"?: ((markdown: string) => any) | undefined;
55
+ onReady?: ((editor: Editor) => any) | undefined;
56
+ onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
54
57
  "onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
55
58
  "onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
56
- onReady?: ((editor: Editor) => any) | undefined;
57
- "onUpdate:modelValue"?: ((markdown: string) => any) | undefined;
59
+ onFrontmatter?: ((frontmatter: string | null) => any) | undefined;
58
60
  onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
59
61
  onContentError?: ((error: Error) => any) | undefined;
60
- onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
61
- onFrontmatter?: ((frontmatter: string | null) => any) | undefined;
62
62
  onDiagnostics?: ((diagnostics: MarkdownDiagnostic[]) => any) | undefined;
63
63
  }>, {
64
- modelValue: string;
65
64
  editable: boolean;
65
+ modelValue: string;
66
66
  settingsUi: "internal" | "external";
67
67
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
68
68
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,4 +1,5 @@
1
- import { TYPED_BLOCK_TITLE, isTypedBlock, typeIdOf } from "../core/typed-block.js";
1
+ import { isTypedBlock, typeIdOf } from "../core/typed-block.js";
2
+ import { inlineText, titleOf } from "../core/block-index.js";
2
3
  import { loadCurrentContent } from "./stored-content.js";
3
4
  import { createBlockMarkdown } from "./block-index.js";
4
5
  import { mayReadDocument } from "./readable-documents.js";
@@ -22,23 +23,6 @@ async function readableContent(ctx, documentId) {
22
23
  if (!await mayReadDocument(ctx.ports, ctx.user, documentId)) return null;
23
24
  return ctx.liveContent(documentId) ?? await loadCurrentContent(ctx.ports.documents, documentId);
24
25
  }
25
- const attrString = (node, key) => typeof node.attrs?.[key] === "string" ? node.attrs[key] : "";
26
- /** The plain text of an inline node or a textblock's inline content, chips as their label. */
27
- function inlineText(node) {
28
- switch (node.type) {
29
- case "text": return node.text ?? "";
30
- case "hardBreak": return " ";
31
- case "mention": return `@${attrString(node, "label") || attrString(node, "id")}`;
32
- case "tag": return `#${attrString(node, "name")}`;
33
- case "reference": return attrString(node, "label");
34
- default: return (node.content ?? []).map(inlineText).join("");
35
- }
36
- }
37
- /** The title of a Block as the Block index has it: a Typed block's title row, a heading's text, `''` for any other Block. */
38
- function titleOf(node) {
39
- if (isTypedBlock(node)) return (node.content ?? []).filter((child) => child.type === TYPED_BLOCK_TITLE).map(inlineText).join("").trim();
40
- return node.type === "heading" ? inlineText(node).trim() : "";
41
- }
42
26
  /**
43
27
  * Every Block of `content` (every non-inline node with an `id`, the rule of `blockLinesOfContent`), in document order, a Block
44
28
  * before the Blocks nested in it. A Block's `children` are the Blocks directly inside it, through nodes without an ID (a list
@@ -1 +1 @@
1
- {"version":3,"file":"document-tools.js","names":[],"sources":["../../src/server/document-tools.ts"],"sourcesContent":["// i18n-ignore-file: model-facing tool descriptions and results (German, like the chat's system prompt)\nimport type { JSONContent } from '@tiptap/core'\nimport { jsonSchema, tool } from 'ai'\nimport { z } from 'zod'\nimport type { BlockMarkdown } from '../core/block-index'\nimport { isTypedBlock, TYPED_BLOCK_TITLE, typeIdOf } from '../core/typed-block'\nimport { createBlockMarkdown } from './block-index'\nimport { findBlock, INLINE_NODES } from './document-blocks'\nimport { mayReadDocument } from './readable-documents'\nimport { loadCurrentContent } from './stored-content'\nimport type { ToolContext, ToolProvider } from './tools'\nimport { parsed, READ_BLOCK_LIMIT, unreadableBlock, type WorkspaceToolsOptions } from './workspace-tools'\n\n// Read-only tools of the document chat that look into one Document as a whole: its tree of Blocks and several Blocks at once.\n// Unlike `createWorkspaceTools` they read the Document's content directly (no Block index): the live content of a Document\n// open in the collaboration server first, else the stored content in the current shape.\n\n/** Block IDs `read_blocks` reads in one call. */\nconst READ_BLOCKS_MAX = 50\n/** Characters of all Blocks one `read_blocks` call returns; the Blocks after the one that reaches it are left out with a note. */\nconst READ_BLOCKS_TOTAL_LIMIT = 8 * READ_BLOCK_LIMIT\n\n/** The Markdown serializer `read_blocks` writes with: the options of `createWorkspaceTools`. */\nexport type DocumentToolsOptions = WorkspaceToolsOptions\n\nconst outlineInput = z.object({\n documentId: z.string().min(1).max(200).optional(),\n})\nconst readBlocksInput = z.object({\n documentId: z.string().min(1).max(200).optional(),\n blockIds: z.array(z.string().min(1).max(100)).min(1).max(READ_BLOCKS_MAX),\n})\n\nconst NO_DOCUMENT = 'Im Chat ist kein Dokument geöffnet: gib documentId an.'\n\n/** The current content of `documentId` (live first, else stored), `null` when the user may not read it or it does not exist. */\nasync function readableContent(ctx: ToolContext, documentId: string): Promise<JSONContent | null> {\n if (!await mayReadDocument(ctx.ports, ctx.user, documentId)) return null\n return ctx.liveContent(documentId) ?? await loadCurrentContent(ctx.ports.documents, documentId)\n}\n\nconst attrString = (node: JSONContent, key: string): string => typeof node.attrs?.[key] === 'string' ? node.attrs[key] as string : ''\n\n/** The plain text of an inline node or a textblock's inline content, chips as their label. */\nfunction inlineText(node: JSONContent): string {\n switch (node.type) {\n case 'text': return node.text ?? ''\n case 'hardBreak': return ' '\n case 'mention': return `@${attrString(node, 'label') || attrString(node, 'id')}`\n case 'tag': return `#${attrString(node, 'name')}`\n case 'reference': return attrString(node, 'label')\n default: return (node.content ?? []).map(inlineText).join('')\n }\n}\n\n/** The title of a Block as the Block index has it: a Typed block's title row, a heading's text, `''` for any other Block. */\nfunction titleOf(node: JSONContent): string {\n if (isTypedBlock(node)) return (node.content ?? []).filter(child => child.type === TYPED_BLOCK_TITLE).map(inlineText).join('').trim()\n return node.type === 'heading' ? inlineText(node).trim() : ''\n}\n\n/** One Block of `document_outline`. */\ninterface OutlineEntry {\n id: string\n /** Node type (`paragraph`, `heading`, `typedBlock`, …). */\n type: string\n /** Level of a heading. */\n level?: number\n /** Block type id of a Typed block. */\n blockType?: string\n /** Title of a Typed block, text of a heading. */\n title?: string\n /** IDs of the Blocks directly inside this one, in document order. */\n children?: string[]\n /** Characters of the Block's text, nested Blocks included. */\n chars: number\n}\n\n/**\n * Every Block of `content` (every non-inline node with an `id`, the rule of `blockLinesOfContent`), in document order, a Block\n * before the Blocks nested in it. A Block's `children` are the Blocks directly inside it, through nodes without an ID (a list\n * without one, say).\n */\nfunction outlineOf(content: JSONContent): OutlineEntry[] {\n const entries: OutlineEntry[] = []\n const walk = (node: JSONContent): { chars: number, blocks: string[] } => {\n let chars = 0\n const blocks: string[] = []\n for (const child of node.content ?? []) {\n if (child.type !== undefined && INLINE_NODES.has(child.type)) {\n chars += inlineText(child).length\n continue\n }\n const id = child.attrs?.id\n if (typeof id !== 'string' || !id) {\n const inner = walk(child)\n chars += inner.chars\n blocks.push(...inner.blocks)\n continue\n }\n const entry: OutlineEntry = { id, type: child.type ?? '', chars: 0 }\n if (child.type === 'heading' && typeof child.attrs?.level === 'number') entry.level = child.attrs.level\n if (isTypedBlock(child)) entry.blockType = typeIdOf(child)\n const title = titleOf(child)\n if (title) entry.title = title\n entries.push(entry)\n const inner = walk(child)\n entry.chars = inner.chars\n if (inner.blocks.length) entry.children = inner.blocks\n chars += inner.chars\n blocks.push(id)\n }\n return { chars, blocks }\n }\n walk(content)\n return entries\n}\n\n/**\n * The read-only document tools of the document chat, for `EditorServerOptions.tools` / `provideEditorServer({ tools })`, next\n * to `createWorkspaceTools`:\n *\n * - `document_outline(documentId?)`: the tree of all Blocks of the open Document (or of another one the user may read): per\n * Block its ID, node type, heading level or Block type and title, the IDs of the Blocks directly inside it and the length of\n * its text;\n * - `read_blocks(documentId?, blockIds)`: up to 50 Blocks of one Document as Markdown, each cut like `read_block`\n * (`READ_BLOCK_LIMIT`), all together at most eight times that.\n *\n * Both read the current content (live content of an open Document first, else `loadCurrentContent`) and need no index. An\n * unreadable Document and an unknown Block answer the same sentence as `read_block`, so the tools never confirm that a\n * Document or Block exists where the user may not look.\n */\nexport function createDocumentTools(options: DocumentToolsOptions = {}): ToolProvider[] {\n let markdown = options.markdown\n const markdownOf = (): BlockMarkdown => markdown ??= createBlockMarkdown({\n ...(options.schema && { schema: options.schema }),\n ...(options.blockTypes && { blockTypes: options.blockTypes }),\n })\n\n const documentOutline: ToolProvider = {\n name: 'document_outline',\n tool: tool({\n description: 'Liefert die Gliederung eines Dokuments als JSON: alle Blöcke in Dokumentreihenfolge, je mit id, type (Knotentyp), level (bei Überschriften), blockType (bei typisierten Blöcken), title (Überschriftentext bzw. Titel des typisierten Blocks), children (IDs der direkt enthaltenen Blöcke) und chars (Textlänge in Zeichen, enthaltene Blöcke eingeschlossen). Ohne documentId das im Chat geöffnete Dokument in seinem aktuellen Stand; mit documentId ein anderes lesbares Dokument. Den Inhalt einzelner Blöcke liest du mit read_blocks.',\n inputSchema: jsonSchema<{ documentId?: string }>({\n type: 'object',\n properties: {\n documentId: { type: 'string', description: 'ID des Dokuments; ohne Angabe das geöffnete Dokument.' },\n },\n }),\n }),\n async handle(ctx, input) {\n const checked = parsed(outlineInput, input)\n if ('problem' in checked) return checked.problem\n const documentId = checked.value.documentId ?? ctx.documentId\n if (!documentId) return NO_DOCUMENT\n const content = await readableContent(ctx, documentId)\n // The same sentence for an unreadable and a missing Document: the tool never confirms that a Document exists the user may not read.\n if (!content) return `Das Dokument „${documentId}“ ist nicht lesbar oder existiert nicht.`\n const blocks = outlineOf(content)\n if (!blocks.length) return `Das Dokument „${documentId}“ hat keine Blöcke.`\n return JSON.stringify({ documentId, blocks })\n },\n }\n\n const readBlocks: ToolProvider = {\n name: 'read_blocks',\n tool: tool({\n description: `Liest Blöcke eines Dokuments als Markdown, bis zu ${READ_BLOCKS_MAX} auf einmal, in der angegebenen Reihenfolge. Ohne documentId aus dem im Chat geöffneten Dokument in seinem aktuellen Stand, mit documentId aus einem anderen lesbaren Dokument. Ein Block enthält die in ihm verschachtelten Blöcke. Block-IDs stammen aus document_outline oder find_blocks. Ein Block über ${READ_BLOCK_LIMIT} Zeichen wird gekürzt; ab ${READ_BLOCKS_TOTAL_LIMIT} Zeichen insgesamt werden die übrigen Blöcke ausgelassen und sind mit einem weiteren Aufruf zu lesen.`,\n inputSchema: jsonSchema<{ documentId?: string, blockIds: string[] }>({\n type: 'object',\n properties: {\n documentId: { type: 'string', description: 'ID des Dokuments; ohne Angabe das geöffnete Dokument.' },\n blockIds: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: READ_BLOCKS_MAX, description: 'IDs der Blöcke im Dokument.' },\n },\n required: ['blockIds'],\n }),\n }),\n async handle(ctx, input) {\n const checked = parsed(readBlocksInput, input)\n if ('problem' in checked) return checked.problem\n const documentId = checked.value.documentId ?? ctx.documentId\n if (!documentId) return NO_DOCUMENT\n const blockIds = [...new Set(checked.value.blockIds)]\n const content = await readableContent(ctx, documentId)\n const parts: string[] = []\n let total = 0\n for (const [i, blockId] of blockIds.entries()) {\n if (total >= READ_BLOCKS_TOTAL_LIMIT) {\n const rest = blockIds.slice(i)\n parts.push(`[… ${rest.length} weitere Blöcke ausgelassen, die Grenze von ${READ_BLOCKS_TOTAL_LIMIT} Zeichen ist erreicht: ${rest.join(', ')}]`)\n break\n }\n const node = content && findBlock(content, blockId)\n if (!node) {\n parts.push(unreadableBlock(documentId, blockId))\n continue\n }\n const title = titleOf(node)\n const head = `Dokument „${documentId}“, Block „${blockId}“ (${isTypedBlock(node) ? typeIdOf(node) : node.type ?? ''}${title ? `, ${title}` : ''}):`\n const body = markdownOf()([node]).trim()\n const shown = body.length > READ_BLOCK_LIMIT ? `${body.slice(0, READ_BLOCK_LIMIT)}\\n\\n[… gekürzt, ${body.length - READ_BLOCK_LIMIT} Zeichen fehlen]` : body\n total += Math.min(body.length, READ_BLOCK_LIMIT)\n parts.push(`${head}\\n\\n${shown}`)\n }\n return parts.join('\\n\\n---\\n\\n')\n },\n }\n\n return [documentOutline, readBlocks]\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"document-tools.js","names":[],"sources":["../../src/server/document-tools.ts"],"sourcesContent":["// i18n-ignore-file: model-facing tool descriptions and results (German, like the chat's system prompt)\nimport type { JSONContent } from '@tiptap/core'\nimport { jsonSchema, tool } from 'ai'\nimport { z } from 'zod'\nimport { type BlockMarkdown, inlineText, titleOf } from '../core/block-index'\nimport { isTypedBlock, typeIdOf } from '../core/typed-block'\nimport { createBlockMarkdown } from './block-index'\nimport { findBlock, INLINE_NODES } from './document-blocks'\nimport { mayReadDocument } from './readable-documents'\nimport { loadCurrentContent } from './stored-content'\nimport type { ToolContext, ToolProvider } from './tools'\nimport { parsed, READ_BLOCK_LIMIT, unreadableBlock, type WorkspaceToolsOptions } from './workspace-tools'\n\n// Read-only tools of the document chat that look into one Document as a whole: its tree of Blocks and several Blocks at once.\n// Unlike `createWorkspaceTools` they read the Document's content directly (no Block index): the live content of a Document\n// open in the collaboration server first, else the stored content in the current shape.\n\n/** Block IDs `read_blocks` reads in one call. */\nconst READ_BLOCKS_MAX = 50\n/** Characters of all Blocks one `read_blocks` call returns; the Blocks after the one that reaches it are left out with a note. */\nconst READ_BLOCKS_TOTAL_LIMIT = 8 * READ_BLOCK_LIMIT\n\n/** The Markdown serializer `read_blocks` writes with: the options of `createWorkspaceTools`. */\nexport type DocumentToolsOptions = WorkspaceToolsOptions\n\nconst outlineInput = z.object({\n documentId: z.string().min(1).max(200).optional(),\n})\nconst readBlocksInput = z.object({\n documentId: z.string().min(1).max(200).optional(),\n blockIds: z.array(z.string().min(1).max(100)).min(1).max(READ_BLOCKS_MAX),\n})\n\nconst NO_DOCUMENT = 'Im Chat ist kein Dokument geöffnet: gib documentId an.'\n\n/** The current content of `documentId` (live first, else stored), `null` when the user may not read it or it does not exist. */\nasync function readableContent(ctx: ToolContext, documentId: string): Promise<JSONContent | null> {\n if (!await mayReadDocument(ctx.ports, ctx.user, documentId)) return null\n return ctx.liveContent(documentId) ?? await loadCurrentContent(ctx.ports.documents, documentId)\n}\n\n/** One Block of `document_outline`. */\ninterface OutlineEntry {\n id: string\n /** Node type (`paragraph`, `heading`, `typedBlock`, …). */\n type: string\n /** Level of a heading. */\n level?: number\n /** Block type id of a Typed block. */\n blockType?: string\n /** Title of a Typed block, text of a heading. */\n title?: string\n /** IDs of the Blocks directly inside this one, in document order. */\n children?: string[]\n /** Characters of the Block's text, nested Blocks included. */\n chars: number\n}\n\n/**\n * Every Block of `content` (every non-inline node with an `id`, the rule of `blockLinesOfContent`), in document order, a Block\n * before the Blocks nested in it. A Block's `children` are the Blocks directly inside it, through nodes without an ID (a list\n * without one, say).\n */\nfunction outlineOf(content: JSONContent): OutlineEntry[] {\n const entries: OutlineEntry[] = []\n const walk = (node: JSONContent): { chars: number, blocks: string[] } => {\n let chars = 0\n const blocks: string[] = []\n for (const child of node.content ?? []) {\n if (child.type !== undefined && INLINE_NODES.has(child.type)) {\n chars += inlineText(child).length\n continue\n }\n const id = child.attrs?.id\n if (typeof id !== 'string' || !id) {\n const inner = walk(child)\n chars += inner.chars\n blocks.push(...inner.blocks)\n continue\n }\n const entry: OutlineEntry = { id, type: child.type ?? '', chars: 0 }\n if (child.type === 'heading' && typeof child.attrs?.level === 'number') entry.level = child.attrs.level\n if (isTypedBlock(child)) entry.blockType = typeIdOf(child)\n const title = titleOf(child)\n if (title) entry.title = title\n entries.push(entry)\n const inner = walk(child)\n entry.chars = inner.chars\n if (inner.blocks.length) entry.children = inner.blocks\n chars += inner.chars\n blocks.push(id)\n }\n return { chars, blocks }\n }\n walk(content)\n return entries\n}\n\n/**\n * The read-only document tools of the document chat, for `EditorServerOptions.tools` / `provideEditorServer({ tools })`, next\n * to `createWorkspaceTools`:\n *\n * - `document_outline(documentId?)`: the tree of all Blocks of the open Document (or of another one the user may read): per\n * Block its ID, node type, heading level or Block type and title, the IDs of the Blocks directly inside it and the length of\n * its text;\n * - `read_blocks(documentId?, blockIds)`: up to 50 Blocks of one Document as Markdown, each cut like `read_block`\n * (`READ_BLOCK_LIMIT`), all together at most eight times that.\n *\n * Both read the current content (live content of an open Document first, else `loadCurrentContent`) and need no index. An\n * unreadable Document and an unknown Block answer the same sentence as `read_block`, so the tools never confirm that a\n * Document or Block exists where the user may not look.\n */\nexport function createDocumentTools(options: DocumentToolsOptions = {}): ToolProvider[] {\n let markdown = options.markdown\n const markdownOf = (): BlockMarkdown => markdown ??= createBlockMarkdown({\n ...(options.schema && { schema: options.schema }),\n ...(options.blockTypes && { blockTypes: options.blockTypes }),\n })\n\n const documentOutline: ToolProvider = {\n name: 'document_outline',\n tool: tool({\n description: 'Liefert die Gliederung eines Dokuments als JSON: alle Blöcke in Dokumentreihenfolge, je mit id, type (Knotentyp), level (bei Überschriften), blockType (bei typisierten Blöcken), title (Überschriftentext bzw. Titel des typisierten Blocks), children (IDs der direkt enthaltenen Blöcke) und chars (Textlänge in Zeichen, enthaltene Blöcke eingeschlossen). Ohne documentId das im Chat geöffnete Dokument in seinem aktuellen Stand; mit documentId ein anderes lesbares Dokument. Den Inhalt einzelner Blöcke liest du mit read_blocks.',\n inputSchema: jsonSchema<{ documentId?: string }>({\n type: 'object',\n properties: {\n documentId: { type: 'string', description: 'ID des Dokuments; ohne Angabe das geöffnete Dokument.' },\n },\n }),\n }),\n async handle(ctx, input) {\n const checked = parsed(outlineInput, input)\n if ('problem' in checked) return checked.problem\n const documentId = checked.value.documentId ?? ctx.documentId\n if (!documentId) return NO_DOCUMENT\n const content = await readableContent(ctx, documentId)\n // The same sentence for an unreadable and a missing Document: the tool never confirms that a Document exists the user may not read.\n if (!content) return `Das Dokument „${documentId}“ ist nicht lesbar oder existiert nicht.`\n const blocks = outlineOf(content)\n if (!blocks.length) return `Das Dokument „${documentId}“ hat keine Blöcke.`\n return JSON.stringify({ documentId, blocks })\n },\n }\n\n const readBlocks: ToolProvider = {\n name: 'read_blocks',\n tool: tool({\n description: `Liest Blöcke eines Dokuments als Markdown, bis zu ${READ_BLOCKS_MAX} auf einmal, in der angegebenen Reihenfolge. Ohne documentId aus dem im Chat geöffneten Dokument in seinem aktuellen Stand, mit documentId aus einem anderen lesbaren Dokument. Ein Block enthält die in ihm verschachtelten Blöcke. Block-IDs stammen aus document_outline oder find_blocks. Ein Block über ${READ_BLOCK_LIMIT} Zeichen wird gekürzt; ab ${READ_BLOCKS_TOTAL_LIMIT} Zeichen insgesamt werden die übrigen Blöcke ausgelassen und sind mit einem weiteren Aufruf zu lesen.`,\n inputSchema: jsonSchema<{ documentId?: string, blockIds: string[] }>({\n type: 'object',\n properties: {\n documentId: { type: 'string', description: 'ID des Dokuments; ohne Angabe das geöffnete Dokument.' },\n blockIds: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: READ_BLOCKS_MAX, description: 'IDs der Blöcke im Dokument.' },\n },\n required: ['blockIds'],\n }),\n }),\n async handle(ctx, input) {\n const checked = parsed(readBlocksInput, input)\n if ('problem' in checked) return checked.problem\n const documentId = checked.value.documentId ?? ctx.documentId\n if (!documentId) return NO_DOCUMENT\n const blockIds = [...new Set(checked.value.blockIds)]\n const content = await readableContent(ctx, documentId)\n const parts: string[] = []\n let total = 0\n for (const [i, blockId] of blockIds.entries()) {\n if (total >= READ_BLOCKS_TOTAL_LIMIT) {\n const rest = blockIds.slice(i)\n parts.push(`[… ${rest.length} weitere Blöcke ausgelassen, die Grenze von ${READ_BLOCKS_TOTAL_LIMIT} Zeichen ist erreicht: ${rest.join(', ')}]`)\n break\n }\n const node = content && findBlock(content, blockId)\n if (!node) {\n parts.push(unreadableBlock(documentId, blockId))\n continue\n }\n const title = titleOf(node)\n const head = `Dokument „${documentId}“, Block „${blockId}“ (${isTypedBlock(node) ? typeIdOf(node) : node.type ?? ''}${title ? `, ${title}` : ''}):`\n const body = markdownOf()([node]).trim()\n const shown = body.length > READ_BLOCK_LIMIT ? `${body.slice(0, READ_BLOCK_LIMIT)}\\n\\n[… gekürzt, ${body.length - READ_BLOCK_LIMIT} Zeichen fehlen]` : body\n total += Math.min(body.length, READ_BLOCK_LIMIT)\n parts.push(`${head}\\n\\n${shown}`)\n }\n return parts.join('\\n\\n---\\n\\n')\n },\n }\n\n return [documentOutline, readBlocks]\n}\n"],"mappings":"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"}
@@ -5,10 +5,10 @@ import { basketById } from "../features/composites/baskets.js";
5
5
  import { useUiKit } from "../vue/kit/use-ui-kit.js";
6
6
  import { useEditorSettings } from "../vue/settings.js";
7
7
  import { useEditorI18n } from "../vue/use-editor-i18n.js";
8
+ import _sfc_main$1 from "../features/composites/components/DocumentBadges.js";
8
9
  import { DRAG_MIME, focusCrumbs, indexItems, isOutlineHeader, itemBasket } from "./tree-items.js";
9
10
  import { refocusRow, rowPosition } from "./row-focus.js";
10
11
  import { rowOf } from "./tree-model.js";
11
- import _sfc_main$1 from "../features/composites/components/DocumentBadges.js";
12
12
  import { useTreeModel } from "./use-tree-model.js";
13
13
  import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, toDisplayString, unref, useModel, withCtx, withKeys, withModifiers } from "vue";
14
14
  //#region src/tree/DocumentTree.vue
@@ -3,10 +3,10 @@ import { useEditorSettings } from "../vue/settings.js";
3
3
  import { useEditorContext } from "../vue/context.js";
4
4
  import { useEditorI18n } from "../vue/use-editor-i18n.js";
5
5
  import { useStoreRef } from "../vue/store-ref.js";
6
+ import { useFileImport } from "../features/media/vue.js";
6
7
  import { treeView } from "./tree-view-model.js";
7
8
  import { createTreeModel } from "./tree-model.js";
8
9
  import { createTreeExecutor, treeEditorOf } from "./tree-host.js";
9
- import { useFileImport } from "../features/media/vue.js";
10
10
  import { computed, onScopeDispose, shallowRef, watch } from "vue";
11
11
  //#region src/tree/use-tree-model.ts
12
12
  /**
@@ -82,21 +82,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
82
82
  editor: import("vue").ComputedRef<Editor | undefined>;
83
83
  preset: ComposedEditor<Component>;
84
84
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
85
+ "update:modelValue": (content: JSONContent) => any;
86
+ ready: (editor: Editor) => any;
87
+ settingsOpen: (tab?: string | undefined) => any;
85
88
  "update:sidebarOpen": (value: boolean) => any;
86
89
  "update:commentsOpen": (value: boolean) => any;
87
- ready: (editor: Editor) => any;
88
- "update:modelValue": (content: JSONContent) => any;
89
90
  metadataCreate: (list: string, item: MetadataItem) => any;
90
91
  contentError: (error: Error) => any;
91
- settingsOpen: (tab?: string | undefined) => any;
92
92
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
93
+ "onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
94
+ onReady?: ((editor: Editor) => any) | undefined;
95
+ onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
93
96
  "onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
94
97
  "onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
95
- onReady?: ((editor: Editor) => any) | undefined;
96
- "onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
97
98
  onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
98
99
  onContentError?: ((error: Error) => any) | undefined;
99
- onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
100
100
  }>, {
101
101
  content: ContentLevel | readonly FeatureId[];
102
102
  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
+ "update:modelValue": (content: JSONContent) => any;
45
+ ready: (editor: Editor) => any;
46
+ settingsOpen: (tab?: string | undefined) => any;
44
47
  "update:sidebarOpen": (value: boolean) => any;
45
48
  "update:commentsOpen": (value: boolean) => any;
46
- ready: (editor: Editor) => any;
47
- "update:modelValue": (content: JSONContent) => any;
48
49
  metadataCreate: (list: string, item: MetadataItem) => any;
49
50
  contentError: (error: Error) => any;
50
- settingsOpen: (tab?: string | undefined) => any;
51
51
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
52
+ "onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
53
+ onReady?: ((editor: Editor) => any) | undefined;
54
+ onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
52
55
  "onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
53
56
  "onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
54
- onReady?: ((editor: Editor) => any) | undefined;
55
- "onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
56
57
  onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
57
58
  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
+ "update:modelValue": (content: JSONContent) => any;
47
+ ready: (editor: Editor) => any;
48
+ settingsOpen: (tab?: string | undefined) => any;
46
49
  "update:sidebarOpen": (value: boolean) => any;
47
50
  "update:commentsOpen": (value: boolean) => any;
48
- ready: (editor: Editor) => any;
49
- "update:modelValue": (content: JSONContent) => any;
50
51
  metadataCreate: (list: string, item: MetadataItem) => any;
51
52
  contentError: (error: Error) => any;
52
- settingsOpen: (tab?: string | undefined) => any;
53
53
  }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
54
+ "onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
55
+ onReady?: ((editor: Editor) => any) | undefined;
56
+ onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
54
57
  "onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
55
58
  "onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
56
- onReady?: ((editor: Editor) => any) | undefined;
57
- "onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
58
59
  onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
59
60
  onContentError?: ((error: Error) => any) | undefined;
60
- onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
61
61
  }>, {
62
62
  editable: boolean;
63
63
  settingsUi: "internal" | "external";
@@ -1,6 +1,5 @@
1
1
  import { coreUiFeature } from "../core/core-feature.js";
2
2
  import { AI_BINDINGS, layerCompositions } from "../presets/layer.js";
3
- import { mediaFeature } from "../features/media/vue.js";
4
3
  import _sfc_main from "./components/EditorCommandPalette.js";
5
4
  import { aiFeature } from "../features/ai/vue.js";
6
5
  import { authorsFeature } from "../features/authors/vue.js";
@@ -8,6 +7,7 @@ import { blockUiFeature } from "../features/block-ui/vue.js";
8
7
  import { collabFeature } from "../features/collab/vue.js";
9
8
  import { commentsFeature } from "../features/comments/vue.js";
10
9
  import { compositeFeature } from "../features/composites/vue.js";
10
+ import { mediaFeature } from "../features/media/vue.js";
11
11
  import { themesFeature } from "../features/themes/vue.js";
12
12
  import { pruefungFeature } from "../features/pruefung/vue.js";
13
13
  import { exploreFeature } from "../features/explore/vue.js";
package/dist/vue/index.js CHANGED
@@ -11,8 +11,8 @@ import { vueSuggestionRenderer } from "./suggestion-renderer.js";
11
11
  import _sfc_main$2 from "./components/EditorSettingsPanel.js";
12
12
  import _sfc_main$1 from "./components/EditorSettingsModal.js";
13
13
  import _sfc_main$3 from "./components/EditorSurface.js";
14
- import { useNotifier } from "./use-notifier.js";
15
14
  import _sfc_main from "./components/EditorCommandPalette.js";
15
+ import { useNotifier } from "./use-notifier.js";
16
16
  import _sfc_main$5 from "./components/SettingsSection.js";
17
17
  import _sfc_main$4 from "./components/EditorToolbar.js";
18
18
  export { EDITOR_CONTEXT, EDITOR_UI, _sfc_main as EditorCommandPalette, _sfc_main$1 as EditorSettingsModal, _sfc_main$2 as EditorSettingsPanel, _sfc_main$3 as EditorSurface, _sfc_main$4 as EditorToolbar, _sfc_main$5 as SettingsSection, _sfc_main$6 as SuggestionList, VUE_UI_KIT, attachNotifier, createEditor, createQueuedNotifier, storeFromRef, trackedStore, useEditorContext, useEditorI18n, useEditorSettings, useEditorUi, useNotifier, useStoreRef, useUiKit, useWritableStoreRef, vueSuggestionRenderer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skaile/forge-editor",
3
- "version": "6.4.0",
3
+ "version": "6.4.1",
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",