@ecsplico/editor 3.1.1 → 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 +42 -0
- package/dist/adapters/direct-ai.d.ts +36 -0
- package/dist/adapters/direct-ai.js +169 -0
- package/dist/adapters/direct-ai.js.map +1 -0
- package/dist/adapters/memory.js.map +1 -1
- package/dist/code/CodeSurface.vue.d.ts +1 -1
- package/dist/core/block-drag.js +3 -0
- package/dist/core/block-drag.js.map +1 -1
- package/dist/core/block-transfer.d.ts +44 -0
- package/dist/core/block-transfer.js +68 -0
- package/dist/core/block-transfer.js.map +1 -0
- package/dist/core/commands.js +5 -0
- package/dist/core/commands.js.map +1 -1
- package/dist/core/core-feature.d.ts +4 -1
- package/dist/core/core-feature.js +8 -3
- package/dist/core/core-feature.js.map +1 -1
- package/dist/core/editor-ui.d.ts +3 -1
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/extension-points.d.ts +17 -1
- package/dist/core/feature.d.ts +6 -1
- package/dist/core/feature.js.map +1 -1
- package/dist/core/index.d.ts +4 -2
- package/dist/core/index.js +3 -2
- package/dist/core/schema/index.d.ts +24 -3
- package/dist/core/schema/index.js +21 -4
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/surface-session.js +3 -1
- package/dist/core/surface-session.js.map +1 -1
- package/dist/core/toolbar-items.js +1 -1
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +1 -1
- package/dist/features/ai/agent-op-review.js +1 -1
- package/dist/features/ai/agent-ops.js +2 -2
- package/dist/features/ai/ai-apply.js +1 -1
- package/dist/features/ai/assistant.js +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +2 -2
- package/dist/features/ai/components/AiSidebar.js +2 -2
- package/dist/features/ai/components/SettingsAiTab.js +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/document-chat-model.js +1 -1
- package/dist/features/ai/document-chat.js +2 -2
- package/dist/features/ai/ghost-autocomplete.js +1 -1
- package/dist/features/ai/index.d.ts +4 -3
- package/dist/features/ai/index.js +6 -5
- package/dist/features/ai/protocol/complete.d.ts +19 -0
- package/dist/features/ai/protocol/complete.js +27 -1
- package/dist/features/ai/protocol/complete.js.map +1 -1
- package/dist/features/ai/protocol/generate.d.ts +95 -0
- package/dist/features/ai/protocol/generate.js +58 -0
- package/dist/features/ai/protocol/generate.js.map +1 -0
- package/dist/features/ai/protocol/index.d.ts +4 -3
- package/dist/features/ai/protocol/index.js +6 -5
- package/dist/features/ai/protocol/tasks.d.ts +3 -1
- package/dist/features/ai/protocol/tasks.js +74 -2
- package/dist/features/ai/protocol/tasks.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js +2 -2
- package/dist/features/ai/react/AiSidebar.js +2 -2
- package/dist/features/ai/react/SettingsAiTab.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/selection-flow.js +1 -1
- package/dist/features/ai/sidebar-flow.js +1 -1
- package/dist/features/block-ui/block-actions.d.ts +19 -3
- package/dist/features/block-ui/block-actions.js +49 -3
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/components/BlockActionsOverlay.js +24 -13
- package/dist/features/block-ui/components/BlockActionsOverlay.js.map +1 -1
- package/dist/features/block-ui/components/BlockActionsOverlay.vue.d.ts +12 -5
- package/dist/features/block-ui/components/BlockUi.js +52 -20
- package/dist/features/block-ui/components/BlockUi.js.map +1 -1
- package/dist/features/block-ui/components/BlockUi.vue.d.ts +6 -4
- package/dist/features/block-ui/feature.d.ts +8 -2
- package/dist/features/block-ui/feature.js +7 -2
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/index.d.ts +3 -2
- package/dist/features/block-ui/index.js +2 -2
- package/dist/features/block-ui/react/BlockActionsOverlay.d.ts +14 -7
- package/dist/features/block-ui/react/BlockActionsOverlay.js +25 -13
- package/dist/features/block-ui/react/BlockActionsOverlay.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.d.ts +6 -4
- package/dist/features/block-ui/react/BlockUi.js +43 -15
- package/dist/features/block-ui/react/BlockUi.js.map +1 -1
- package/dist/features/block-ui/react.d.ts +4 -2
- package/dist/features/block-ui/react.js +3 -3
- package/dist/features/block-ui/react.js.map +1 -1
- package/dist/features/block-ui/vue.d.ts +4 -2
- package/dist/features/block-ui/vue.js +3 -3
- package/dist/features/block-ui/vue.js.map +1 -1
- package/dist/features/composites/baskets.d.ts +27 -0
- package/dist/features/composites/baskets.js +46 -0
- package/dist/features/composites/baskets.js.map +1 -0
- package/dist/features/composites/components/CompositeBasketMenu.js +97 -0
- package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -0
- package/dist/features/composites/components/CompositeChrome.js +79 -23
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/components/CompositeView.js +21 -2
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +12 -3
- package/dist/features/composites/components/DerivationBadge.js.map +1 -1
- package/dist/features/composites/components/FocusChip.js +53 -0
- package/dist/features/composites/components/FocusChip.js.map +1 -0
- package/dist/features/composites/components/FocusChip.vue.d.ts +14 -0
- package/dist/features/composites/components/SettingsEditingTab.js +40 -4
- package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/derivation.d.ts +2 -1
- package/dist/features/composites/derivation.js +5 -1
- package/dist/features/composites/derivation.js.map +1 -1
- package/dist/features/composites/derived-chrome.js +13 -3
- package/dist/features/composites/derived-chrome.js.map +1 -1
- package/dist/features/composites/feature.d.ts +7 -1
- package/dist/features/composites/feature.js +107 -23
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/focus.d.ts +38 -0
- package/dist/features/composites/focus.js +86 -0
- package/dist/features/composites/focus.js.map +1 -0
- package/dist/features/composites/index.d.ts +7 -3
- package/dist/features/composites/index.js +7 -3
- 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/meta-keys.d.ts +4 -0
- package/dist/features/composites/meta-keys.js +8 -4
- package/dist/features/composites/meta-keys.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +15 -2
- package/dist/features/composites/nodes.js +61 -5
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/outline.d.ts +4 -0
- package/dist/features/composites/outline.js +3 -0
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/react/CompositeBasketMenu.js +88 -0
- package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -0
- package/dist/features/composites/react/CompositeChrome.js +52 -4
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeView.js +15 -2
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +10 -1
- package/dist/features/composites/react/DerivationBadge.js.map +1 -1
- package/dist/features/composites/react/FocusChip.d.ts +11 -0
- package/dist/features/composites/react/FocusChip.js +50 -0
- package/dist/features/composites/react/FocusChip.js.map +1 -0
- package/dist/features/composites/react/SettingsEditingTab.js +36 -1
- package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/react.d.ts +2 -1
- package/dist/features/composites/react.js +3 -1
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/schema.d.ts +5 -3
- package/dist/features/composites/schema.js +8 -4
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/split.d.ts +62 -0
- package/dist/features/composites/split.js +172 -0
- package/dist/features/composites/split.js.map +1 -0
- package/dist/features/composites/vue.d.ts +4 -3
- package/dist/features/composites/vue.js +7 -5
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/transfer/components/TransferMenu.js +93 -0
- package/dist/features/transfer/components/TransferMenu.js.map +1 -0
- package/dist/features/transfer/components/TransferMenu.vue.d.ts +15 -0
- package/dist/features/transfer/feature.d.ts +76 -0
- package/dist/features/transfer/feature.js +169 -0
- package/dist/features/transfer/feature.js.map +1 -0
- package/dist/features/transfer/index.d.ts +3 -0
- package/dist/features/transfer/index.js +3 -0
- package/dist/features/transfer/react/TransferMenu.d.ts +12 -0
- package/dist/features/transfer/react/TransferMenu.js +71 -0
- package/dist/features/transfer/react/TransferMenu.js.map +1 -0
- package/dist/features/transfer/react.d.ts +10 -0
- package/dist/features/transfer/react.js +11 -0
- package/dist/features/transfer/react.js.map +1 -0
- package/dist/features/transfer/transforms.d.ts +83 -0
- package/dist/features/transfer/transforms.js +137 -0
- package/dist/features/transfer/transforms.js.map +1 -0
- package/dist/features/transfer/vue.d.ts +9 -0
- package/dist/features/transfer/vue.js +11 -0
- package/dist/features/transfer/vue.js.map +1 -0
- package/dist/i18n/de/ai.js +4 -0
- package/dist/i18n/de/ai.js.map +1 -1
- package/dist/i18n/de/composites.js +21 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/integrations.js +16 -0
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/transfer.js +17 -0
- package/dist/i18n/de/transfer.js.map +1 -0
- package/dist/i18n/de/tree.js +7 -0
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +57 -0
- package/dist/i18n/de.js +3 -1
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en/ai.js +4 -0
- package/dist/i18n/en/ai.js.map +1 -1
- package/dist/i18n/en/composites.js +21 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/integrations.js +16 -0
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/transfer.js +17 -0
- package/dist/i18n/en/transfer.js.map +1 -0
- package/dist/i18n/en/tree.js +7 -0
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/i18n/en.js +3 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/integrations/composite-ai/composite-fill.js +1 -1
- package/dist/integrations/composite-ai/composite-summary.js +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +21 -0
- package/dist/integrations/composite-ai/derive.js +69 -47
- package/dist/integrations/composite-ai/derive.js.map +1 -1
- package/dist/integrations/composite-ai/index.d.ts +8 -3
- package/dist/integrations/composite-ai/index.js +50 -4
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.d.ts +48 -0
- package/dist/integrations/composite-ai/outline-sanitize.js +175 -0
- package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -0
- package/dist/integrations/composite-ai/split.d.ts +44 -0
- package/dist/integrations/composite-ai/split.js +187 -0
- package/dist/integrations/composite-ai/split.js.map +1 -0
- package/dist/integrations/composite-ai/transfer-transforms.d.ts +27 -0
- package/dist/integrations/composite-ai/transfer-transforms.js +153 -0
- package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -0
- package/dist/integrations/transcript-ai/protocol.d.ts +13 -0
- package/dist/integrations/transcript-ai/protocol.js +49 -1
- package/dist/integrations/transcript-ai/protocol.js.map +1 -1
- package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +1 -1
- package/dist/nuxt/module.d.ts +114 -0
- package/dist/presets/compose.d.ts +24 -8
- package/dist/presets/compose.js +117 -58
- package/dist/presets/compose.js.map +1 -1
- package/dist/react/compose.js +5 -1
- package/dist/react/compose.js.map +1 -1
- package/dist/react/composed-surface.d.ts +9 -0
- package/dist/react/composed-surface.js +4 -1
- package/dist/react/composed-surface.js.map +1 -1
- package/dist/server/ai.d.ts +2 -5
- package/dist/server/ai.js.map +1 -1
- package/dist/server/handlers/ai.d.ts +4 -75
- package/dist/server/handlers/ai.js +30 -94
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +1 -0
- package/dist/server/handlers/index.d.ts +3 -1
- package/dist/server/handlers/index.js +3 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/settings/index.d.ts +8 -0
- package/dist/settings/index.js +16 -4
- package/dist/settings/index.js.map +1 -1
- package/dist/styles/composites.css +41 -0
- package/dist/tree/DocumentTree.js +246 -80
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/drop-rules.js +8 -0
- package/dist/tree/drop-rules.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +164 -32
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +41 -2
- package/dist/tree/tree-host.js.map +1 -1
- package/dist/tree/tree-items.js +53 -5
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +122 -10
- package/dist/tree/tree-model.js.map +1 -1
- package/dist/tree/tree-rows.js +20 -7
- package/dist/tree/tree-rows.js.map +1 -1
- package/dist/tree/tree-view-model.d.ts +5 -0
- package/dist/tree/tree-view-model.js +13 -0
- package/dist/tree/tree-view-model.js.map +1 -1
- package/dist/vue/AdvancedEditor.js +6 -0
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +9 -1
- package/dist/vue/compose.js +5 -1
- package/dist/vue/compose.js.map +1 -1
- package/package.json +21 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodes.js","names":[],"sources":["../../../src/features/composites/nodes.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type JSONContent, mergeAttributes, Node } from '@tiptap/core'\nimport type { Node as PMNode, ResolvedPos } from '@tiptap/pm/model'\nimport { type EditorState, Selection, TextSelection } from '@tiptap/pm/state'\nimport { editorEntryOf } from '../../core/editor-ui'\nimport { createStore, type Store } from '../../core/store'\nimport { ABILITY, isChromeNode, TEXT_ABILITIES } from '../../core/schema'\nimport type { HeadingConversionScope } from '../../settings'\nimport { compositeFence, withReferencedFenceIds } from './fences'\nimport { derivationOf } from './derivation'\nimport { flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange } from './heading-composites'\nimport { type CompositeKind, compositeKind, defaultCompositeKinds, fallbackCompositeKinds } from './kinds'\nimport { keptMeta } from './meta-keys'\n\n/** Options of the `composite` node: the kinds its `kind` attribute selects from. */\nexport interface CompositeOptions {\n kinds: readonly CompositeKind[]\n}\n\n/** Storage of the `composite` node: mirrors `options.kinds` for code that only has the editor. */\nexport interface CompositeStorage {\n kinds: readonly CompositeKind[]\n /** Ids of the derived Bausteine (those with a `meta.derivation`) the user opened; every other derived Baustein shows collapsed. */\n expanded: Store<ReadonlySet<string>>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n composite: {\n /** Insert an empty composite of `kind` after the caret's block (replacing an empty paragraph; at the body start from the composite chrome), caret in its title. */\n insertComposite: (kind?: string) => ReturnType\n /** Wrap the selected blocks into a new composite of `kind`, caret in its title. */\n wrapInComposite: (kind?: string) => ReturnType\n /** Leave the caret's innermost composite: new paragraph right after it (a trailing empty body paragraph is moved there). */\n exitComposite: () => ReturnType\n /** Group headings in the selected blocks (or, without a selection, in `scope`) into composites of `kind`; `recursive` nests every heading level. False when the range holds no heading. */\n convertHeadingsToComposites: (options: { recursive: boolean, scope: HeadingConversionScope, kind?: string }) => ReturnType\n /** Turn composites among the selected blocks (or, without a selection, in `scope`) into headings of their nesting level, with description, tags and meta as text lines; `recursive` flattens every level. False when the range holds no composite. */\n convertCompositesToHeadings: (options: { recursive: boolean, scope: HeadingConversionScope }) => ReturnType\n /** Switch the composite at `pos` (default: the caret's innermost composite) to `kind`; `meta` keeps the target kind's fields plus `derivation` and `bib`. */\n setCompositeKind: (kind: string, pos?: number) => ReturnType\n /** Open the derived Baustein with block id `id` if it is collapsed, collapse it if it is open. */\n toggleCompositeExpanded: (id: string) => ReturnType\n }\n }\n\n interface Storage {\n composite: CompositeStorage\n }\n}\n\nconst COMPOSITE = 'composite'\nconst TITLE = 'compositeTitle'\nconst DESCRIPTION = 'compositeDescription'\nconst TAGS = 'compositeTags'\n/** Kind of a composite created without an explicit one (also the attribute default). */\nconst DEFAULT_KIND = 'section'\n/** Title, description and tags: the fixed children in front of a composite's body. */\nexport const COMPOSITE_CHROME_COUNT = 3\nconst CHROME_COUNT = COMPOSITE_CHROME_COUNT\n\n/**\n * Composite kinds `editor` was configured with. An editor without the composite node gets the built-in kinds in the\n * language of its surface (the default language without one).\n */\nexport function compositeKindsOf(editor: Editor): readonly CompositeKind[] {\n const configured: readonly CompositeKind[] | undefined = editor.storage.composite?.kinds\n if (configured) return configured\n const entry = editorEntryOf(editor)\n return entry ? defaultCompositeKinds(entry.ctx.t) : fallbackCompositeKinds()\n}\n\n/** JSON of a fresh composite of `kind` (empty title/description/tags, hidden meta) with `body`. */\nexport function compositeJSON(kind: string, body: JSONContent[] = [{ type: 'paragraph' }]): JSONContent {\n return { type: COMPOSITE, attrs: { kind }, content: [{ type: TITLE }, { type: DESCRIPTION }, { type: TAGS }, ...body] }\n}\n\n/** Innermost composite around `$pos`: its depth, position and node. */\nfunction compositeAround($pos: ResolvedPos): { depth: number, pos: number, node: PMNode } | null {\n for (let depth = $pos.depth; depth > 0; depth--) {\n const node = $pos.node(depth)\n if (node.type.name === COMPOSITE) return { depth, pos: $pos.before(depth), node }\n }\n return null\n}\n\n/** The composite starting at `pos`, or without `pos` the caret's innermost composite. */\nfunction compositeAt(state: EditorState, pos: number | undefined): { pos: number, node: PMNode } | null {\n if (pos === undefined) return compositeAround(state.selection.$head)\n const node = state.doc.nodeAt(pos)\n return node?.type.name === COMPOSITE ? { pos, node } : null\n}\n\n/** Position right in front of the body (= after the tags row) of the composite whose content starts at `start`. */\nfunction bodyStart(composite: PMNode, start: number): number {\n let pos = start\n for (let i = 0; i < CHROME_COUNT; i++) pos += composite.child(i).nodeSize\n return pos\n}\n\nconst isEmptyParagraph = (node: PMNode) => node.type.name === 'paragraph' && node.content.size === 0\n\n/** The caret's block is an empty paragraph, the last child of a composite whose body has more than this one block. */\nfunction inTrailingEmptyParagraph($head: ResolvedPos): boolean {\n if ($head.depth < 2 || !isEmptyParagraph($head.parent)) return false\n const composite = $head.node($head.depth - 1)\n return composite.type.name === COMPOSITE\n && $head.index($head.depth - 1) === composite.childCount - 1\n && composite.childCount > CHROME_COUNT + 1\n}\n\n/** Nothing typed yet: empty title and description, no tags, body is one empty paragraph. */\nfunction isEmptyComposite(composite: PMNode): boolean {\n const tags: unknown = composite.child(2).attrs.tags\n return composite.child(0).content.size === 0\n && composite.child(1).content.size === 0\n && (!Array.isArray(tags) || tags.length === 0)\n && composite.childCount === CHROME_COUNT + 1\n && isEmptyParagraph(composite.child(CHROME_COUNT))\n}\n\n/** Child range of `parent` a structure conversion works on: the selected blocks (composite chrome excluded), or without a selection the `scope` — for `'heading'` the caret's heading group (`'group'`) or innermost composite (`'flatten'`). */\nfunction structureRange(state: EditorState, scope: HeadingConversionScope, direction: 'group' | 'flatten'): { parent: PMNode, parentDepth: number, parentStart: number, fromIndex: number, toIndex: number } | null {\n const { $from, $to, $head, empty } = state.selection\n if (!empty) {\n const range = $from.blockRange($to, node => node.type.name === 'doc' || node.type.name === COMPOSITE)\n if (!range) return null\n const parent = range.parent\n // The composite chrome (title, description, tags) is never part of the converted blocks.\n const fromIndex = Math.max(range.startIndex, parent.type.name === COMPOSITE ? CHROME_COUNT : 0)\n const toIndex = range.endIndex\n if (fromIndex >= toIndex) return null\n return { parent, parentDepth: range.depth, parentStart: range.$from.start(range.depth), fromIndex, toIndex }\n }\n if (scope === 'container') {\n const found = compositeAround($head)\n if (found) return { parent: found.node, parentDepth: found.depth, parentStart: $head.start(found.depth), fromIndex: CHROME_COUNT, toIndex: found.node.childCount }\n return { parent: state.doc, parentDepth: 0, parentStart: 0, fromIndex: 0, toIndex: state.doc.childCount }\n }\n if (scope === 'heading') {\n if (direction === 'flatten') {\n const found = compositeAround($head)\n if (!found) return null\n const depth = found.depth - 1\n const fromIndex = $head.index(depth)\n return { parent: $head.node(depth), parentDepth: depth, parentStart: $head.start(depth), fromIndex, toIndex: fromIndex + 1 }\n }\n let depth = $head.depth\n while (depth > 0 && !['doc', COMPOSITE].includes($head.node(depth - 1).type.name)) depth--\n if (depth === 0) return null\n const parent = $head.node(depth - 1)\n const minIndex = parent.type.name === COMPOSITE ? CHROME_COUNT : 0\n let index = $head.index(depth - 1)\n while (index >= minIndex && parent.child(index).type.name !== 'heading') index--\n if (index < minIndex) return null\n const group = headingGroupRange(parent, index)\n return { parent, parentDepth: depth - 1, parentStart: $head.start(depth - 1), fromIndex: group.from, toIndex: group.to }\n }\n return { parent: state.doc, parentDepth: 0, parentStart: 0, fromIndex: 0, toIndex: state.doc.childCount }\n}\n\n/** Children `fromIndex..toIndex` of `parent` (content starting at `parentStart`) and their document range. */\nfunction rangeBlocks(parent: PMNode, parentStart: number, fromIndex: number, toIndex: number): { blocks: PMNode[], from: number, to: number } {\n const blocks: PMNode[] = []\n let from = parentStart\n let to = parentStart\n parent.forEach((child, _offset, index) => {\n if (index < fromIndex) from += child.nodeSize\n else if (index < toIndex) blocks.push(child)\n if (index < toIndex) to += child.nodeSize\n })\n return { blocks, from, to }\n}\n\n// Revoked inside title and description: they are single lines of text, not containers for blocks.\nconst CHROME_REVOKES = [\n ABILITY.headings, ABILITY.lists, ABILITY.blockquote, ABILITY.codeBlock, ABILITY.rule, ABILITY.composite, ABILITY.transcript,\n ABILITY.media, ABILITY.record, ABILITY.aiGenerate, ABILITY.blockDuplicate,\n]\n\n// Schema-only nodes (no node views): shared by the client editor and the server-side Yjs schema.\n// The client adds the node view of `composite` in `node-views.ts`; `compositeTags` needs none (a hidden data row).\nexport const CompositeNode = Node.create<CompositeOptions, CompositeStorage>({\n name: COMPOSITE,\n // Above StarterKit's default (100) so this Enter / Backspace / Delete keymap runs first,\n // below Paragraph (1000) so the paragraph stays the schema's default block.\n priority: 101,\n group: 'block',\n content: `${TITLE} ${DESCRIPTION} ${TAGS} block+`,\n isolating: true,\n defining: true,\n editorBlockContainer: true,\n abilities: { own: [ABILITY.compositeMeta, ABILITY.compositeSummarize, ABILITY.compositeExport, ABILITY.compositeDerive] },\n addOptions: () => ({ kinds: fallbackCompositeKinds() }),\n addStorage() {\n return { kinds: this.options.kinds, expanded: createStore<ReadonlySet<string>>(new Set()) }\n },\n // A caret inside the body of a collapsed derived Baustein (also inside nested ones) opens it: typing into hidden text would be blind.\n onSelectionUpdate() {\n const { expanded } = this.storage\n const { $head } = this.editor.state.selection\n let open: Set<string> | null = null\n for (let depth = $head.depth; depth > 0; depth--) {\n const node = $head.node(depth)\n const id: unknown = node.attrs.id\n if (node.type.name !== COMPOSITE || typeof id !== 'string' || !id || $head.index(depth) < CHROME_COUNT) continue\n if (derivationOf(node) && !expanded.get().has(id)) (open ??= new Set(expanded.get())).add(id)\n }\n if (open) expanded.set(open)\n },\n // Block ids reach the Markdown only for referenced Bausteine (see `fences.ts`): `DocumentMarkdown` runs this on the\n // document JSON before serializing, it drops the ids of unreferenced composites, `renderMarkdown` writes the ones left.\n markdownPrepare: (json, editor) => withReferencedFenceIds(json, compositeKindsOf(editor)),\n addAttributes: () => ({\n kind: {\n default: DEFAULT_KIND,\n // `null` (no `data-kind`, e.g. legacy markup) falls back to the default kind.\n parseHTML: el => el.getAttribute('data-kind') || null,\n renderHTML: attrs => ({ 'data-kind': attrs.kind }),\n },\n meta: {\n default: {},\n parseHTML: (el) => {\n try {\n const value: unknown = JSON.parse(el.getAttribute('data-meta') ?? '{}')\n return value && typeof value === 'object' && !Array.isArray(value) ? value : {}\n }\n catch {\n return {}\n }\n },\n renderHTML: attrs => ({ 'data-meta': JSON.stringify(attrs.meta) }),\n },\n }),\n parseHTML: () => [\n { tag: 'section[data-type=\"composite\"]' },\n // Legacy markup of the block the composite family replaced: no `data-kind`, so it parses as kind `section`.\n { tag: 'section[data-type=\"section\"]' },\n ],\n renderHTML: ({ HTMLAttributes }) => ['section', mergeAttributes(HTMLAttributes, { 'data-type': COMPOSITE }), 0],\n // Fenced: `::: <kind> {meta JSON, only when non-empty}`, then title, description, tags and body blocks as separate\n // Markdown blocks (empty description/tags render nothing), then `:::`. Same child iteration as the blockquote's\n // renderMarkdown (keeps list numbering etc. per sibling index).\n renderMarkdown: (node, h) => {\n const kind = typeof node.attrs?.kind === 'string' && node.attrs.kind ? node.attrs.kind : DEFAULT_KIND\n const meta: unknown = node.attrs?.meta\n const body = (node.content ?? [])\n .map((child, index) => h.renderChild?.(child, index) ?? h.renderChildren([child]))\n .filter(Boolean)\n .join('\\n\\n')\n const id: unknown = node.attrs?.id\n return compositeFence(kind, meta && typeof meta === 'object' ? meta as Record<string, unknown> : {}, body, typeof id === 'string' && id ? id : undefined)\n },\n\n addCommands() {\n return {\n toggleCompositeExpanded: id => () => {\n const { expanded } = this.storage\n const next = new Set(expanded.get())\n if (!next.delete(id)) next.add(id)\n expanded.set(next)\n return true\n },\n\n insertComposite: (kind = DEFAULT_KIND) => ({ state, tr, dispatch }) => {\n const { $head } = state.selection\n let depth = $head.depth\n while (depth > 0 && !['doc', COMPOSITE].includes($head.node(depth - 1).type.name)) depth--\n let from = $head.pos\n let to = from\n if (depth > 0) {\n const block = $head.node(depth)\n if (isChromeNode(block)) from = to = bodyStart($head.node(depth - 1), $head.start(depth - 1))\n else if (isEmptyParagraph(block)) {\n from = $head.before(depth)\n to = $head.after(depth)\n }\n else from = to = $head.after(depth)\n }\n const $from = state.doc.resolve(from)\n if (!$from.parent.canReplaceWith($from.index(), state.doc.resolve(to).index(), this.type)) return false\n if (dispatch) {\n const json = compositeJSON(kind)\n tr.replaceWith(from, to, state.schema.nodeFromJSON(json))\n tr.setSelection(TextSelection.create(tr.doc, from + 2)).scrollIntoView()\n }\n return true\n },\n\n wrapInComposite: (kind = DEFAULT_KIND) => ({ state, tr, dispatch }) => {\n const { $from, $to } = state.selection\n const range = $from.blockRange($to, node => node.type.name === 'doc' || node.type.name === COMPOSITE)\n if (!range) return false\n if (range.parent.type.name === COMPOSITE && range.startIndex < CHROME_COUNT) return false\n if (!range.parent.canReplaceWith(range.startIndex, range.endIndex, this.type)) return false\n if (dispatch) {\n const { nodes } = state.schema\n const composite = this.type.create({ kind }, [\n nodes[TITLE]!.create(), nodes[DESCRIPTION]!.create(), nodes[TAGS]!.create(),\n ...state.doc.slice(range.start, range.end).content.content,\n ])\n tr.replaceWith(range.start, range.end, composite)\n tr.setSelection(TextSelection.create(tr.doc, range.start + 2)).scrollIntoView()\n }\n return true\n },\n\n setCompositeKind: (kind, pos) => ({ state, tr, dispatch }) => {\n const found = compositeAt(state, pos)\n if (!found) return false\n if (dispatch) {\n const meta = keptMeta(compositeKind(this.options.kinds, kind), found.node.attrs.meta as Record<string, unknown>)\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, kind, meta })\n }\n return true\n },\n\n exitComposite: () => ({ state, tr, dispatch }) => {\n const { $head } = state.selection\n const composite = compositeAround($head)\n if (!composite) return false\n if (dispatch) {\n // Leaving from a trailing empty paragraph moves it out instead of leaving an empty line behind.\n if ($head.depth === composite.depth + 1 && inTrailingEmptyParagraph($head)) tr.delete($head.before(), $head.after())\n const after = tr.mapping.map($head.after(composite.depth))\n tr.insert(after, state.schema.nodes.paragraph!.create())\n tr.setSelection(TextSelection.create(tr.doc, after + 1)).scrollIntoView()\n }\n return true\n },\n\n convertHeadingsToComposites: ({ recursive, scope, kind = DEFAULT_KIND }) => ({ state, tr, dispatch }) => {\n const range = structureRange(state, scope, 'group')\n if (!range) return false\n const { blocks, from, to } = rangeBlocks(range.parent, range.parentStart, range.fromIndex, range.toIndex)\n const result = groupHeadingsIntoComposites(state.schema, blocks, recursive, kind)\n if (!result) return false\n if (dispatch) {\n tr.replaceWith(from, to, result)\n tr.setSelection(Selection.near(tr.doc.resolve(from), 1)).scrollIntoView()\n }\n return true\n },\n\n convertCompositesToHeadings: ({ recursive, scope }) => ({ state, tr, dispatch }) => {\n const range = structureRange(state, scope, 'flatten')\n if (!range) return false\n const $pos = state.selection.empty ? state.selection.$head : state.selection.$from\n let level = 1\n for (let depth = 1; depth <= range.parentDepth; depth++) {\n if ($pos.node(depth).type.name === COMPOSITE) level++\n }\n const { blocks, from, to } = rangeBlocks(range.parent, range.parentStart, range.fromIndex, range.toIndex)\n const result = flattenCompositesToHeadings(state.schema, blocks, recursive, level, this.options.kinds)\n if (!result) return false\n if (dispatch) {\n tr.replaceWith(from, to, result)\n tr.setSelection(Selection.near(tr.doc.resolve(from), 1)).scrollIntoView()\n }\n return true\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n const { $head } = this.editor.state.selection\n const type = $head.parent.type.name\n if (type === TITLE || type === DESCRIPTION) {\n const depth = $head.depth - 1\n const composite = $head.node(depth)\n const start = $head.start(depth)\n return this.editor.commands.command(({ tr }) => {\n const selection = Selection.findFrom(tr.doc.resolve(bodyStart(composite, start)), 1)\n if (!selection) return false\n tr.setSelection(selection).scrollIntoView()\n return true\n })\n }\n if (this.editor.state.selection.empty && inTrailingEmptyParagraph($head)) return this.editor.commands.exitComposite()\n return false\n },\n\n Backspace: () => {\n const { selection } = this.editor.state\n const { $head } = selection\n if (!selection.empty || !$head.parent.isTextblock || $head.parentOffset !== 0 || $head.depth < 2) return false\n const depth = $head.depth - 1\n const composite = $head.node(depth)\n if (composite.type.name !== COMPOSITE) return false\n const start = $head.start(depth)\n const titleEnd = start + composite.child(0).nodeSize - 1\n const type = $head.parent.type.name\n\n if (type === DESCRIPTION) {\n return this.editor.commands.command(({ tr }) => {\n tr.setSelection(TextSelection.create(tr.doc, titleEnd))\n return true\n })\n }\n if (type === TITLE) {\n // Never joins anything into the title; an untouched composite turns back into a paragraph.\n if (!isEmptyComposite(composite)) return true\n return this.editor.commands.command(({ tr, state }) => {\n const from = $head.before(depth)\n tr.replaceWith(from, $head.after(depth), state.schema.nodes.paragraph!.create())\n tr.setSelection(TextSelection.create(tr.doc, from + 1))\n return true\n })\n }\n // First body block: ProseMirror's joinBackward would delete the tags row (an atom) — move into the chrome instead.\n if ($head.index(depth) !== CHROME_COUNT) return false\n return this.editor.commands.command(({ tr }) => {\n if (isEmptyParagraph($head.parent) && composite.childCount > CHROME_COUNT + 1) tr.delete($head.before(), $head.after())\n tr.setSelection(TextSelection.create(tr.doc, titleEnd))\n return true\n })\n },\n\n // No join of the following block into title or description.\n Delete: () => {\n const { selection } = this.editor.state\n const { $head } = selection\n const type = $head.parent.type.name\n return selection.empty && (type === TITLE || type === DESCRIPTION) && $head.parentOffset === $head.parent.content.size\n },\n\n 'Mod-Shift-a': () => this.editor.commands.insertComposite(DEFAULT_KIND),\n }\n },\n})\n\nexport const CompositeTitleNode = Node.create({\n name: TITLE,\n // Above StarterKit's hard break (100), which also binds Mod-Enter.\n priority: 101,\n content: 'inline*',\n defining: true,\n editorChrome: true,\n abilities: { revokes: CHROME_REVOKES },\n // Above the heading's `h2` rule; the second rule reads legacy markup.\n parseHTML: () => [\n { tag: 'h2[data-type=\"composite-title\"]', priority: 60 },\n { tag: 'h2[data-type=\"section-title\"]', priority: 60 },\n ],\n renderHTML: ({ HTMLAttributes }) => ['h2', mergeAttributes(HTMLAttributes, { 'data-type': 'composite-title', class: 'composite-title' }), 0],\n renderMarkdown: (node, h) => `## ${h.renderChildren(node.content ?? [])}`,\n addKeyboardShortcuts() {\n return {\n // Here rather than on CompositeNode: the ability gate swallows every Mod-/Alt- key of the `composite` type where\n // `format.composite` is missing (title, code blocks, speaker segments), but leaving a composite must work everywhere.\n 'Mod-Enter': () => this.editor.commands.exitComposite(),\n }\n },\n})\n\nexport const CompositeDescriptionNode = Node.create({\n name: DESCRIPTION,\n content: 'inline*',\n defining: true,\n editorChrome: true,\n abilities: { revokes: CHROME_REVOKES },\n // Above the paragraph's `p` rule; the second rule reads legacy markup.\n parseHTML: () => [\n { tag: 'p[data-type=\"composite-description\"]', priority: 60 },\n { tag: 'p[data-type=\"section-summary\"]', priority: 60 },\n ],\n renderHTML: ({ HTMLAttributes }) => ['p', mergeAttributes(HTMLAttributes, { 'data-type': 'composite-description', class: 'composite-description' }), 0],\n renderMarkdown: (node, h) => {\n const text = h.renderChildren(node.content ?? [])\n return text ? `> ${text}` : ''\n },\n})\n\nexport const CompositeTagsNode = Node.create({\n name: TAGS,\n atom: true,\n selectable: false,\n draggable: false,\n allowGapCursor: false,\n editorChrome: true,\n abilities: { revokes: [...TEXT_ABILITIES, ABILITY.blockDelete, ABILITY.blockDuplicate] },\n addAttributes: () => ({\n tags: {\n default: [],\n parseHTML: (el) => {\n try {\n const value: unknown = JSON.parse(el.getAttribute('data-tags') ?? '[]')\n return Array.isArray(value) ? value.filter(tag => typeof tag === 'string') : []\n }\n catch {\n return []\n }\n },\n renderHTML: attrs => ({ 'data-tags': JSON.stringify(attrs.tags) }),\n },\n }),\n // The second rule reads legacy markup.\n parseHTML: () => [{ tag: 'div[data-type=\"composite-tags\"]' }, { tag: 'div[data-type=\"section-tags\"]' }],\n renderHTML: ({ HTMLAttributes }) => ['div', mergeAttributes(HTMLAttributes, { 'data-type': 'composite-tags', class: 'composite-tags' })],\n renderMarkdown: (node) => {\n const tags: unknown = node.attrs?.tags\n return Array.isArray(tags) && tags.length ? `Tags: ${tags.map(tag => `#${tag}`).join(' ')}` : ''\n },\n})\n\nexport const CompositeNodes = [CompositeNode, CompositeTitleNode, CompositeDescriptionNode, CompositeTagsNode]\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nodes.js","names":[],"sources":["../../../src/features/composites/nodes.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type JSONContent, mergeAttributes, Node } from '@tiptap/core'\nimport type { Node as PMNode, ResolvedPos } from '@tiptap/pm/model'\nimport { type EditorState, Selection, TextSelection } from '@tiptap/pm/state'\nimport { editorEntryOf } from '../../core/editor-ui'\nimport { createStore, type Store } from '../../core/store'\nimport { ABILITY, isChromeNode, TEXT_ABILITIES } from '../../core/schema'\nimport type { HeadingConversionScope } from '../../settings'\nimport { compositeFence, withReferencedFenceIds } from './fences'\nimport { type Basket, basketById, defaultBaskets, fallbackBaskets, titleHiddenOf } from './baskets'\nimport { derivationOf } from './derivation'\nimport { flattenCompositesToHeadings, groupHeadingsIntoComposites, headingGroupRange } from './heading-composites'\nimport { type CompositeKind, compositeKind, defaultCompositeKinds, fallbackCompositeKinds } from './kinds'\nimport { BASKET_KEY, keptMeta, TITLE_HIDDEN_KEY } from './meta-keys'\n\n/** Options of the `composite` node: the kinds its `kind` attribute selects from and the baskets its `meta.basket` selects from. */\nexport interface CompositeOptions {\n kinds: readonly CompositeKind[]\n baskets: readonly Basket[]\n}\n\n/** Storage of the `composite` node: mirrors `options.kinds` and `options.baskets` for code that only has the editor. */\nexport interface CompositeStorage {\n kinds: readonly CompositeKind[]\n baskets: readonly Basket[]\n /** Ids of the Bausteine with a hidden title (`meta.titleHidden`) whose title the caret entered; they show it for the rest of the editor session. */\n revealedTitles: Store<ReadonlySet<string>>\n /** Ids of the derived Bausteine (those with a `meta.derivation`) the user opened; every other derived Baustein shows collapsed. */\n expanded: Store<ReadonlySet<string>>\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n composite: {\n /** Insert an empty composite of `kind` after the caret's block (replacing an empty paragraph; at the body start from the composite chrome), caret in its title. */\n insertComposite: (kind?: string) => ReturnType\n /** Wrap the selected blocks into a new composite of `kind`, caret in its title. */\n wrapInComposite: (kind?: string) => ReturnType\n /** Leave the caret's innermost composite: new paragraph right after it (a trailing empty body paragraph is moved there). */\n exitComposite: () => ReturnType\n /** Group headings in the selected blocks (or, without a selection, in `scope`) into composites of `kind`; `recursive` nests every heading level. False when the range holds no heading. */\n convertHeadingsToComposites: (options: { recursive: boolean, scope: HeadingConversionScope, kind?: string }) => ReturnType\n /** Turn composites among the selected blocks (or, without a selection, in `scope`) into headings of their nesting level, with description, tags and meta as text lines; `recursive` flattens every level. False when the range holds no composite. */\n convertCompositesToHeadings: (options: { recursive: boolean, scope: HeadingConversionScope }) => ReturnType\n /** Switch the composite at `pos` (default: the caret's innermost composite) to `kind`; `meta` keeps the target kind's fields plus `derivation` and `bib`. */\n setCompositeKind: (kind: string, pos?: number) => ReturnType\n /** Open the derived Baustein with block id `id` if it is collapsed, collapse it if it is open. */\n toggleCompositeExpanded: (id: string) => ReturnType\n /** Put the composite at `pos` (default: the caret's innermost composite) into the basket `basket` (an id of the editor's baskets; false for an unknown id), or take it out with `null`. */\n setCompositeBasket: (basket: string | null, pos?: number) => ReturnType\n /** Hide (`true`) or show (`false`) the title of the composite at `pos` (default: the caret's innermost composite); showing also ends the caret reveal of its title. */\n setCompositeTitleHidden: (hidden: boolean, pos?: number) => ReturnType\n }\n }\n\n interface Storage {\n composite: CompositeStorage\n }\n}\n\nconst COMPOSITE = 'composite'\nconst TITLE = 'compositeTitle'\nconst DESCRIPTION = 'compositeDescription'\nconst TAGS = 'compositeTags'\n/** Kind of a composite created without an explicit one (also the attribute default). */\nconst DEFAULT_KIND = 'section'\n/** Title, description and tags: the fixed children in front of a composite's body. */\nexport const COMPOSITE_CHROME_COUNT = 3\nconst CHROME_COUNT = COMPOSITE_CHROME_COUNT\n\n/**\n * Composite kinds `editor` was configured with. An editor without the composite node gets the built-in kinds in the\n * language of its surface (the default language without one).\n */\nexport function compositeKindsOf(editor: Editor): readonly CompositeKind[] {\n const configured: readonly CompositeKind[] | undefined = editor.storage.composite?.kinds\n if (configured) return configured\n const entry = editorEntryOf(editor)\n return entry ? defaultCompositeKinds(entry.ctx.t) : fallbackCompositeKinds()\n}\n\n/** Baskets `editor` was configured with (the built-in ones in the default language for an editor without the composite node). */\nexport function compositeBasketsOf(editor: Editor): readonly Basket[] {\n const configured: readonly Basket[] | undefined = editor.storage.composite?.baskets\n if (configured) return configured\n const entry = editorEntryOf(editor)\n return entry ? defaultBaskets(entry.ctx.t) : fallbackBaskets()\n}\n\n/** JSON of a fresh composite of `kind` (empty title/description/tags, hidden meta) with `body`. */\nexport function compositeJSON(kind: string, body: JSONContent[] = [{ type: 'paragraph' }]): JSONContent {\n return { type: COMPOSITE, attrs: { kind }, content: [{ type: TITLE }, { type: DESCRIPTION }, { type: TAGS }, ...body] }\n}\n\n/** Innermost composite around `$pos`: its depth, position and node. */\nexport function compositeAround($pos: ResolvedPos): { depth: number, pos: number, node: PMNode } | null {\n for (let depth = $pos.depth; depth > 0; depth--) {\n const node = $pos.node(depth)\n if (node.type.name === COMPOSITE) return { depth, pos: $pos.before(depth), node }\n }\n return null\n}\n\n/** The composite starting at `pos`, or without `pos` the caret's innermost composite. */\nexport function compositeAt(state: EditorState, pos: number | undefined): { pos: number, node: PMNode } | null {\n if (pos === undefined) return compositeAround(state.selection.$head)\n const node = state.doc.nodeAt(pos)\n return node?.type.name === COMPOSITE ? { pos, node } : null\n}\n\n/** Position right in front of the body (= after the tags row) of the composite whose content starts at `start`. */\nfunction bodyStart(composite: PMNode, start: number): number {\n let pos = start\n for (let i = 0; i < CHROME_COUNT; i++) pos += composite.child(i).nodeSize\n return pos\n}\n\nconst isEmptyParagraph = (node: PMNode) => node.type.name === 'paragraph' && node.content.size === 0\n\n/** The caret's block is an empty paragraph, the last child of a composite whose body has more than this one block. */\nfunction inTrailingEmptyParagraph($head: ResolvedPos): boolean {\n if ($head.depth < 2 || !isEmptyParagraph($head.parent)) return false\n const composite = $head.node($head.depth - 1)\n return composite.type.name === COMPOSITE\n && $head.index($head.depth - 1) === composite.childCount - 1\n && composite.childCount > CHROME_COUNT + 1\n}\n\n/** Nothing typed yet: empty title and description, no tags, body is one empty paragraph. */\nfunction isEmptyComposite(composite: PMNode): boolean {\n const tags: unknown = composite.child(2).attrs.tags\n return composite.child(0).content.size === 0\n && composite.child(1).content.size === 0\n && (!Array.isArray(tags) || tags.length === 0)\n && composite.childCount === CHROME_COUNT + 1\n && isEmptyParagraph(composite.child(CHROME_COUNT))\n}\n\n/** Child range of `parent` a structure conversion works on: the selected blocks (composite chrome excluded), or without a selection the `scope` — for `'heading'` the caret's heading group (`'group'`) or innermost composite (`'flatten'`). */\nfunction structureRange(state: EditorState, scope: HeadingConversionScope, direction: 'group' | 'flatten'): { parent: PMNode, parentDepth: number, parentStart: number, fromIndex: number, toIndex: number } | null {\n const { $from, $to, $head, empty } = state.selection\n if (!empty) {\n const range = $from.blockRange($to, node => node.type.name === 'doc' || node.type.name === COMPOSITE)\n if (!range) return null\n const parent = range.parent\n // The composite chrome (title, description, tags) is never part of the converted blocks.\n const fromIndex = Math.max(range.startIndex, parent.type.name === COMPOSITE ? CHROME_COUNT : 0)\n const toIndex = range.endIndex\n if (fromIndex >= toIndex) return null\n return { parent, parentDepth: range.depth, parentStart: range.$from.start(range.depth), fromIndex, toIndex }\n }\n if (scope === 'container') {\n const found = compositeAround($head)\n if (found) return { parent: found.node, parentDepth: found.depth, parentStart: $head.start(found.depth), fromIndex: CHROME_COUNT, toIndex: found.node.childCount }\n return { parent: state.doc, parentDepth: 0, parentStart: 0, fromIndex: 0, toIndex: state.doc.childCount }\n }\n if (scope === 'heading') {\n if (direction === 'flatten') {\n const found = compositeAround($head)\n if (!found) return null\n const depth = found.depth - 1\n const fromIndex = $head.index(depth)\n return { parent: $head.node(depth), parentDepth: depth, parentStart: $head.start(depth), fromIndex, toIndex: fromIndex + 1 }\n }\n let depth = $head.depth\n while (depth > 0 && !['doc', COMPOSITE].includes($head.node(depth - 1).type.name)) depth--\n if (depth === 0) return null\n const parent = $head.node(depth - 1)\n const minIndex = parent.type.name === COMPOSITE ? CHROME_COUNT : 0\n let index = $head.index(depth - 1)\n while (index >= minIndex && parent.child(index).type.name !== 'heading') index--\n if (index < minIndex) return null\n const group = headingGroupRange(parent, index)\n return { parent, parentDepth: depth - 1, parentStart: $head.start(depth - 1), fromIndex: group.from, toIndex: group.to }\n }\n return { parent: state.doc, parentDepth: 0, parentStart: 0, fromIndex: 0, toIndex: state.doc.childCount }\n}\n\n/** Children `fromIndex..toIndex` of `parent` (content starting at `parentStart`) and their document range. */\nfunction rangeBlocks(parent: PMNode, parentStart: number, fromIndex: number, toIndex: number): { blocks: PMNode[], from: number, to: number } {\n const blocks: PMNode[] = []\n let from = parentStart\n let to = parentStart\n parent.forEach((child, _offset, index) => {\n if (index < fromIndex) from += child.nodeSize\n else if (index < toIndex) blocks.push(child)\n if (index < toIndex) to += child.nodeSize\n })\n return { blocks, from, to }\n}\n\n// Revoked inside title and description: they are single lines of text, not containers for blocks.\nconst CHROME_REVOKES = [\n ABILITY.headings, ABILITY.lists, ABILITY.blockquote, ABILITY.codeBlock, ABILITY.rule, ABILITY.composite, ABILITY.transcript,\n ABILITY.media, ABILITY.record, ABILITY.aiGenerate, ABILITY.blockDuplicate,\n]\n\n// Schema-only nodes (no node views): shared by the client editor and the server-side Yjs schema.\n// The client adds the node view of `composite` in `node-views.ts`; `compositeTags` needs none (a hidden data row).\nexport const CompositeNode = Node.create<CompositeOptions, CompositeStorage>({\n name: COMPOSITE,\n // Above StarterKit's default (100) so this Enter / Backspace / Delete keymap runs first,\n // below Paragraph (1000) so the paragraph stays the schema's default block.\n priority: 101,\n group: 'block',\n content: `${TITLE} ${DESCRIPTION} ${TAGS} block+`,\n isolating: true,\n defining: true,\n editorBlockContainer: true,\n abilities: { own: [ABILITY.compositeMeta, ABILITY.compositeSummarize, ABILITY.compositeExport, ABILITY.compositeDerive] },\n addOptions: () => ({ kinds: fallbackCompositeKinds(), baskets: fallbackBaskets() }),\n addStorage() {\n return {\n kinds: this.options.kinds,\n baskets: this.options.baskets,\n expanded: createStore<ReadonlySet<string>>(new Set()),\n revealedTitles: createStore<ReadonlySet<string>>(new Set()),\n }\n },\n // A caret inside the body of a collapsed derived Baustein (also inside nested ones) opens it: typing into hidden text would be blind.\n // A caret inside the title of a Baustein with a hidden title reveals that title for the rest of the session.\n onSelectionUpdate() {\n const { expanded, revealedTitles } = this.storage\n const { $head } = this.editor.state.selection\n let open: Set<string> | null = null\n for (let depth = $head.depth; depth > 0; depth--) {\n const node = $head.node(depth)\n const id: unknown = node.attrs.id\n if (node.type.name !== COMPOSITE || typeof id !== 'string' || !id || $head.index(depth) < CHROME_COUNT) continue\n if (derivationOf(node) && !expanded.get().has(id)) (open ??= new Set(expanded.get())).add(id)\n }\n if (open) expanded.set(open)\n if ($head.parent.type.name === TITLE && $head.depth > 0) {\n const composite = $head.node($head.depth - 1)\n const id: unknown = composite.attrs.id\n if (composite.type.name === COMPOSITE && typeof id === 'string' && id && titleHiddenOf(composite) && !revealedTitles.get().has(id)) {\n revealedTitles.set(new Set(revealedTitles.get()).add(id))\n }\n }\n },\n // Block ids reach the Markdown only for referenced Bausteine (see `fences.ts`): `DocumentMarkdown` runs this on the\n // document JSON before serializing, it drops the ids of unreferenced composites, `renderMarkdown` writes the ones left.\n markdownPrepare: (json, editor) => withReferencedFenceIds(json, compositeKindsOf(editor)),\n addAttributes: () => ({\n kind: {\n default: DEFAULT_KIND,\n // `null` (no `data-kind`, e.g. legacy markup) falls back to the default kind.\n parseHTML: el => el.getAttribute('data-kind') || null,\n renderHTML: attrs => ({ 'data-kind': attrs.kind }),\n },\n meta: {\n default: {},\n parseHTML: (el) => {\n try {\n const value: unknown = JSON.parse(el.getAttribute('data-meta') ?? '{}')\n return value && typeof value === 'object' && !Array.isArray(value) ? value : {}\n }\n catch {\n return {}\n }\n },\n renderHTML: attrs => ({ 'data-meta': JSON.stringify(attrs.meta) }),\n },\n }),\n parseHTML: () => [\n { tag: 'section[data-type=\"composite\"]' },\n // Legacy markup of the block the composite family replaced: no `data-kind`, so it parses as kind `section`.\n { tag: 'section[data-type=\"section\"]' },\n ],\n renderHTML: ({ HTMLAttributes }) => ['section', mergeAttributes(HTMLAttributes, { 'data-type': COMPOSITE }), 0],\n // Fenced: `::: <kind> {meta JSON, only when non-empty}`, then title, description, tags and body blocks as separate\n // Markdown blocks (empty description/tags render nothing), then `:::`. Same child iteration as the blockquote's\n // renderMarkdown (keeps list numbering etc. per sibling index).\n renderMarkdown: (node, h) => {\n const kind = typeof node.attrs?.kind === 'string' && node.attrs.kind ? node.attrs.kind : DEFAULT_KIND\n const meta: unknown = node.attrs?.meta\n const body = (node.content ?? [])\n .map((child, index) => h.renderChild?.(child, index) ?? h.renderChildren([child]))\n .filter(Boolean)\n .join('\\n\\n')\n const id: unknown = node.attrs?.id\n return compositeFence(kind, meta && typeof meta === 'object' ? meta as Record<string, unknown> : {}, body, typeof id === 'string' && id ? id : undefined)\n },\n\n addCommands() {\n return {\n toggleCompositeExpanded: id => () => {\n const { expanded } = this.storage\n const next = new Set(expanded.get())\n if (!next.delete(id)) next.add(id)\n expanded.set(next)\n return true\n },\n\n setCompositeBasket: (basket, pos) => ({ state, tr, dispatch }) => {\n const found = compositeAt(state, pos)\n if (!found) return false\n if (basket !== null && !basketById(this.options.baskets, basket)) return false\n if (dispatch) {\n const { [BASKET_KEY]: _previous, ...meta } = found.node.attrs.meta as Record<string, unknown>\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, meta: basket === null ? meta : { ...meta, [BASKET_KEY]: basket } })\n }\n return true\n },\n\n setCompositeTitleHidden: (hidden, pos) => ({ state, tr, dispatch }) => {\n const found = compositeAt(state, pos)\n if (!found) return false\n if (dispatch) {\n const { [TITLE_HIDDEN_KEY]: _previous, ...meta } = found.node.attrs.meta as Record<string, unknown>\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, meta: hidden ? { ...meta, [TITLE_HIDDEN_KEY]: true } : meta })\n const id: unknown = found.node.attrs.id\n const { revealedTitles } = this.storage\n if (!hidden && typeof id === 'string' && revealedTitles.get().has(id)) {\n const next = new Set(revealedTitles.get())\n next.delete(id)\n revealedTitles.set(next)\n }\n }\n return true\n },\n\n insertComposite: (kind = DEFAULT_KIND) => ({ state, tr, dispatch }) => {\n const { $head } = state.selection\n let depth = $head.depth\n while (depth > 0 && !['doc', COMPOSITE].includes($head.node(depth - 1).type.name)) depth--\n let from = $head.pos\n let to = from\n if (depth > 0) {\n const block = $head.node(depth)\n if (isChromeNode(block)) from = to = bodyStart($head.node(depth - 1), $head.start(depth - 1))\n else if (isEmptyParagraph(block)) {\n from = $head.before(depth)\n to = $head.after(depth)\n }\n else from = to = $head.after(depth)\n }\n const $from = state.doc.resolve(from)\n if (!$from.parent.canReplaceWith($from.index(), state.doc.resolve(to).index(), this.type)) return false\n if (dispatch) {\n const json = compositeJSON(kind)\n tr.replaceWith(from, to, state.schema.nodeFromJSON(json))\n tr.setSelection(TextSelection.create(tr.doc, from + 2)).scrollIntoView()\n }\n return true\n },\n\n wrapInComposite: (kind = DEFAULT_KIND) => ({ state, tr, dispatch }) => {\n const { $from, $to } = state.selection\n const range = $from.blockRange($to, node => node.type.name === 'doc' || node.type.name === COMPOSITE)\n if (!range) return false\n if (range.parent.type.name === COMPOSITE && range.startIndex < CHROME_COUNT) return false\n if (!range.parent.canReplaceWith(range.startIndex, range.endIndex, this.type)) return false\n if (dispatch) {\n const { nodes } = state.schema\n const composite = this.type.create({ kind }, [\n nodes[TITLE]!.create(), nodes[DESCRIPTION]!.create(), nodes[TAGS]!.create(),\n ...state.doc.slice(range.start, range.end).content.content,\n ])\n tr.replaceWith(range.start, range.end, composite)\n tr.setSelection(TextSelection.create(tr.doc, range.start + 2)).scrollIntoView()\n }\n return true\n },\n\n setCompositeKind: (kind, pos) => ({ state, tr, dispatch }) => {\n const found = compositeAt(state, pos)\n if (!found) return false\n if (dispatch) {\n const meta = keptMeta(compositeKind(this.options.kinds, kind), found.node.attrs.meta as Record<string, unknown>)\n tr.setNodeMarkup(found.pos, undefined, { ...found.node.attrs, kind, meta })\n }\n return true\n },\n\n exitComposite: () => ({ state, tr, dispatch }) => {\n const { $head } = state.selection\n const composite = compositeAround($head)\n if (!composite) return false\n if (dispatch) {\n // Leaving from a trailing empty paragraph moves it out instead of leaving an empty line behind.\n if ($head.depth === composite.depth + 1 && inTrailingEmptyParagraph($head)) tr.delete($head.before(), $head.after())\n const after = tr.mapping.map($head.after(composite.depth))\n tr.insert(after, state.schema.nodes.paragraph!.create())\n tr.setSelection(TextSelection.create(tr.doc, after + 1)).scrollIntoView()\n }\n return true\n },\n\n convertHeadingsToComposites: ({ recursive, scope, kind = DEFAULT_KIND }) => ({ state, tr, dispatch }) => {\n const range = structureRange(state, scope, 'group')\n if (!range) return false\n const { blocks, from, to } = rangeBlocks(range.parent, range.parentStart, range.fromIndex, range.toIndex)\n const result = groupHeadingsIntoComposites(state.schema, blocks, recursive, kind)\n if (!result) return false\n if (dispatch) {\n tr.replaceWith(from, to, result)\n tr.setSelection(Selection.near(tr.doc.resolve(from), 1)).scrollIntoView()\n }\n return true\n },\n\n convertCompositesToHeadings: ({ recursive, scope }) => ({ state, tr, dispatch }) => {\n const range = structureRange(state, scope, 'flatten')\n if (!range) return false\n const $pos = state.selection.empty ? state.selection.$head : state.selection.$from\n let level = 1\n for (let depth = 1; depth <= range.parentDepth; depth++) {\n if ($pos.node(depth).type.name === COMPOSITE) level++\n }\n const { blocks, from, to } = rangeBlocks(range.parent, range.parentStart, range.fromIndex, range.toIndex)\n const result = flattenCompositesToHeadings(state.schema, blocks, recursive, level, this.options.kinds)\n if (!result) return false\n if (dispatch) {\n tr.replaceWith(from, to, result)\n tr.setSelection(Selection.near(tr.doc.resolve(from), 1)).scrollIntoView()\n }\n return true\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n const { $head } = this.editor.state.selection\n const type = $head.parent.type.name\n if (type === TITLE || type === DESCRIPTION) {\n const depth = $head.depth - 1\n const composite = $head.node(depth)\n const start = $head.start(depth)\n return this.editor.commands.command(({ tr }) => {\n const selection = Selection.findFrom(tr.doc.resolve(bodyStart(composite, start)), 1)\n if (!selection) return false\n tr.setSelection(selection).scrollIntoView()\n return true\n })\n }\n if (this.editor.state.selection.empty && inTrailingEmptyParagraph($head)) return this.editor.commands.exitComposite()\n return false\n },\n\n Backspace: () => {\n const { selection } = this.editor.state\n const { $head } = selection\n if (!selection.empty || !$head.parent.isTextblock || $head.parentOffset !== 0 || $head.depth < 2) return false\n const depth = $head.depth - 1\n const composite = $head.node(depth)\n if (composite.type.name !== COMPOSITE) return false\n const start = $head.start(depth)\n const titleEnd = start + composite.child(0).nodeSize - 1\n const type = $head.parent.type.name\n\n if (type === DESCRIPTION) {\n return this.editor.commands.command(({ tr }) => {\n tr.setSelection(TextSelection.create(tr.doc, titleEnd))\n return true\n })\n }\n if (type === TITLE) {\n // Never joins anything into the title; an untouched composite turns back into a paragraph.\n if (!isEmptyComposite(composite)) return true\n return this.editor.commands.command(({ tr, state }) => {\n const from = $head.before(depth)\n tr.replaceWith(from, $head.after(depth), state.schema.nodes.paragraph!.create())\n tr.setSelection(TextSelection.create(tr.doc, from + 1))\n return true\n })\n }\n // First body block: ProseMirror's joinBackward would delete the tags row (an atom) — move into the chrome instead.\n if ($head.index(depth) !== CHROME_COUNT) return false\n return this.editor.commands.command(({ tr }) => {\n if (isEmptyParagraph($head.parent) && composite.childCount > CHROME_COUNT + 1) tr.delete($head.before(), $head.after())\n tr.setSelection(TextSelection.create(tr.doc, titleEnd))\n return true\n })\n },\n\n // No join of the following block into title or description.\n Delete: () => {\n const { selection } = this.editor.state\n const { $head } = selection\n const type = $head.parent.type.name\n return selection.empty && (type === TITLE || type === DESCRIPTION) && $head.parentOffset === $head.parent.content.size\n },\n\n 'Mod-Shift-a': () => this.editor.commands.insertComposite(DEFAULT_KIND),\n }\n },\n})\n\nexport const CompositeTitleNode = Node.create({\n name: TITLE,\n // Above StarterKit's hard break (100), which also binds Mod-Enter.\n priority: 101,\n content: 'inline*',\n defining: true,\n editorChrome: true,\n abilities: { revokes: CHROME_REVOKES },\n // Above the heading's `h2` rule; the second rule reads legacy markup.\n parseHTML: () => [\n { tag: 'h2[data-type=\"composite-title\"]', priority: 60 },\n { tag: 'h2[data-type=\"section-title\"]', priority: 60 },\n ],\n renderHTML: ({ HTMLAttributes }) => ['h2', mergeAttributes(HTMLAttributes, { 'data-type': 'composite-title', class: 'composite-title' }), 0],\n renderMarkdown: (node, h) => `## ${h.renderChildren(node.content ?? [])}`,\n addKeyboardShortcuts() {\n return {\n // Here rather than on CompositeNode: the ability gate swallows every Mod-/Alt- key of the `composite` type where\n // `format.composite` is missing (title, code blocks, speaker segments), but leaving a composite must work everywhere.\n 'Mod-Enter': () => this.editor.commands.exitComposite(),\n }\n },\n})\n\nexport const CompositeDescriptionNode = Node.create({\n name: DESCRIPTION,\n content: 'inline*',\n defining: true,\n editorChrome: true,\n abilities: { revokes: CHROME_REVOKES },\n // Above the paragraph's `p` rule; the second rule reads legacy markup.\n parseHTML: () => [\n { tag: 'p[data-type=\"composite-description\"]', priority: 60 },\n { tag: 'p[data-type=\"section-summary\"]', priority: 60 },\n ],\n renderHTML: ({ HTMLAttributes }) => ['p', mergeAttributes(HTMLAttributes, { 'data-type': 'composite-description', class: 'composite-description' }), 0],\n renderMarkdown: (node, h) => {\n const text = h.renderChildren(node.content ?? [])\n return text ? `> ${text}` : ''\n },\n})\n\nexport const CompositeTagsNode = Node.create({\n name: TAGS,\n atom: true,\n selectable: false,\n draggable: false,\n allowGapCursor: false,\n editorChrome: true,\n abilities: { revokes: [...TEXT_ABILITIES, ABILITY.blockDelete, ABILITY.blockDuplicate] },\n addAttributes: () => ({\n tags: {\n default: [],\n parseHTML: (el) => {\n try {\n const value: unknown = JSON.parse(el.getAttribute('data-tags') ?? '[]')\n return Array.isArray(value) ? value.filter(tag => typeof tag === 'string') : []\n }\n catch {\n return []\n }\n },\n renderHTML: attrs => ({ 'data-tags': JSON.stringify(attrs.tags) }),\n },\n }),\n // The second rule reads legacy markup.\n parseHTML: () => [{ tag: 'div[data-type=\"composite-tags\"]' }, { tag: 'div[data-type=\"section-tags\"]' }],\n renderHTML: ({ HTMLAttributes }) => ['div', mergeAttributes(HTMLAttributes, { 'data-type': 'composite-tags', class: 'composite-tags' })],\n renderMarkdown: (node) => {\n const tags: unknown = node.attrs?.tags\n return Array.isArray(tags) && tags.length ? `Tags: ${tags.map(tag => `#${tag}`).join(' ')}` : ''\n },\n})\n\nexport const CompositeNodes = [CompositeNode, CompositeTitleNode, CompositeDescriptionNode, CompositeTagsNode]\n"],"mappings":";;;;;;;;;;;;;AA4DA,MAAM,YAAY;AAClB,MAAM,QAAQ;AACd,MAAM,cAAc;AACpB,MAAM,OAAO;;AAEb,MAAM,eAAe;;AAErB,MAAa,yBAAyB;AACtC,MAAM,eAAA;;;;;AAMN,SAAgB,iBAAiB,QAA0C;CACzE,MAAM,aAAmD,OAAO,QAAQ,WAAW;CACnF,IAAI,YAAY,OAAO;CACvB,MAAM,QAAQ,cAAc,MAAM;CAClC,OAAO,QAAQ,sBAAsB,MAAM,IAAI,CAAC,IAAI,uBAAuB;AAC7E;;AAGA,SAAgB,mBAAmB,QAAmC;CACpE,MAAM,aAA4C,OAAO,QAAQ,WAAW;CAC5E,IAAI,YAAY,OAAO;CACvB,MAAM,QAAQ,cAAc,MAAM;CAClC,OAAO,QAAQ,eAAe,MAAM,IAAI,CAAC,IAAI,gBAAgB;AAC/D;;AAGA,SAAgB,cAAc,MAAc,OAAsB,CAAC,EAAE,MAAM,YAAY,CAAC,GAAgB;CACtG,OAAO;EAAE,MAAM;EAAW,OAAO,EAAE,KAAK;EAAG,SAAS;GAAC,EAAE,MAAM,MAAM;GAAG,EAAE,MAAM,YAAY;GAAG,EAAE,MAAM,KAAK;GAAG,GAAG;EAAI;CAAE;AACxH;;AAGA,SAAgB,gBAAgB,MAAwE;CACtG,KAAK,IAAI,QAAQ,KAAK,OAAO,QAAQ,GAAG,SAAS;EAC/C,MAAM,OAAO,KAAK,KAAK,KAAK;EAC5B,IAAI,KAAK,KAAK,SAAS,WAAW,OAAO;GAAE;GAAO,KAAK,KAAK,OAAO,KAAK;GAAG;EAAK;CAClF;CACA,OAAO;AACT;;AAGA,SAAgB,YAAY,OAAoB,KAA+D;CAC7G,IAAI,QAAQ,KAAA,GAAW,OAAO,gBAAgB,MAAM,UAAU,KAAK;CACnE,MAAM,OAAO,MAAM,IAAI,OAAO,GAAG;CACjC,OAAO,MAAM,KAAK,SAAS,YAAY;EAAE;EAAK;CAAK,IAAI;AACzD;;AAGA,SAAS,UAAU,WAAmB,OAAuB;CAC3D,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,KAAK,OAAO,UAAU,MAAM,CAAC,CAAC,CAAC;CACjE,OAAO;AACT;AAEA,MAAM,oBAAoB,SAAiB,KAAK,KAAK,SAAS,eAAe,KAAK,QAAQ,SAAS;;AAGnG,SAAS,yBAAyB,OAA6B;CAC7D,IAAI,MAAM,QAAQ,KAAK,CAAC,iBAAiB,MAAM,MAAM,GAAG,OAAO;CAC/D,MAAM,YAAY,MAAM,KAAK,MAAM,QAAQ,CAAC;CAC5C,OAAO,UAAU,KAAK,SAAS,aAC1B,MAAM,MAAM,MAAM,QAAQ,CAAC,MAAM,UAAU,aAAa,KACxD,UAAU,aAAa;AAC9B;;AAGA,SAAS,iBAAiB,WAA4B;CACpD,MAAM,OAAgB,UAAU,MAAM,CAAC,CAAC,CAAC,MAAM;CAC/C,OAAO,UAAU,MAAM,CAAC,CAAC,CAAC,QAAQ,SAAS,KACtC,UAAU,MAAM,CAAC,CAAC,CAAC,QAAQ,SAAS,MACnC,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,MACzC,UAAU,eAAe,KACzB,iBAAiB,UAAU,MAAM,YAAY,CAAC;AACrD;;AAGA,SAAS,eAAe,OAAoB,OAA+B,WAAyI;CAClN,MAAM,EAAE,OAAO,KAAK,OAAO,UAAU,MAAM;CAC3C,IAAI,CAAC,OAAO;EACV,MAAM,QAAQ,MAAM,WAAW,MAAK,SAAQ,KAAK,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,SAAS;EACpG,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,SAAS,MAAM;EAErB,MAAM,YAAY,KAAK,IAAI,MAAM,YAAY,OAAO,KAAK,SAAS,YAAY,eAAe,CAAC;EAC9F,MAAM,UAAU,MAAM;EACtB,IAAI,aAAa,SAAS,OAAO;EACjC,OAAO;GAAE;GAAQ,aAAa,MAAM;GAAO,aAAa,MAAM,MAAM,MAAM,MAAM,KAAK;GAAG;GAAW;EAAQ;CAC7G;CACA,IAAI,UAAU,aAAa;EACzB,MAAM,QAAQ,gBAAgB,KAAK;EACnC,IAAI,OAAO,OAAO;GAAE,QAAQ,MAAM;GAAM,aAAa,MAAM;GAAO,aAAa,MAAM,MAAM,MAAM,KAAK;GAAG,WAAW;GAAc,SAAS,MAAM,KAAK;EAAW;EACjK,OAAO;GAAE,QAAQ,MAAM;GAAK,aAAa;GAAG,aAAa;GAAG,WAAW;GAAG,SAAS,MAAM,IAAI;EAAW;CAC1G;CACA,IAAI,UAAU,WAAW;EACvB,IAAI,cAAc,WAAW;GAC3B,MAAM,QAAQ,gBAAgB,KAAK;GACnC,IAAI,CAAC,OAAO,OAAO;GACnB,MAAM,QAAQ,MAAM,QAAQ;GAC5B,MAAM,YAAY,MAAM,MAAM,KAAK;GACnC,OAAO;IAAE,QAAQ,MAAM,KAAK,KAAK;IAAG,aAAa;IAAO,aAAa,MAAM,MAAM,KAAK;IAAG;IAAW,SAAS,YAAY;GAAE;EAC7H;EACA,IAAI,QAAQ,MAAM;EAClB,OAAO,QAAQ,KAAK,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,SAAS,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG;EACnF,IAAI,UAAU,GAAG,OAAO;EACxB,MAAM,SAAS,MAAM,KAAK,QAAQ,CAAC;EACnC,MAAM,WAAW,OAAO,KAAK,SAAS,YAAY,eAAe;EACjE,IAAI,QAAQ,MAAM,MAAM,QAAQ,CAAC;EACjC,OAAO,SAAS,YAAY,OAAO,MAAM,KAAK,CAAC,CAAC,KAAK,SAAS,WAAW;EACzE,IAAI,QAAQ,UAAU,OAAO;EAC7B,MAAM,QAAQ,kBAAkB,QAAQ,KAAK;EAC7C,OAAO;GAAE;GAAQ,aAAa,QAAQ;GAAG,aAAa,MAAM,MAAM,QAAQ,CAAC;GAAG,WAAW,MAAM;GAAM,SAAS,MAAM;EAAG;CACzH;CACA,OAAO;EAAE,QAAQ,MAAM;EAAK,aAAa;EAAG,aAAa;EAAG,WAAW;EAAG,SAAS,MAAM,IAAI;CAAW;AAC1G;;AAGA,SAAS,YAAY,QAAgB,aAAqB,WAAmB,SAAiE;CAC5I,MAAM,SAAmB,CAAC;CAC1B,IAAI,OAAO;CACX,IAAI,KAAK;CACT,OAAO,SAAS,OAAO,SAAS,UAAU;EACxC,IAAI,QAAQ,WAAW,QAAQ,MAAM;OAChC,IAAI,QAAQ,SAAS,OAAO,KAAK,KAAK;EAC3C,IAAI,QAAQ,SAAS,MAAM,MAAM;CACnC,CAAC;CACD,OAAO;EAAE;EAAQ;EAAM;CAAG;AAC5B;AAGA,MAAM,iBAAiB;CACrB,QAAQ;CAAU,QAAQ;CAAO,QAAQ;CAAY,QAAQ;CAAW,QAAQ;CAAM,QAAQ;CAAW,QAAQ;CACjH,QAAQ;CAAO,QAAQ;CAAQ,QAAQ;CAAY,QAAQ;AAC7D;AAIA,MAAa,gBAAgB,KAAK,OAA2C;CAC3E,MAAM;CAGN,UAAU;CACV,OAAO;CACP,SAAS,GAAG,MAAM,GAAG,YAAY,GAAG,KAAK;CACzC,WAAW;CACX,UAAU;CACV,sBAAsB;CACtB,WAAW,EAAE,KAAK;EAAC,QAAQ;EAAe,QAAQ;EAAoB,QAAQ;EAAiB,QAAQ;CAAe,EAAE;CACxH,mBAAmB;EAAE,OAAO,uBAAuB;EAAG,SAAS,gBAAgB;CAAE;CACjF,aAAa;EACX,OAAO;GACL,OAAO,KAAK,QAAQ;GACpB,SAAS,KAAK,QAAQ;GACtB,UAAU,4BAAiC,IAAI,IAAI,CAAC;GACpD,gBAAgB,4BAAiC,IAAI,IAAI,CAAC;EAC5D;CACF;CAGA,oBAAoB;EAClB,MAAM,EAAE,UAAU,mBAAmB,KAAK;EAC1C,MAAM,EAAE,UAAU,KAAK,OAAO,MAAM;EACpC,IAAI,OAA2B;EAC/B,KAAK,IAAI,QAAQ,MAAM,OAAO,QAAQ,GAAG,SAAS;GAChD,MAAM,OAAO,MAAM,KAAK,KAAK;GAC7B,MAAM,KAAc,KAAK,MAAM;GAC/B,IAAI,KAAK,KAAK,SAAS,aAAa,OAAO,OAAO,YAAY,CAAC,MAAM,MAAM,MAAM,KAAK,IAAI,cAAc;GACxG,IAAI,aAAa,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,IAAI,SAAS,IAAI,CAAC,EAAA,CAAG,IAAI,EAAE;EAC9F;EACA,IAAI,MAAM,SAAS,IAAI,IAAI;EAC3B,IAAI,MAAM,OAAO,KAAK,SAAS,SAAS,MAAM,QAAQ,GAAG;GACvD,MAAM,YAAY,MAAM,KAAK,MAAM,QAAQ,CAAC;GAC5C,MAAM,KAAc,UAAU,MAAM;GACpC,IAAI,UAAU,KAAK,SAAS,aAAa,OAAO,OAAO,YAAY,MAAM,cAAc,SAAS,KAAK,CAAC,eAAe,IAAI,CAAC,CAAC,IAAI,EAAE,GAC/H,eAAe,IAAI,IAAI,IAAI,eAAe,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;EAE5D;CACF;CAGA,kBAAkB,MAAM,WAAW,uBAAuB,MAAM,iBAAiB,MAAM,CAAC;CACxF,sBAAsB;EACpB,MAAM;GACJ,SAAS;GAET,YAAW,OAAM,GAAG,aAAa,WAAW,KAAK;GACjD,aAAY,WAAU,EAAE,aAAa,MAAM,KAAK;EAClD;EACA,MAAM;GACJ,SAAS,CAAC;GACV,YAAY,OAAO;IACjB,IAAI;KACF,MAAM,QAAiB,KAAK,MAAM,GAAG,aAAa,WAAW,KAAK,IAAI;KACtE,OAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;IAChF,QACM;KACJ,OAAO,CAAC;IACV;GACF;GACA,aAAY,WAAU,EAAE,aAAa,KAAK,UAAU,MAAM,IAAI,EAAE;EAClE;CACF;CACA,iBAAiB,CACf,EAAE,KAAK,mCAAiC,GAExC,EAAE,KAAK,iCAA+B,CACxC;CACA,aAAa,EAAE,qBAAqB;EAAC;EAAW,gBAAgB,gBAAgB,EAAE,aAAa,UAAU,CAAC;EAAG;CAAC;CAI9G,iBAAiB,MAAM,MAAM;EAC3B,MAAM,OAAO,OAAO,KAAK,OAAO,SAAS,YAAY,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO;EACzF,MAAM,OAAgB,KAAK,OAAO;EAClC,MAAM,QAAQ,KAAK,WAAW,CAAC,EAAA,CAC5B,KAAK,OAAO,UAAU,EAAE,cAAc,OAAO,KAAK,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CACjF,OAAO,OAAO,CAAC,CACf,KAAK,MAAM;EACd,MAAM,KAAc,KAAK,OAAO;EAChC,OAAO,eAAe,MAAM,QAAQ,OAAO,SAAS,WAAW,OAAkC,CAAC,GAAG,MAAM,OAAO,OAAO,YAAY,KAAK,KAAK,KAAA,CAAS;CAC1J;CAEA,cAAc;EACZ,OAAO;GACL,0BAAyB,aAAY;IACnC,MAAM,EAAE,aAAa,KAAK;IAC1B,MAAM,OAAO,IAAI,IAAI,SAAS,IAAI,CAAC;IACnC,IAAI,CAAC,KAAK,OAAO,EAAE,GAAG,KAAK,IAAI,EAAE;IACjC,SAAS,IAAI,IAAI;IACjB,OAAO;GACT;GAEA,qBAAqB,QAAQ,SAAS,EAAE,OAAO,IAAI,eAAe;IAChE,MAAM,QAAQ,YAAY,OAAO,GAAG;IACpC,IAAI,CAAC,OAAO,OAAO;IACnB,IAAI,WAAW,QAAQ,CAAC,WAAW,KAAK,QAAQ,SAAS,MAAM,GAAG,OAAO;IACzE,IAAI,UAAU;KACZ,MAAM,GAAG,aAAa,WAAW,GAAG,SAAS,MAAM,KAAK,MAAM;KAC9D,GAAG,cAAc,MAAM,KAAK,KAAA,GAAW;MAAE,GAAG,MAAM,KAAK;MAAO,MAAM,WAAW,OAAO,OAAO;OAAE,GAAG;QAAO,aAAa;MAAO;KAAE,CAAC;IAClI;IACA,OAAO;GACT;GAEA,0BAA0B,QAAQ,SAAS,EAAE,OAAO,IAAI,eAAe;IACrE,MAAM,QAAQ,YAAY,OAAO,GAAG;IACpC,IAAI,CAAC,OAAO,OAAO;IACnB,IAAI,UAAU;KACZ,MAAM,GAAG,mBAAmB,WAAW,GAAG,SAAS,MAAM,KAAK,MAAM;KACpE,GAAG,cAAc,MAAM,KAAK,KAAA,GAAW;MAAE,GAAG,MAAM,KAAK;MAAO,MAAM,SAAS;OAAE,GAAG;QAAO,mBAAmB;MAAK,IAAI;KAAK,CAAC;KAC3H,MAAM,KAAc,MAAM,KAAK,MAAM;KACrC,MAAM,EAAE,mBAAmB,KAAK;KAChC,IAAI,CAAC,UAAU,OAAO,OAAO,YAAY,eAAe,IAAI,CAAC,CAAC,IAAI,EAAE,GAAG;MACrE,MAAM,OAAO,IAAI,IAAI,eAAe,IAAI,CAAC;MACzC,KAAK,OAAO,EAAE;MACd,eAAe,IAAI,IAAI;KACzB;IACF;IACA,OAAO;GACT;GAEA,kBAAkB,OAAO,kBAAkB,EAAE,OAAO,IAAI,eAAe;IACrE,MAAM,EAAE,UAAU,MAAM;IACxB,IAAI,QAAQ,MAAM;IAClB,OAAO,QAAQ,KAAK,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,SAAS,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG;IACnF,IAAI,OAAO,MAAM;IACjB,IAAI,KAAK;IACT,IAAI,QAAQ,GAAG;KACb,MAAM,QAAQ,MAAM,KAAK,KAAK;KAC9B,IAAI,aAAa,KAAK,GAAG,OAAO,KAAK,UAAU,MAAM,KAAK,QAAQ,CAAC,GAAG,MAAM,MAAM,QAAQ,CAAC,CAAC;UACvF,IAAI,iBAAiB,KAAK,GAAG;MAChC,OAAO,MAAM,OAAO,KAAK;MACzB,KAAK,MAAM,MAAM,KAAK;KACxB,OACK,OAAO,KAAK,MAAM,MAAM,KAAK;IACpC;IACA,MAAM,QAAQ,MAAM,IAAI,QAAQ,IAAI;IACpC,IAAI,CAAC,MAAM,OAAO,eAAe,MAAM,MAAM,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,KAAK,IAAI,GAAG,OAAO;IAClG,IAAI,UAAU;KACZ,MAAM,OAAO,cAAc,IAAI;KAC/B,GAAG,YAAY,MAAM,IAAI,MAAM,OAAO,aAAa,IAAI,CAAC;KACxD,GAAG,aAAa,cAAc,OAAO,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,eAAe;IACzE;IACA,OAAO;GACT;GAEA,kBAAkB,OAAO,kBAAkB,EAAE,OAAO,IAAI,eAAe;IACrE,MAAM,EAAE,OAAO,QAAQ,MAAM;IAC7B,MAAM,QAAQ,MAAM,WAAW,MAAK,SAAQ,KAAK,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,SAAS;IACpG,IAAI,CAAC,OAAO,OAAO;IACnB,IAAI,MAAM,OAAO,KAAK,SAAS,aAAa,MAAM,aAAa,cAAc,OAAO;IACpF,IAAI,CAAC,MAAM,OAAO,eAAe,MAAM,YAAY,MAAM,UAAU,KAAK,IAAI,GAAG,OAAO;IACtF,IAAI,UAAU;KACZ,MAAM,EAAE,UAAU,MAAM;KACxB,MAAM,YAAY,KAAK,KAAK,OAAO,EAAE,KAAK,GAAG;MAC3C,MAAM,MAAM,CAAE,OAAO;MAAG,MAAM,YAAY,CAAE,OAAO;MAAG,MAAM,KAAK,CAAE,OAAO;MAC1E,GAAG,MAAM,IAAI,MAAM,MAAM,OAAO,MAAM,GAAG,CAAC,CAAC,QAAQ;KACrD,CAAC;KACD,GAAG,YAAY,MAAM,OAAO,MAAM,KAAK,SAAS;KAChD,GAAG,aAAa,cAAc,OAAO,GAAG,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,eAAe;IAChF;IACA,OAAO;GACT;GAEA,mBAAmB,MAAM,SAAS,EAAE,OAAO,IAAI,eAAe;IAC5D,MAAM,QAAQ,YAAY,OAAO,GAAG;IACpC,IAAI,CAAC,OAAO,OAAO;IACnB,IAAI,UAAU;KACZ,MAAM,OAAO,SAAS,cAAc,KAAK,QAAQ,OAAO,IAAI,GAAG,MAAM,KAAK,MAAM,IAA+B;KAC/G,GAAG,cAAc,MAAM,KAAK,KAAA,GAAW;MAAE,GAAG,MAAM,KAAK;MAAO;MAAM;KAAK,CAAC;IAC5E;IACA,OAAO;GACT;GAEA,sBAAsB,EAAE,OAAO,IAAI,eAAe;IAChD,MAAM,EAAE,UAAU,MAAM;IACxB,MAAM,YAAY,gBAAgB,KAAK;IACvC,IAAI,CAAC,WAAW,OAAO;IACvB,IAAI,UAAU;KAEZ,IAAI,MAAM,UAAU,UAAU,QAAQ,KAAK,yBAAyB,KAAK,GAAG,GAAG,OAAO,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC;KACnH,MAAM,QAAQ,GAAG,QAAQ,IAAI,MAAM,MAAM,UAAU,KAAK,CAAC;KACzD,GAAG,OAAO,OAAO,MAAM,OAAO,MAAM,UAAW,OAAO,CAAC;KACvD,GAAG,aAAa,cAAc,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,eAAe;IAC1E;IACA,OAAO;GACT;GAEA,8BAA8B,EAAE,WAAW,OAAO,OAAO,oBAAoB,EAAE,OAAO,IAAI,eAAe;IACvG,MAAM,QAAQ,eAAe,OAAO,OAAO,OAAO;IAClD,IAAI,CAAC,OAAO,OAAO;IACnB,MAAM,EAAE,QAAQ,MAAM,OAAO,YAAY,MAAM,QAAQ,MAAM,aAAa,MAAM,WAAW,MAAM,OAAO;IACxG,MAAM,SAAS,4BAA4B,MAAM,QAAQ,QAAQ,WAAW,IAAI;IAChF,IAAI,CAAC,QAAQ,OAAO;IACpB,IAAI,UAAU;KACZ,GAAG,YAAY,MAAM,IAAI,MAAM;KAC/B,GAAG,aAAa,UAAU,KAAK,GAAG,IAAI,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe;IAC1E;IACA,OAAO;GACT;GAEA,8BAA8B,EAAE,WAAW,aAAa,EAAE,OAAO,IAAI,eAAe;IAClF,MAAM,QAAQ,eAAe,OAAO,OAAO,SAAS;IACpD,IAAI,CAAC,OAAO,OAAO;IACnB,MAAM,OAAO,MAAM,UAAU,QAAQ,MAAM,UAAU,QAAQ,MAAM,UAAU;IAC7E,IAAI,QAAQ;IACZ,KAAK,IAAI,QAAQ,GAAG,SAAS,MAAM,aAAa,SAC9C,IAAI,KAAK,KAAK,KAAK,CAAC,CAAC,KAAK,SAAS,WAAW;IAEhD,MAAM,EAAE,QAAQ,MAAM,OAAO,YAAY,MAAM,QAAQ,MAAM,aAAa,MAAM,WAAW,MAAM,OAAO;IACxG,MAAM,SAAS,4BAA4B,MAAM,QAAQ,QAAQ,WAAW,OAAO,KAAK,QAAQ,KAAK;IACrG,IAAI,CAAC,QAAQ,OAAO;IACpB,IAAI,UAAU;KACZ,GAAG,YAAY,MAAM,IAAI,MAAM;KAC/B,GAAG,aAAa,UAAU,KAAK,GAAG,IAAI,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe;IAC1E;IACA,OAAO;GACT;EACF;CACF;CAEA,uBAAuB;EACrB,OAAO;GACL,aAAa;IACX,MAAM,EAAE,UAAU,KAAK,OAAO,MAAM;IACpC,MAAM,OAAO,MAAM,OAAO,KAAK;IAC/B,IAAI,SAAS,SAAS,SAAS,aAAa;KAC1C,MAAM,QAAQ,MAAM,QAAQ;KAC5B,MAAM,YAAY,MAAM,KAAK,KAAK;KAClC,MAAM,QAAQ,MAAM,MAAM,KAAK;KAC/B,OAAO,KAAK,OAAO,SAAS,SAAS,EAAE,SAAS;MAC9C,MAAM,YAAY,UAAU,SAAS,GAAG,IAAI,QAAQ,UAAU,WAAW,KAAK,CAAC,GAAG,CAAC;MACnF,IAAI,CAAC,WAAW,OAAO;MACvB,GAAG,aAAa,SAAS,CAAC,CAAC,eAAe;MAC1C,OAAO;KACT,CAAC;IACH;IACA,IAAI,KAAK,OAAO,MAAM,UAAU,SAAS,yBAAyB,KAAK,GAAG,OAAO,KAAK,OAAO,SAAS,cAAc;IACpH,OAAO;GACT;GAEA,iBAAiB;IACf,MAAM,EAAE,cAAc,KAAK,OAAO;IAClC,MAAM,EAAE,UAAU;IAClB,IAAI,CAAC,UAAU,SAAS,CAAC,MAAM,OAAO,eAAe,MAAM,iBAAiB,KAAK,MAAM,QAAQ,GAAG,OAAO;IACzG,MAAM,QAAQ,MAAM,QAAQ;IAC5B,MAAM,YAAY,MAAM,KAAK,KAAK;IAClC,IAAI,UAAU,KAAK,SAAS,WAAW,OAAO;IAE9C,MAAM,WADQ,MAAM,MAAM,KACL,IAAI,UAAU,MAAM,CAAC,CAAC,CAAC,WAAW;IACvD,MAAM,OAAO,MAAM,OAAO,KAAK;IAE/B,IAAI,SAAS,aACX,OAAO,KAAK,OAAO,SAAS,SAAS,EAAE,SAAS;KAC9C,GAAG,aAAa,cAAc,OAAO,GAAG,KAAK,QAAQ,CAAC;KACtD,OAAO;IACT,CAAC;IAEH,IAAI,SAAS,OAAO;KAElB,IAAI,CAAC,iBAAiB,SAAS,GAAG,OAAO;KACzC,OAAO,KAAK,OAAO,SAAS,SAAS,EAAE,IAAI,YAAY;MACrD,MAAM,OAAO,MAAM,OAAO,KAAK;MAC/B,GAAG,YAAY,MAAM,MAAM,MAAM,KAAK,GAAG,MAAM,OAAO,MAAM,UAAW,OAAO,CAAC;MAC/E,GAAG,aAAa,cAAc,OAAO,GAAG,KAAK,OAAO,CAAC,CAAC;MACtD,OAAO;KACT,CAAC;IACH;IAEA,IAAI,MAAM,MAAM,KAAK,MAAM,cAAc,OAAO;IAChD,OAAO,KAAK,OAAO,SAAS,SAAS,EAAE,SAAS;KAC9C,IAAI,iBAAiB,MAAM,MAAM,KAAK,UAAU,aAAa,GAAkB,GAAG,OAAO,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC;KACtH,GAAG,aAAa,cAAc,OAAO,GAAG,KAAK,QAAQ,CAAC;KACtD,OAAO;IACT,CAAC;GACH;GAGA,cAAc;IACZ,MAAM,EAAE,cAAc,KAAK,OAAO;IAClC,MAAM,EAAE,UAAU;IAClB,MAAM,OAAO,MAAM,OAAO,KAAK;IAC/B,OAAO,UAAU,UAAU,SAAS,SAAS,SAAS,gBAAgB,MAAM,iBAAiB,MAAM,OAAO,QAAQ;GACpH;GAEA,qBAAqB,KAAK,OAAO,SAAS,gBAAgB,YAAY;EACxE;CACF;AACF,CAAC;AAED,MAAa,qBAAqB,KAAK,OAAO;CAC5C,MAAM;CAEN,UAAU;CACV,SAAS;CACT,UAAU;CACV,cAAc;CACd,WAAW,EAAE,SAAS,eAAe;CAErC,iBAAiB,CACf;EAAE,KAAK;EAAmC,UAAU;CAAG,GACvD;EAAE,KAAK;EAAiC,UAAU;CAAG,CACvD;CACA,aAAa,EAAE,qBAAqB;EAAC;EAAM,gBAAgB,gBAAgB;GAAE,aAAa;GAAmB,OAAO;EAAkB,CAAC;EAAG;CAAC;CAC3I,iBAAiB,MAAM,MAAM,MAAM,EAAE,eAAe,KAAK,WAAW,CAAC,CAAC;CACtE,uBAAuB;EACrB,OAAO,EAGL,mBAAmB,KAAK,OAAO,SAAS,cAAc,EACxD;CACF;AACF,CAAC;AAED,MAAa,2BAA2B,KAAK,OAAO;CAClD,MAAM;CACN,SAAS;CACT,UAAU;CACV,cAAc;CACd,WAAW,EAAE,SAAS,eAAe;CAErC,iBAAiB,CACf;EAAE,KAAK;EAAwC,UAAU;CAAG,GAC5D;EAAE,KAAK;EAAkC,UAAU;CAAG,CACxD;CACA,aAAa,EAAE,qBAAqB;EAAC;EAAK,gBAAgB,gBAAgB;GAAE,aAAa;GAAyB,OAAO;EAAwB,CAAC;EAAG;CAAC;CACtJ,iBAAiB,MAAM,MAAM;EAC3B,MAAM,OAAO,EAAE,eAAe,KAAK,WAAW,CAAC,CAAC;EAChD,OAAO,OAAO,KAAK,SAAS;CAC9B;AACF,CAAC;AAED,MAAa,oBAAoB,KAAK,OAAO;CAC3C,MAAM;CACN,MAAM;CACN,YAAY;CACZ,WAAW;CACX,gBAAgB;CAChB,cAAc;CACd,WAAW,EAAE,SAAS;EAAC,GAAG;EAAgB,QAAQ;EAAa,QAAQ;CAAc,EAAE;CACvF,sBAAsB,EACpB,MAAM;EACJ,SAAS,CAAC;EACV,YAAY,OAAO;GACjB,IAAI;IACF,MAAM,QAAiB,KAAK,MAAM,GAAG,aAAa,WAAW,KAAK,IAAI;IACtE,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAO,QAAO,OAAO,QAAQ,QAAQ,IAAI,CAAC;GAChF,QACM;IACJ,OAAO,CAAC;GACV;EACF;EACA,aAAY,WAAU,EAAE,aAAa,KAAK,UAAU,MAAM,IAAI,EAAE;CAClE,EACF;CAEA,iBAAiB,CAAC,EAAE,KAAK,oCAAkC,GAAG,EAAE,KAAK,kCAAgC,CAAC;CACtG,aAAa,EAAE,qBAAqB,CAAC,OAAO,gBAAgB,gBAAgB;EAAE,aAAa;EAAkB,OAAO;CAAiB,CAAC,CAAC;CACvI,iBAAiB,SAAS;EACxB,MAAM,OAAgB,KAAK,OAAO;EAClC,OAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,SAAS,KAAK,KAAI,QAAO,IAAI,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM;CAChG;AACF,CAAC"}
|
|
@@ -13,6 +13,10 @@ export interface OutlineNode {
|
|
|
13
13
|
title: string;
|
|
14
14
|
/** The composite has a description (the summary). */
|
|
15
15
|
hasSummary: boolean;
|
|
16
|
+
/** Basket id (`meta.basket`), `null` without one. */
|
|
17
|
+
basket: string | null;
|
|
18
|
+
/** The title is hidden in the editor (`meta.titleHidden`). */
|
|
19
|
+
titleHidden: boolean;
|
|
16
20
|
children: OutlineNode[];
|
|
17
21
|
}
|
|
18
22
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { blockIdOf, findBlockById } from "../../core/schema/block-ids.js";
|
|
2
2
|
import { kindAccepts } from "./kinds.js";
|
|
3
|
+
import { basketOf, titleHiddenOf } from "./baskets.js";
|
|
3
4
|
import { compositeKindsOf } from "./nodes.js";
|
|
4
5
|
import { nestingReason, outermostComposites } from "./accepted-children.js";
|
|
5
6
|
//#region src/features/composites/outline.ts
|
|
@@ -28,6 +29,8 @@ function compositeOutline(doc, hiddenKinds, untitled) {
|
|
|
28
29
|
kind,
|
|
29
30
|
title: node.firstChild?.textContent.trim() || untitled,
|
|
30
31
|
hasSummary: (node.maybeChild(1)?.content.size ?? 0) > 0,
|
|
32
|
+
basket: basketOf(node),
|
|
33
|
+
titleHidden: titleHiddenOf(node),
|
|
31
34
|
children
|
|
32
35
|
});
|
|
33
36
|
} else if (!node.isTextblock && !node.isLeaf) collect(node, pos + 1, into);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outline.js","names":[],"sources":["../../../src/features/composites/outline.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf, findBlockById } from '../../core/schema'\nimport type { Translate } from '../../i18n'\nimport { nestingReason, outermostComposites } from './accepted-children'\nimport { kindAccepts } from './kinds'\nimport { compositeKindsOf } from './nodes'\n\n/** One Baustein of a document's outline. */\nexport interface OutlineNode {\n /** Block ID, or `pos:<pos>` for a block without one yet; unique within the document. */\n key: string\n id: string | null\n /** Position of the composite in the document it was read from. */\n pos: number\n kind: string\n title: string\n /** The composite has a description (the summary). */\n hasSummary: boolean\n children: OutlineNode[]\n}\n\n/**\n * The composites of `doc` as a tree (title, kind, summary flag). A composite without a title gets `untitled` (the\n * caller's translated „(ohne Titel)“). Composites of a kind in `hiddenKinds` are omitted and their visible descendants\n * take their place in the parent's list.\n */\nexport function compositeOutline(doc: PMNode, hiddenKinds: ReadonlySet<string>, untitled: string): OutlineNode[] {\n function collect(parent: PMNode, start: number, into: OutlineNode[]) {\n parent.forEach((node, offset) => {\n const pos = start + offset\n if (node.type.name === 'composite') {\n const kind = String(node.attrs.kind)\n const children: OutlineNode[] = []\n collect(node, pos + 1, children)\n if (hiddenKinds.has(kind)) {\n into.push(...children)\n return\n }\n const id = blockIdOf(node)\n into.push({\n key: id ?? `pos:${pos}`,\n id,\n pos,\n kind,\n title: node.firstChild?.textContent.trim() || untitled,\n hasSummary: (node.maybeChild(1)?.content.size ?? 0) > 0,\n children,\n })\n }\n else if (!node.isTextblock && !node.isLeaf) {\n // Composites can sit in quotes or lists as well.\n collect(node, pos + 1, into)\n }\n })\n }\n const roots: OutlineNode[] = []\n collect(doc, 0, roots)\n return roots\n}\n\n/** Whether a block may move into a Baustein; `reason` is a short sentence for the UI (in the language of the `t` it was judged with) when it may not. */\nexport type MoveVerdict = { ok: true } | { ok: false, reason: string }\n\nconst refused = (reason: string): MoveVerdict => ({ ok: false, reason })\n\n/**\n * Whether block `id` may move into the Baustein `targetId` (`null` = the end of the document), with the reason when\n * not: a block is missing, the target is no Baustein, the target is the block itself or inside it, or the target's kind\n * does not accept the block's kind (`CompositeKind.accepts`). A block that is no Baustein may move into any Baustein,\n * unless it holds Bausteine of a refused kind; Bausteine nested inside a moved Baustein stay with it and are not\n * checked. Moving a block to the end of the Baustein it already sits in adds no nesting and is never refused for it.\n */\nexport function moveIntoVerdict(editor: Editor, id: string, targetId: string | null, t: Translate): MoveVerdict {\n const { doc } = editor.state\n const block = findBlockById(doc, id)\n if (!block) return refused(t('composites.move.blockGone'))\n if (targetId === null) return { ok: true }\n const target = findBlockById(doc, targetId)\n if (!target) return refused(t('composites.move.targetGone'))\n if (target.node.type.name !== 'composite') return refused(t('composites.move.targetNotBlock'))\n if (target.pos === block.pos) return refused(t('composites.move.intoSelf'))\n if (target.pos > block.pos && target.pos < block.pos + block.node.nodeSize) return refused(t('composites.move.intoOwn'))\n\n // The nearest Baustein around the block: when it is the target, the block only moves within it.\n const $block = doc.resolve(block.pos)\n let parentPos = -1\n for (let depth = $block.depth; depth > 0; depth--) {\n if ($block.node(depth).type.name === 'composite') {\n parentPos = $block.before(depth)\n break\n }\n }\n if (parentPos === target.pos) return { ok: true }\n\n const kinds = compositeKindsOf(editor)\n const targetKind = String(target.node.attrs.kind)\n for (const child of outermostComposites(block.node)) {\n const childKind = String(child.attrs.kind)\n if (!kindAccepts(kinds, targetKind, childKind)) return refused(nestingReason(kinds, targetKind, childKind, t))\n }\n return { ok: true }\n}\n\n/** `canMoveBlockInto` reads only `ok`, so its verdict needs no text: the message keys stand in for the sentences. */\nconst untranslated: Translate = key => key\n\n/** Whether block `id` can move into the Baustein `targetId` (`null` = the end of the document); see `moveIntoVerdict`. */\nexport function canMoveBlockInto(editor: Editor, id: string, targetId: string | null): boolean {\n return moveIntoVerdict(editor, id, targetId, untranslated).ok\n}\n\n/**\n * Moves block `id` to the end of the body of the Baustein `targetId` (`null` = the end of the document) in a single\n * transaction, i.e. one undo step. `false` when nothing moved: `moveIntoVerdict` refuses the move (or the editor\n * refused the transaction).\n */\nexport function moveBlockInto(editor: Editor, id: string, targetId: string | null): boolean {\n if (!canMoveBlockInto(editor, id, targetId)) return false\n const { doc, tr } = editor.state\n const block = findBlockById(doc, id)!\n const target = targetId === null ? null : findBlockById(doc, targetId)!\n // A composite's body is its last children, so its end is just before the composite's closing token.\n const insertPos = target ? target.pos + target.node.nodeSize - 1 : doc.content.size\n tr.delete(block.pos, block.pos + block.node.nodeSize)\n tr.insert(tr.mapping.map(insertPos), block.node)\n editor.view.dispatch(tr.scrollIntoView())\n return editor.state.doc !== doc\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"outline.js","names":[],"sources":["../../../src/features/composites/outline.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport { blockIdOf, findBlockById } from '../../core/schema'\nimport type { Translate } from '../../i18n'\nimport { nestingReason, outermostComposites } from './accepted-children'\nimport { basketOf, titleHiddenOf } from './baskets'\nimport { kindAccepts } from './kinds'\nimport { compositeKindsOf } from './nodes'\n\n/** One Baustein of a document's outline. */\nexport interface OutlineNode {\n /** Block ID, or `pos:<pos>` for a block without one yet; unique within the document. */\n key: string\n id: string | null\n /** Position of the composite in the document it was read from. */\n pos: number\n kind: string\n title: string\n /** The composite has a description (the summary). */\n hasSummary: boolean\n /** Basket id (`meta.basket`), `null` without one. */\n basket: string | null\n /** The title is hidden in the editor (`meta.titleHidden`). */\n titleHidden: boolean\n children: OutlineNode[]\n}\n\n/**\n * The composites of `doc` as a tree (title, kind, summary flag). A composite without a title gets `untitled` (the\n * caller's translated „(ohne Titel)“). Composites of a kind in `hiddenKinds` are omitted and their visible descendants\n * take their place in the parent's list.\n */\nexport function compositeOutline(doc: PMNode, hiddenKinds: ReadonlySet<string>, untitled: string): OutlineNode[] {\n function collect(parent: PMNode, start: number, into: OutlineNode[]) {\n parent.forEach((node, offset) => {\n const pos = start + offset\n if (node.type.name === 'composite') {\n const kind = String(node.attrs.kind)\n const children: OutlineNode[] = []\n collect(node, pos + 1, children)\n if (hiddenKinds.has(kind)) {\n into.push(...children)\n return\n }\n const id = blockIdOf(node)\n into.push({\n key: id ?? `pos:${pos}`,\n id,\n pos,\n kind,\n title: node.firstChild?.textContent.trim() || untitled,\n hasSummary: (node.maybeChild(1)?.content.size ?? 0) > 0,\n basket: basketOf(node),\n titleHidden: titleHiddenOf(node),\n children,\n })\n }\n else if (!node.isTextblock && !node.isLeaf) {\n // Composites can sit in quotes or lists as well.\n collect(node, pos + 1, into)\n }\n })\n }\n const roots: OutlineNode[] = []\n collect(doc, 0, roots)\n return roots\n}\n\n/** Whether a block may move into a Baustein; `reason` is a short sentence for the UI (in the language of the `t` it was judged with) when it may not. */\nexport type MoveVerdict = { ok: true } | { ok: false, reason: string }\n\nconst refused = (reason: string): MoveVerdict => ({ ok: false, reason })\n\n/**\n * Whether block `id` may move into the Baustein `targetId` (`null` = the end of the document), with the reason when\n * not: a block is missing, the target is no Baustein, the target is the block itself or inside it, or the target's kind\n * does not accept the block's kind (`CompositeKind.accepts`). A block that is no Baustein may move into any Baustein,\n * unless it holds Bausteine of a refused kind; Bausteine nested inside a moved Baustein stay with it and are not\n * checked. Moving a block to the end of the Baustein it already sits in adds no nesting and is never refused for it.\n */\nexport function moveIntoVerdict(editor: Editor, id: string, targetId: string | null, t: Translate): MoveVerdict {\n const { doc } = editor.state\n const block = findBlockById(doc, id)\n if (!block) return refused(t('composites.move.blockGone'))\n if (targetId === null) return { ok: true }\n const target = findBlockById(doc, targetId)\n if (!target) return refused(t('composites.move.targetGone'))\n if (target.node.type.name !== 'composite') return refused(t('composites.move.targetNotBlock'))\n if (target.pos === block.pos) return refused(t('composites.move.intoSelf'))\n if (target.pos > block.pos && target.pos < block.pos + block.node.nodeSize) return refused(t('composites.move.intoOwn'))\n\n // The nearest Baustein around the block: when it is the target, the block only moves within it.\n const $block = doc.resolve(block.pos)\n let parentPos = -1\n for (let depth = $block.depth; depth > 0; depth--) {\n if ($block.node(depth).type.name === 'composite') {\n parentPos = $block.before(depth)\n break\n }\n }\n if (parentPos === target.pos) return { ok: true }\n\n const kinds = compositeKindsOf(editor)\n const targetKind = String(target.node.attrs.kind)\n for (const child of outermostComposites(block.node)) {\n const childKind = String(child.attrs.kind)\n if (!kindAccepts(kinds, targetKind, childKind)) return refused(nestingReason(kinds, targetKind, childKind, t))\n }\n return { ok: true }\n}\n\n/** `canMoveBlockInto` reads only `ok`, so its verdict needs no text: the message keys stand in for the sentences. */\nconst untranslated: Translate = key => key\n\n/** Whether block `id` can move into the Baustein `targetId` (`null` = the end of the document); see `moveIntoVerdict`. */\nexport function canMoveBlockInto(editor: Editor, id: string, targetId: string | null): boolean {\n return moveIntoVerdict(editor, id, targetId, untranslated).ok\n}\n\n/**\n * Moves block `id` to the end of the body of the Baustein `targetId` (`null` = the end of the document) in a single\n * transaction, i.e. one undo step. `false` when nothing moved: `moveIntoVerdict` refuses the move (or the editor\n * refused the transaction).\n */\nexport function moveBlockInto(editor: Editor, id: string, targetId: string | null): boolean {\n if (!canMoveBlockInto(editor, id, targetId)) return false\n const { doc, tr } = editor.state\n const block = findBlockById(doc, id)!\n const target = targetId === null ? null : findBlockById(doc, targetId)!\n // A composite's body is its last children, so its end is just before the composite's closing token.\n const insertPos = target ? target.pos + target.node.nodeSize - 1 : doc.content.size\n tr.delete(block.pos, block.pos + block.node.nodeSize)\n tr.insert(tr.mapping.map(insertPos), block.node)\n editor.view.dispatch(tr.scrollIntoView())\n return editor.state.doc !== doc\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { basketOf } from "../baskets.js";
|
|
2
|
+
import { compositeBasketsOf } from "../nodes.js";
|
|
3
|
+
import { useEditorI18n } from "../../../react/use-editor-i18n.js";
|
|
4
|
+
import { Icon } from "../../../react/Icon.js";
|
|
5
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../../../react/ui/popover.js";
|
|
6
|
+
import { compositeOfRow } from "../rows.js";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/features/composites/react/CompositeBasketMenu.tsx
|
|
10
|
+
const OPTION = "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-foreground transition-colors duration-150 hover:bg-muted focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary";
|
|
11
|
+
/**
|
|
12
|
+
* Basket choice of a Baustein: the child (a button) opens a list of the editor's baskets (a dot in the basket's colour
|
|
13
|
+
* and its label) and „Kein Korb“. The menu is a popover, not a dropdown menu: a dropdown item cannot show a dot in an
|
|
14
|
+
* arbitrary CSS colour.
|
|
15
|
+
*/
|
|
16
|
+
function CompositeBasketMenu({ editor, row, children }) {
|
|
17
|
+
const { t } = useEditorI18n();
|
|
18
|
+
const [open, setOpen] = useState(false);
|
|
19
|
+
const current = basketOf(row);
|
|
20
|
+
function choose(next) {
|
|
21
|
+
setOpen(false);
|
|
22
|
+
const found = compositeOfRow(editor.state.doc, row);
|
|
23
|
+
if (found) editor.commands.setCompositeBasket(next, found.pos);
|
|
24
|
+
}
|
|
25
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
26
|
+
open,
|
|
27
|
+
onOpenChange: setOpen,
|
|
28
|
+
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
29
|
+
asChild: true,
|
|
30
|
+
children
|
|
31
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
32
|
+
side: "bottom",
|
|
33
|
+
align: "start",
|
|
34
|
+
sideOffset: 4,
|
|
35
|
+
className: "w-40 gap-0 p-1",
|
|
36
|
+
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
37
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
38
|
+
role: "menu",
|
|
39
|
+
"aria-label": t("composites.basket.label"),
|
|
40
|
+
children: [compositeBasketsOf(editor).map((candidate) => /* @__PURE__ */ jsxs("button", {
|
|
41
|
+
type: "button",
|
|
42
|
+
role: "menuitemradio",
|
|
43
|
+
"aria-checked": current === candidate.id,
|
|
44
|
+
className: OPTION,
|
|
45
|
+
onClick: () => choose(candidate.id),
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ jsx("span", {
|
|
48
|
+
className: "size-2.5 shrink-0 rounded-full",
|
|
49
|
+
style: { backgroundColor: candidate.color }
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ jsx("span", {
|
|
52
|
+
className: "truncate",
|
|
53
|
+
children: candidate.label
|
|
54
|
+
}),
|
|
55
|
+
current === candidate.id && /* @__PURE__ */ jsx(Icon, {
|
|
56
|
+
name: "i-lucide-check",
|
|
57
|
+
className: "ml-auto size-3.5 shrink-0"
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
}, candidate.id)), /* @__PURE__ */ jsxs("button", {
|
|
61
|
+
type: "button",
|
|
62
|
+
role: "menuitemradio",
|
|
63
|
+
"aria-checked": current === null,
|
|
64
|
+
className: OPTION,
|
|
65
|
+
onClick: () => choose(null),
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
68
|
+
name: "i-lucide-circle-dashed",
|
|
69
|
+
className: "size-3 shrink-0 text-muted-foreground"
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ jsx("span", {
|
|
72
|
+
className: "truncate",
|
|
73
|
+
children: t("composites.basket.none")
|
|
74
|
+
}),
|
|
75
|
+
current === null && /* @__PURE__ */ jsx(Icon, {
|
|
76
|
+
name: "i-lucide-check",
|
|
77
|
+
className: "ml-auto size-3.5 shrink-0"
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
})]
|
|
81
|
+
})
|
|
82
|
+
})]
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { CompositeBasketMenu };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=CompositeBasketMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CompositeBasketMenu.js","names":[],"sources":["../../../../src/features/composites/react/CompositeBasketMenu.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type ReactNode, useState } from 'react'\nimport { Icon } from '../../../react/Icon'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../react/ui/popover'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { basketOf } from '../baskets'\nimport { compositeBasketsOf } from '../nodes'\nimport { compositeOfRow, type CompositeRow } from '../rows'\n\nconst OPTION = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-foreground transition-colors duration-150 hover:bg-muted focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary'\n\n/**\n * Basket choice of a Baustein: the child (a button) opens a list of the editor's baskets (a dot in the basket's colour\n * and its label) and „Kein Korb“. The menu is a popover, not a dropdown menu: a dropdown item cannot show a dot in an\n * arbitrary CSS colour.\n */\nexport function CompositeBasketMenu({ editor, row, children }: { editor: Editor, row: CompositeRow, children: ReactNode }) {\n const { t } = useEditorI18n()\n const [open, setOpen] = useState(false)\n\n const current = basketOf(row)\n\n function choose(next: string | null) {\n setOpen(false)\n const found = compositeOfRow(editor.state.doc, row)\n if (found) editor.commands.setCompositeBasket(next, found.pos)\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={4}\n className=\"w-40 gap-0 p-1\"\n onCloseAutoFocus={event => event.preventDefault()}\n >\n <div role=\"menu\" aria-label={t('composites.basket.label')}>\n {compositeBasketsOf(editor).map(candidate => (\n <button\n key={candidate.id}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={current === candidate.id}\n className={OPTION}\n onClick={() => choose(candidate.id)}\n >\n <span className=\"size-2.5 shrink-0 rounded-full\" style={{ backgroundColor: candidate.color }} />\n <span className=\"truncate\">{candidate.label}</span>\n {current === candidate.id && <Icon name=\"i-lucide-check\" className=\"ml-auto size-3.5 shrink-0\" />}\n </button>\n ))}\n <button type=\"button\" role=\"menuitemradio\" aria-checked={current === null} className={OPTION} onClick={() => choose(null)}>\n <Icon name=\"i-lucide-circle-dashed\" className=\"size-3 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{t('composites.basket.none')}</span>\n {current === null && <Icon name=\"i-lucide-check\" className=\"ml-auto size-3.5 shrink-0\" />}\n </button>\n </div>\n </PopoverContent>\n </Popover>\n )\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,SAAS;;;;;;AAOf,SAAgB,oBAAoB,EAAE,QAAQ,KAAK,YAAwE;CACzH,MAAM,EAAE,MAAM,cAAc;CAC5B,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CAEtC,MAAM,UAAU,SAAS,GAAG;CAE5B,SAAS,OAAO,MAAqB;EACnC,QAAQ,KAAK;EACb,MAAM,QAAQ,eAAe,OAAO,MAAM,KAAK,GAAG;EAClD,IAAI,OAAO,OAAO,SAAS,mBAAmB,MAAM,MAAM,GAAG;CAC/D;CAEA,OACE,qBAAC,SAAD;EAAe;EAAM,cAAc;EAAnC,UAAA,CACE,oBAAC,gBAAD;GAAgB,SAAA;GAAS;EAAyB,CAAA,GAClD,oBAAC,gBAAD;GACE,MAAK;GACL,OAAM;GACN,YAAY;GACZ,WAAU;GACV,mBAAkB,UAAS,MAAM,eAAe;GAEhD,UAAA,qBAAC,OAAD;IAAK,MAAK;IAAO,cAAY,EAAE,yBAAyB;IAAxD,UAAA,CACG,mBAAmB,MAAM,CAAC,CAAC,KAAI,cAC9B,qBAAC,UAAD;KAEE,MAAK;KACL,MAAK;KACL,gBAAc,YAAY,UAAU;KACpC,WAAW;KACX,eAAe,OAAO,UAAU,EAAE;KANpC,UAAA;MAQE,oBAAC,QAAD;OAAM,WAAU;OAAiC,OAAO,EAAE,iBAAiB,UAAU,MAAM;MAAI,CAAA;MAC/F,oBAAC,QAAD;OAAM,WAAU;OAAY,UAAA,UAAU;MAAY,CAAA;MACjD,YAAY,UAAU,MAAM,oBAAC,MAAD;OAAM,MAAK;OAAiB,WAAU;MAA6B,CAAA;KAC1F;IAVD,GAAA,UAAU,EAUT,CACT,GACD,qBAAC,UAAD;KAAQ,MAAK;KAAS,MAAK;KAAgB,gBAAc,YAAY;KAAM,WAAW;KAAQ,eAAe,OAAO,IAAI;KAAxH,UAAA;MACE,oBAAC,MAAD;OAAM,MAAK;OAAyB,WAAU;MAAyC,CAAA;MACvF,oBAAC,QAAD;OAAM,WAAU;OAAY,UAAA,EAAE,wBAAwB;MAAQ,CAAA;MAC7D,YAAY,QAAQ,oBAAC,MAAD;OAAM,MAAK;OAAiB,WAAU;MAA6B,CAAA;KAClF;IACL,CAAA,CAAA;;EACS,CAAA,CACT;;AAEb"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { startBlockDrag } from "../../../core/block-drag.js";
|
|
2
|
+
import { basketById, basketOf, titleHiddenOf } from "../baskets.js";
|
|
3
|
+
import { compositeBasketsOf } from "../nodes.js";
|
|
2
4
|
import { useEditorI18n } from "../../../react/use-editor-i18n.js";
|
|
3
5
|
import { cn } from "../../../react/ui/utils.js";
|
|
4
6
|
import { Icon } from "../../../react/Icon.js";
|
|
@@ -9,6 +11,7 @@ import { compositeOfRow, setCompositeTags } from "../rows.js";
|
|
|
9
11
|
import { compositeSectionOf } from "../anchors.js";
|
|
10
12
|
import { derivedChromeOf } from "../derived-chrome.js";
|
|
11
13
|
import { rowStatus } from "../meta-display.js";
|
|
14
|
+
import { CompositeBasketMenu } from "./CompositeBasketMenu.js";
|
|
12
15
|
import { useCompositeRows } from "./use-composite-rows.js";
|
|
13
16
|
import { CompositeDetails } from "./CompositeDetails.js";
|
|
14
17
|
import { CompositeStatusMenu } from "./CompositeStatusMenu.js";
|
|
@@ -81,7 +84,9 @@ function CompositeChrome({ editor }) {
|
|
|
81
84
|
if (!found || !startBlockDrag(editor, event, found.pos)) event.preventDefault();
|
|
82
85
|
}
|
|
83
86
|
const expanded = useStore(editor.storage.composite.expanded);
|
|
87
|
+
const revealedTitles = useStore(editor.storage.composite.revealedTitles);
|
|
84
88
|
const placed = useMemo(() => {
|
|
89
|
+
const baskets = compositeBasketsOf(editor);
|
|
85
90
|
let hiddenUntil = -1;
|
|
86
91
|
return rows.flatMap((row) => {
|
|
87
92
|
if (row.pos < hiddenUntil) return [];
|
|
@@ -90,19 +95,23 @@ function CompositeChrome({ editor }) {
|
|
|
90
95
|
if (!open) hiddenUntil = row.end;
|
|
91
96
|
const anchor = anchors.get(row.key);
|
|
92
97
|
if (!anchor) return [];
|
|
93
|
-
const
|
|
98
|
+
const titleHidden = titleHiddenOf(row);
|
|
99
|
+
const inStrip = titleHidden && !(row.id && revealedTitles.has(row.id)) && anchor.toggleSize > 0;
|
|
100
|
+
const left = inStrip ? anchor.toggleLeft + anchor.toggleSize + 6 : anchor.lineEnd + (anchor.titleEmpty ? 116 : 12);
|
|
94
101
|
return [{
|
|
95
102
|
row,
|
|
96
103
|
anchor,
|
|
97
104
|
left,
|
|
98
|
-
top: anchor.lastCenter - ROW_HEIGHT / 2,
|
|
105
|
+
top: (inStrip ? anchor.toggleTop + anchor.toggleSize / 2 : anchor.lastCenter) - ROW_HEIGHT / 2,
|
|
99
106
|
width: Math.max((frame?.right ?? anchor.right) - left, 120),
|
|
100
107
|
status: rowStatus(row, t),
|
|
101
108
|
derived: derived && {
|
|
102
109
|
stale: derived.stale,
|
|
103
110
|
open,
|
|
104
111
|
toggleable: !!row.id
|
|
105
|
-
}
|
|
112
|
+
},
|
|
113
|
+
basket: basketById(baskets, basketOf(row)) ?? null,
|
|
114
|
+
titleHidden
|
|
106
115
|
}];
|
|
107
116
|
});
|
|
108
117
|
}, [
|
|
@@ -111,6 +120,7 @@ function CompositeChrome({ editor }) {
|
|
|
111
120
|
anchors,
|
|
112
121
|
frame,
|
|
113
122
|
expanded,
|
|
123
|
+
revealedTitles,
|
|
114
124
|
t
|
|
115
125
|
]);
|
|
116
126
|
function toggleExpanded(row) {
|
|
@@ -119,6 +129,10 @@ function CompositeChrome({ editor }) {
|
|
|
119
129
|
function removeTag(row, tag) {
|
|
120
130
|
setCompositeTags(editor, row, row.tags.filter((candidate) => candidate !== tag));
|
|
121
131
|
}
|
|
132
|
+
function toggleTitleHidden(row) {
|
|
133
|
+
const found = compositeOfRow(editor.state.doc, row);
|
|
134
|
+
if (found) editor.commands.setCompositeTitleHidden(!titleHiddenOf(row), found.pos);
|
|
135
|
+
}
|
|
122
136
|
if (!host) return null;
|
|
123
137
|
return createPortal(/* @__PURE__ */ jsx("div", {
|
|
124
138
|
className: "absolute top-0 left-0 h-0 w-0",
|
|
@@ -158,7 +172,7 @@ function CompositeChrome({ editor }) {
|
|
|
158
172
|
row: item.row,
|
|
159
173
|
editable
|
|
160
174
|
})
|
|
161
|
-
})] }), (item.status || item.derived || item.row.tagsVisible &&
|
|
175
|
+
})] }), (editable || item.basket || item.status || item.derived || item.row.tagsVisible && item.row.tags.length > 0) && /* @__PURE__ */ jsxs("div", {
|
|
162
176
|
className: "absolute z-10 flex items-center gap-px overflow-hidden rounded-md bg-background",
|
|
163
177
|
style: {
|
|
164
178
|
top: `${item.top}px`,
|
|
@@ -169,6 +183,40 @@ function CompositeChrome({ editor }) {
|
|
|
169
183
|
onMouseEnter: () => setChipsHoveredKey(item.row.key),
|
|
170
184
|
onMouseLeave: () => setChipsHoveredKey(null),
|
|
171
185
|
children: [
|
|
186
|
+
editable ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CompositeBasketMenu, {
|
|
187
|
+
editor,
|
|
188
|
+
row: item.row,
|
|
189
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
190
|
+
type: "button",
|
|
191
|
+
className: CHIP_BUTTON,
|
|
192
|
+
"aria-label": t("composites.basket.label"),
|
|
193
|
+
title: item.basket ? `${t("composites.basket.label")}: ${item.basket.label}` : t("composites.basket.label"),
|
|
194
|
+
children: item.basket ? /* @__PURE__ */ jsx("span", {
|
|
195
|
+
className: "size-2.5 rounded-full",
|
|
196
|
+
style: { backgroundColor: item.basket.color }
|
|
197
|
+
}) : /* @__PURE__ */ jsx(Icon, {
|
|
198
|
+
name: "i-lucide-circle-dashed",
|
|
199
|
+
className: "size-3.5"
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
203
|
+
type: "button",
|
|
204
|
+
className: CHIP_BUTTON,
|
|
205
|
+
"aria-label": t(item.titleHidden ? "composites.titleHidden.show" : "composites.titleHidden.hide"),
|
|
206
|
+
title: t(item.titleHidden ? "composites.titleHidden.show" : "composites.titleHidden.hide"),
|
|
207
|
+
onClick: () => toggleTitleHidden(item.row),
|
|
208
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
209
|
+
name: item.titleHidden ? "i-lucide-eye" : "i-lucide-eye-off",
|
|
210
|
+
className: "size-3.5"
|
|
211
|
+
})
|
|
212
|
+
})] }) : item.basket && /* @__PURE__ */ jsx("span", {
|
|
213
|
+
className: CHIP,
|
|
214
|
+
title: item.basket.label,
|
|
215
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
216
|
+
className: "size-2.5 rounded-full",
|
|
217
|
+
style: { backgroundColor: item.basket.color }
|
|
218
|
+
})
|
|
219
|
+
}),
|
|
172
220
|
item.derived && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
173
221
|
item.derived.toggleable && /* @__PURE__ */ jsx("button", {
|
|
174
222
|
type: "button",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompositeChrome.js","names":[],"sources":["../../../../src/features/composites/react/CompositeChrome.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Fragment, type KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { startBlockDrag } from '../../../core'\nimport { Icon } from '../../../react/Icon'\nimport { useStore } from '../../../react/use-store'\nimport { useEditorSettings } from '../../../react/settings'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../react/ui/popover'\nimport { cn } from '../../../react/ui/utils'\nimport { compositeSectionOf } from '../anchors'\nimport { derivedChromeOf } from '../derived-chrome'\nimport { rowStatus } from '../meta-display'\nimport { compositeOfRow, type CompositeRow, setCompositeTags } from '../rows'\nimport { CompositeDetails } from './CompositeDetails'\nimport { CompositeStatusMenu } from './CompositeStatusMenu'\nimport { CompositeTagsEditor } from './CompositeTagsEditor'\nimport { useCompositeAnchors } from './use-composite-anchors'\nimport { useCompositeRows } from './use-composite-rows'\n\n/**\n * Chrome of every composite, laid over the editor (mounted by the composites feature): on the title line the status\n * chip and one chip per tag (with a `+` chip to add tags), and on the front the kind icon. The icon is a button that\n * opens the details popover (kind with a confirmation before it changes, the kind's fields and the summary;\n * `CompositeDetails`) and, in an editable editor, the composite's drag handle: dragging it moves the composite\n * (`startBlockDrag`). With `blocks.kindIcon: 'hover'` it shows only while the composite is hovered, its popover is\n * open or the icon has keyboard focus. Chips and popover also render in read-only editors, without edit affordances.\n * The node view's own description and tags rows are hidden by CSS; this overlay shows and edits them.\n */\nexport function CompositeChrome({ editor }: { editor: Editor }) {\n const { rows } = useCompositeRows(editor)\n const { host, anchors, frame } = useCompositeAnchors(editor, rows)\n\n // `isEditable` is not reactive: follow it on every transaction (`EditorSurface` dispatches one after `setEditable`).\n const [editable, setEditable] = useState(() => !editor.isDestroyed && editor.isEditable)\n useEffect(() => {\n const sync = () => setEditable(!editor.isDestroyed && editor.isEditable)\n sync()\n editor.on('transaction', sync)\n return () => {\n editor.off('transaction', sync)\n }\n }, [editor])\n\n const { settings } = useEditorSettings()\n const { t } = useEditorI18n()\n const iconOnHover = settings.blocks.kindIcon === 'hover'\n\n // Hover state of `kindIcon: 'hover'`: the keys of the rows whose composite is under the pointer (that one and every\n // composite around it), plus the row whose chips the pointer is on (they sit outside the editor's DOM).\n const [hoveredKeys, setHoveredKeys] = useState<ReadonlySet<string>>(NO_KEYS)\n const [chipsHoveredKey, setChipsHoveredKey] = useState<string | null>(null)\n const latestRows = useRef(rows)\n latestRows.current = rows\n\n useEffect(() => {\n let hoveredSection: Element | null = null\n const onMouseleave = () => {\n hoveredSection = null\n setHoveredKeys(NO_KEYS)\n }\n const onMouseover = (event: MouseEvent) => {\n const section = event.target instanceof Element ? event.target.closest('.composite') : null\n if (section === hoveredSection) return\n hoveredSection = section\n const chain = new Set<Element>()\n for (let el = section; el; el = el.parentElement?.closest('.composite') ?? null) chain.add(el)\n const { view, state } = editor\n setHoveredKeys(new Set(latestRows.current.filter((row) => {\n const found = compositeOfRow(state.doc, row)\n const dom = found ? view.nodeDOM(found.pos) : null\n const section = dom instanceof Element ? compositeSectionOf(dom) : null\n return section !== null && chain.has(section)\n }).map(row => row.key)))\n }\n\n onMouseleave()\n if (!iconOnHover || editor.isDestroyed) return\n const { dom } = editor.view\n dom.addEventListener('mouseover', onMouseover)\n dom.addEventListener('mouseleave', onMouseleave)\n return () => {\n dom.removeEventListener('mouseover', onMouseover)\n dom.removeEventListener('mouseleave', onMouseleave)\n }\n }, [editor, iconOnHover])\n\n function onIconDragStart(event: DragEvent, row: CompositeRow) {\n const found = compositeOfRow(editor.state.doc, row)\n if (!found || !startBlockDrag(editor, event, found.pos)) event.preventDefault()\n }\n\n const expanded = useStore(editor.storage.composite.expanded)\n\n const placed = useMemo(() => {\n // End of the collapsed derived Baustein the rows being walked sit in: its clipped content shows no chrome.\n let hiddenUntil = -1\n return rows.flatMap((row) => {\n if (row.pos < hiddenUntil) return []\n const derived = derivedChromeOf(editor, row)\n const open = !derived || !row.id || expanded.has(row.id)\n if (!open) hiddenUntil = row.end\n const anchor = anchors.get(row.key)\n if (!anchor) return []\n // An empty title shows a placeholder the anchor cannot measure: keep clear of it.\n const left = anchor.lineEnd + (anchor.titleEmpty ? 116 : 12)\n return [{\n row,\n anchor,\n left,\n top: anchor.lastCenter - ROW_HEIGHT / 2,\n width: Math.max((frame?.right ?? anchor.right) - left, 120),\n status: rowStatus(row, t),\n derived: derived && { stale: derived.stale, open, toggleable: !!row.id },\n }]\n })\n }, [editor, rows, anchors, frame, expanded, t])\n\n function toggleExpanded(row: CompositeRow) {\n if (row.id) editor.commands.toggleCompositeExpanded(row.id)\n }\n\n function removeTag(row: CompositeRow, tag: string) {\n setCompositeTags(editor, row, row.tags.filter(candidate => candidate !== tag))\n }\n\n if (!host) return null\n\n return createPortal(\n <div className=\"absolute top-0 left-0 h-0 w-0\">\n {placed.map(item => (\n <Fragment key={item.row.key}>\n {item.anchor.toggleSize > 0 && (\n <Popover>\n <PopoverTrigger asChild>\n {/* A span, not a `button`: Firefox does not start drags from buttons. */}\n <span\n role=\"button\"\n tabIndex={0}\n draggable={editable}\n className={cn(\n 'absolute z-10 flex items-center justify-center rounded-sm bg-background text-muted-foreground transition-[color,background-color,opacity] duration-120 motion-reduce:transition-none hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary',\n editable ? 'cursor-grab active:cursor-grabbing' : 'cursor-pointer',\n iconOnHover && !hoveredKeys.has(item.row.key) && chipsHoveredKey !== item.row.key && 'opacity-0 hover:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100',\n )}\n style={{ top: `${item.anchor.toggleTop}px`, left: `${item.anchor.toggleLeft}px`, width: `${item.anchor.toggleSize}px`, height: `${item.anchor.toggleSize}px` }}\n aria-label={t('composites.chrome.details', { kind: item.row.kindLabel })}\n title={editable ? t('composites.chrome.detailsDrag', { kind: item.row.kindLabel }) : t('composites.chrome.details', { kind: item.row.kindLabel })}\n onKeyDown={(event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Enter' && event.key !== ' ') return\n event.preventDefault()\n event.currentTarget.click()\n }}\n onDragStart={event => onIconDragStart(event.nativeEvent, item.row)}\n >\n <Icon key={item.row.icon} name={item.row.icon} className=\"size-3.5\" />\n </span>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={4}\n className={DETAILS_POPOVER}\n >\n <CompositeDetails editor={editor} row={item.row} editable={editable} />\n </PopoverContent>\n </Popover>\n )}\n\n {(item.status || item.derived || (item.row.tagsVisible && (item.row.tags.length > 0 || editable))) && (\n <div\n className=\"absolute z-10 flex items-center gap-px overflow-hidden rounded-md bg-background\"\n style={{ top: `${item.top}px`, left: `${item.left}px`, maxWidth: `${item.width}px`, height: `${ROW_HEIGHT}px` }}\n onMouseEnter={() => setChipsHoveredKey(item.row.key)}\n onMouseLeave={() => setChipsHoveredKey(null)}\n >\n {item.derived && (\n <>\n {item.derived.toggleable && (\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-expanded={item.derived.open}\n aria-label={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n title={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n onClick={() => toggleExpanded(item.row)}\n >\n <Icon name={item.derived.open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'} className=\"size-3.5\" />\n </button>\n )}\n <span className={CHIP}>\n <Icon name=\"i-lucide-git-branch\" className=\"size-3.5\" />{t('composites.derivation.badge')}\n </span>\n {item.derived.stale && (\n <span className={cn(CHIP, 'text-(--sk-warning)')}>\n <Icon name=\"i-lucide-triangle-alert\" className=\"size-3.5\" />{t('composites.derivation.stale')}\n </span>\n )}\n </>\n )}\n\n {item.status && (editable\n ? (\n <CompositeStatusMenu editor={editor} row={item.row}>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.status.label', { status: item.status.label })}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </button>\n </CompositeStatusMenu>\n )\n : (\n <span className={CHIP}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </span>\n ))}\n\n {(item.row.tagsVisible ? item.row.tags : []).map(tag => (\n <span key={tag} className={cn(CHIP, 'group max-w-40 gap-0')}>\n <span className=\"truncate\">#{tag}</span>\n {editable && (\n <button\n type=\"button\"\n className=\"flex h-4 w-0 shrink-0 items-center justify-center overflow-hidden rounded-sm opacity-0 transition-all duration-150 group-hover:ml-1 group-hover:w-4 group-hover:opacity-100 hover:bg-muted hover:text-foreground focus-visible:ml-1 focus-visible:w-4 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-primary\"\n aria-label={t('composites.tag.remove', { tag })}\n onClick={() => removeTag(item.row, tag)}\n >\n <Icon name=\"i-lucide-x\" className=\"size-3\" />\n </button>\n )}\n </span>\n ))}\n\n {editable && item.row.tagsVisible && (\n <Popover>\n <PopoverTrigger asChild>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.tag.add')} title={t('composites.tag.add')}>\n <Icon name=\"i-lucide-plus\" className=\"size-3.5\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={6}\n className=\"w-auto gap-0 p-0\"\n onCloseAutoFocus={event => event.preventDefault()}\n >\n <div className=\"w-64 p-3\">\n <CompositeTagsEditor editor={editor} row={item.row} />\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n )}\n </Fragment>\n ))}\n </div>,\n host,\n )\n}\n\nconst NO_KEYS: ReadonlySet<string> = new Set()\n\n/** Chips start right after the title's last character and run on to the editor's right edge. */\nconst ROW_HEIGHT = 24\n\nconst CHIP = 'inline-flex h-5 shrink-0 items-center gap-1 rounded-sm px-1.5 text-xs leading-none whitespace-nowrap text-muted-foreground'\nconst CHIP_BUTTON = `${CHIP} transition-colors duration-150 hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary`\n\n/** `statusStyle` names Nuxt UI text colors: the shadcn counterparts (`--sk-info` and `--sk-success` come from the token stylesheets). */\nconst STATUS_COLOR: Record<string, string> = {\n 'text-muted': 'text-muted-foreground',\n 'text-info': 'text-(--sk-info)',\n 'text-success': 'text-(--sk-success)',\n 'text-dimmed': 'text-muted-foreground/70',\n}\n\n/** Scale and fade in from the icon (`transform-origin` is Radix's), a bit longer than the default; `composites.css` defines the keyframes and the reduced-motion override. */\nconst DETAILS_POPOVER = 'w-auto gap-0 p-0 composite-popover data-[state=open]:animate-[composite-pop-in_160ms_var(--ease-out)] data-[state=closed]:animate-[composite-pop-out_120ms_var(--ease-out)]'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CompositeChrome.js","names":[],"sources":["../../../../src/features/composites/react/CompositeChrome.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Fragment, type KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { startBlockDrag } from '../../../core'\nimport { Icon } from '../../../react/Icon'\nimport { useStore } from '../../../react/use-store'\nimport { useEditorSettings } from '../../../react/settings'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../react/ui/popover'\nimport { cn } from '../../../react/ui/utils'\nimport { compositeSectionOf } from '../anchors'\nimport { derivedChromeOf } from '../derived-chrome'\nimport { rowStatus } from '../meta-display'\nimport { basketById, basketOf, titleHiddenOf } from '../baskets'\nimport { compositeBasketsOf } from '../nodes'\nimport { compositeOfRow, type CompositeRow, setCompositeTags } from '../rows'\nimport { CompositeBasketMenu } from './CompositeBasketMenu'\nimport { CompositeDetails } from './CompositeDetails'\nimport { CompositeStatusMenu } from './CompositeStatusMenu'\nimport { CompositeTagsEditor } from './CompositeTagsEditor'\nimport { useCompositeAnchors } from './use-composite-anchors'\nimport { useCompositeRows } from './use-composite-rows'\n\n/**\n * Chrome of every composite, laid over the editor (mounted by the composites feature): on the title line the status\n * chip and one chip per tag (with a `+` chip to add tags), and on the front the kind icon. The icon is a button that\n * opens the details popover (kind with a confirmation before it changes, the kind's fields and the summary;\n * `CompositeDetails`) and, in an editable editor, the composite's drag handle: dragging it moves the composite\n * (`startBlockDrag`). With `blocks.kindIcon: 'hover'` it shows only while the composite is hovered, its popover is\n * open or the icon has keyboard focus. Chips and popover also render in read-only editors, without edit affordances.\n * The node view's own description and tags rows are hidden by CSS; this overlay shows and edits them.\n */\nexport function CompositeChrome({ editor }: { editor: Editor }) {\n const { rows } = useCompositeRows(editor)\n const { host, anchors, frame } = useCompositeAnchors(editor, rows)\n\n // `isEditable` is not reactive: follow it on every transaction (`EditorSurface` dispatches one after `setEditable`).\n const [editable, setEditable] = useState(() => !editor.isDestroyed && editor.isEditable)\n useEffect(() => {\n const sync = () => setEditable(!editor.isDestroyed && editor.isEditable)\n sync()\n editor.on('transaction', sync)\n return () => {\n editor.off('transaction', sync)\n }\n }, [editor])\n\n const { settings } = useEditorSettings()\n const { t } = useEditorI18n()\n const iconOnHover = settings.blocks.kindIcon === 'hover'\n\n // Hover state of `kindIcon: 'hover'`: the keys of the rows whose composite is under the pointer (that one and every\n // composite around it), plus the row whose chips the pointer is on (they sit outside the editor's DOM).\n const [hoveredKeys, setHoveredKeys] = useState<ReadonlySet<string>>(NO_KEYS)\n const [chipsHoveredKey, setChipsHoveredKey] = useState<string | null>(null)\n const latestRows = useRef(rows)\n latestRows.current = rows\n\n useEffect(() => {\n let hoveredSection: Element | null = null\n const onMouseleave = () => {\n hoveredSection = null\n setHoveredKeys(NO_KEYS)\n }\n const onMouseover = (event: MouseEvent) => {\n const section = event.target instanceof Element ? event.target.closest('.composite') : null\n if (section === hoveredSection) return\n hoveredSection = section\n const chain = new Set<Element>()\n for (let el = section; el; el = el.parentElement?.closest('.composite') ?? null) chain.add(el)\n const { view, state } = editor\n setHoveredKeys(new Set(latestRows.current.filter((row) => {\n const found = compositeOfRow(state.doc, row)\n const dom = found ? view.nodeDOM(found.pos) : null\n const section = dom instanceof Element ? compositeSectionOf(dom) : null\n return section !== null && chain.has(section)\n }).map(row => row.key)))\n }\n\n onMouseleave()\n if (!iconOnHover || editor.isDestroyed) return\n const { dom } = editor.view\n dom.addEventListener('mouseover', onMouseover)\n dom.addEventListener('mouseleave', onMouseleave)\n return () => {\n dom.removeEventListener('mouseover', onMouseover)\n dom.removeEventListener('mouseleave', onMouseleave)\n }\n }, [editor, iconOnHover])\n\n function onIconDragStart(event: DragEvent, row: CompositeRow) {\n const found = compositeOfRow(editor.state.doc, row)\n if (!found || !startBlockDrag(editor, event, found.pos)) event.preventDefault()\n }\n\n const expanded = useStore(editor.storage.composite.expanded)\n const revealedTitles = useStore(editor.storage.composite.revealedTitles)\n\n const placed = useMemo(() => {\n const baskets = compositeBasketsOf(editor)\n // End of the collapsed derived Baustein the rows being walked sit in: its clipped content shows no chrome.\n let hiddenUntil = -1\n return rows.flatMap((row) => {\n if (row.pos < hiddenUntil) return []\n const derived = derivedChromeOf(editor, row)\n const open = !derived || !row.id || expanded.has(row.id)\n if (!open) hiddenUntil = row.end\n const anchor = anchors.get(row.key)\n if (!anchor) return []\n const titleHidden = titleHiddenOf(row)\n // A hidden title leaves nothing to measure: the chips sit in the strip beside the kind icon (`composites.css`).\n const inStrip = titleHidden && !(row.id && revealedTitles.has(row.id)) && anchor.toggleSize > 0\n // An empty title shows a placeholder the anchor cannot measure: keep clear of it.\n const left = inStrip ? anchor.toggleLeft + anchor.toggleSize + 6 : anchor.lineEnd + (anchor.titleEmpty ? 116 : 12)\n return [{\n row,\n anchor,\n left,\n top: (inStrip ? anchor.toggleTop + anchor.toggleSize / 2 : anchor.lastCenter) - ROW_HEIGHT / 2,\n width: Math.max((frame?.right ?? anchor.right) - left, 120),\n status: rowStatus(row, t),\n derived: derived && { stale: derived.stale, open, toggleable: !!row.id },\n basket: basketById(baskets, basketOf(row)) ?? null,\n titleHidden,\n }]\n })\n }, [editor, rows, anchors, frame, expanded, revealedTitles, t])\n\n function toggleExpanded(row: CompositeRow) {\n if (row.id) editor.commands.toggleCompositeExpanded(row.id)\n }\n\n function removeTag(row: CompositeRow, tag: string) {\n setCompositeTags(editor, row, row.tags.filter(candidate => candidate !== tag))\n }\n\n function toggleTitleHidden(row: CompositeRow) {\n const found = compositeOfRow(editor.state.doc, row)\n if (found) editor.commands.setCompositeTitleHidden(!titleHiddenOf(row), found.pos)\n }\n\n if (!host) return null\n\n return createPortal(\n <div className=\"absolute top-0 left-0 h-0 w-0\">\n {placed.map(item => (\n <Fragment key={item.row.key}>\n {item.anchor.toggleSize > 0 && (\n <Popover>\n <PopoverTrigger asChild>\n {/* A span, not a `button`: Firefox does not start drags from buttons. */}\n <span\n role=\"button\"\n tabIndex={0}\n draggable={editable}\n className={cn(\n 'absolute z-10 flex items-center justify-center rounded-sm bg-background text-muted-foreground transition-[color,background-color,opacity] duration-120 motion-reduce:transition-none hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary',\n editable ? 'cursor-grab active:cursor-grabbing' : 'cursor-pointer',\n iconOnHover && !hoveredKeys.has(item.row.key) && chipsHoveredKey !== item.row.key && 'opacity-0 hover:opacity-100 focus-visible:opacity-100 data-[state=open]:opacity-100',\n )}\n style={{ top: `${item.anchor.toggleTop}px`, left: `${item.anchor.toggleLeft}px`, width: `${item.anchor.toggleSize}px`, height: `${item.anchor.toggleSize}px` }}\n aria-label={t('composites.chrome.details', { kind: item.row.kindLabel })}\n title={editable ? t('composites.chrome.detailsDrag', { kind: item.row.kindLabel }) : t('composites.chrome.details', { kind: item.row.kindLabel })}\n onKeyDown={(event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Enter' && event.key !== ' ') return\n event.preventDefault()\n event.currentTarget.click()\n }}\n onDragStart={event => onIconDragStart(event.nativeEvent, item.row)}\n >\n <Icon key={item.row.icon} name={item.row.icon} className=\"size-3.5\" />\n </span>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={4}\n className={DETAILS_POPOVER}\n >\n <CompositeDetails editor={editor} row={item.row} editable={editable} />\n </PopoverContent>\n </Popover>\n )}\n\n {(editable || item.basket || item.status || item.derived || (item.row.tagsVisible && item.row.tags.length > 0)) && (\n <div\n className=\"absolute z-10 flex items-center gap-px overflow-hidden rounded-md bg-background\"\n style={{ top: `${item.top}px`, left: `${item.left}px`, maxWidth: `${item.width}px`, height: `${ROW_HEIGHT}px` }}\n onMouseEnter={() => setChipsHoveredKey(item.row.key)}\n onMouseLeave={() => setChipsHoveredKey(null)}\n >\n {editable\n ? (\n <>\n <CompositeBasketMenu editor={editor} row={item.row}>\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-label={t('composites.basket.label')}\n title={item.basket ? `${t('composites.basket.label')}: ${item.basket.label}` : t('composites.basket.label')}\n >\n {item.basket\n ? <span className=\"size-2.5 rounded-full\" style={{ backgroundColor: item.basket.color }} />\n : <Icon name=\"i-lucide-circle-dashed\" className=\"size-3.5\" />}\n </button>\n </CompositeBasketMenu>\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-label={t(item.titleHidden ? 'composites.titleHidden.show' : 'composites.titleHidden.hide')}\n title={t(item.titleHidden ? 'composites.titleHidden.show' : 'composites.titleHidden.hide')}\n onClick={() => toggleTitleHidden(item.row)}\n >\n <Icon name={item.titleHidden ? 'i-lucide-eye' : 'i-lucide-eye-off'} className=\"size-3.5\" />\n </button>\n </>\n )\n : item.basket && (\n <span className={CHIP} title={item.basket.label}>\n <span className=\"size-2.5 rounded-full\" style={{ backgroundColor: item.basket.color }} />\n </span>\n )}\n\n {item.derived && (\n <>\n {item.derived.toggleable && (\n <button\n type=\"button\"\n className={CHIP_BUTTON}\n aria-expanded={item.derived.open}\n aria-label={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n title={t(item.derived.open ? 'composites.derivation.collapse' : 'composites.derivation.expand')}\n onClick={() => toggleExpanded(item.row)}\n >\n <Icon name={item.derived.open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'} className=\"size-3.5\" />\n </button>\n )}\n <span className={CHIP}>\n <Icon name=\"i-lucide-git-branch\" className=\"size-3.5\" />{t('composites.derivation.badge')}\n </span>\n {item.derived.stale && (\n <span className={cn(CHIP, 'text-(--sk-warning)')}>\n <Icon name=\"i-lucide-triangle-alert\" className=\"size-3.5\" />{t('composites.derivation.stale')}\n </span>\n )}\n </>\n )}\n\n {item.status && (editable\n ? (\n <CompositeStatusMenu editor={editor} row={item.row}>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.status.label', { status: item.status.label })}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </button>\n </CompositeStatusMenu>\n )\n : (\n <span className={CHIP}>\n <Icon name={item.status.icon} className={cn('size-3.5', STATUS_COLOR[item.status.color] ?? item.status.color)} />{item.status.label}\n </span>\n ))}\n\n {(item.row.tagsVisible ? item.row.tags : []).map(tag => (\n <span key={tag} className={cn(CHIP, 'group max-w-40 gap-0')}>\n <span className=\"truncate\">#{tag}</span>\n {editable && (\n <button\n type=\"button\"\n className=\"flex h-4 w-0 shrink-0 items-center justify-center overflow-hidden rounded-sm opacity-0 transition-all duration-150 group-hover:ml-1 group-hover:w-4 group-hover:opacity-100 hover:bg-muted hover:text-foreground focus-visible:ml-1 focus-visible:w-4 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-primary\"\n aria-label={t('composites.tag.remove', { tag })}\n onClick={() => removeTag(item.row, tag)}\n >\n <Icon name=\"i-lucide-x\" className=\"size-3\" />\n </button>\n )}\n </span>\n ))}\n\n {editable && item.row.tagsVisible && (\n <Popover>\n <PopoverTrigger asChild>\n <button type=\"button\" className={CHIP_BUTTON} aria-label={t('composites.tag.add')} title={t('composites.tag.add')}>\n <Icon name=\"i-lucide-plus\" className=\"size-3.5\" />\n </button>\n </PopoverTrigger>\n <PopoverContent\n side=\"bottom\"\n align=\"start\"\n sideOffset={6}\n className=\"w-auto gap-0 p-0\"\n onCloseAutoFocus={event => event.preventDefault()}\n >\n <div className=\"w-64 p-3\">\n <CompositeTagsEditor editor={editor} row={item.row} />\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n )}\n </Fragment>\n ))}\n </div>,\n host,\n )\n}\n\nconst NO_KEYS: ReadonlySet<string> = new Set()\n\n/** Chips start right after the title's last character and run on to the editor's right edge. */\nconst ROW_HEIGHT = 24\n\nconst CHIP = 'inline-flex h-5 shrink-0 items-center gap-1 rounded-sm px-1.5 text-xs leading-none whitespace-nowrap text-muted-foreground'\nconst CHIP_BUTTON = `${CHIP} transition-colors duration-150 hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary`\n\n/** `statusStyle` names Nuxt UI text colors: the shadcn counterparts (`--sk-info` and `--sk-success` come from the token stylesheets). */\nconst STATUS_COLOR: Record<string, string> = {\n 'text-muted': 'text-muted-foreground',\n 'text-info': 'text-(--sk-info)',\n 'text-success': 'text-(--sk-success)',\n 'text-dimmed': 'text-muted-foreground/70',\n}\n\n/** Scale and fade in from the icon (`transform-origin` is Radix's), a bit longer than the default; `composites.css` defines the keyframes and the reduced-motion override. */\nconst DETAILS_POPOVER = 'w-auto gap-0 p-0 composite-popover data-[state=open]:animate-[composite-pop-in_160ms_var(--ease-out)] data-[state=closed]:animate-[composite-pop-out_120ms_var(--ease-out)]'\n"],"mappings":"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"}
|