@ecsplico/editor 3.1.0 → 3.2.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 +41 -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 +2 -2
- 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 +2 -2
- 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/CompositeFieldEditor.js +1 -0
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeView.js +17 -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 +96 -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 +6 -3
- package/dist/features/composites/index.js +6 -3
- 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 +14 -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 +12 -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/media/index.js +1 -1
- package/dist/features/media/react/AudioRecorderModal.js +1 -1
- package/dist/features/media/react.js +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 +20 -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 +6 -0
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +55 -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 +20 -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 +6 -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 +6 -6
- package/dist/nuxt/module.d.ts +110 -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 +6 -2
- 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 +7 -78
- 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 +32 -0
- package/dist/tree/DocumentTree.js +229 -78
- 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 +152 -31
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +30 -1
- package/dist/tree/tree-host.js.map +1 -1
- package/dist/tree/tree-items.js +52 -5
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +113 -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 +13 -5
- package/dist/vue/SimpleEditor.vue.d.ts +4 -4
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/dist/vue/compose.js +6 -2
- package/dist/vue/compose.js.map +1 -1
- package/dist/vue/kit/types.d.ts +2 -0
- package/package.json +21 -1
|
@@ -4,12 +4,13 @@ import { Metadata } from "./metadata.js";
|
|
|
4
4
|
import { BlockId } from "./schema/block-ids.js";
|
|
5
5
|
import { NodeFlags } from "./schema/node-flags.js";
|
|
6
6
|
import { BlockConvert, convertMenuItems, coreConversions } from "./block-convert.js";
|
|
7
|
+
import { BlockTransferStorage } from "./block-transfer.js";
|
|
7
8
|
import { BlockFlash } from "./block-flash.js";
|
|
8
9
|
import { DocAnchors } from "./doc-anchors.js";
|
|
9
10
|
import { DocumentMarkdown } from "./document-markdown.js";
|
|
10
11
|
import { OpaqueFence } from "./schema/opaque-fence.js";
|
|
11
12
|
import { StrikeX } from "./schema/strike.js";
|
|
12
|
-
import { coreContentSchemaExtensions } from "./schema/index.js";
|
|
13
|
+
import { coreContentSchemaExtensions, starterKitOf } from "./schema/index.js";
|
|
13
14
|
import { SlashCommand } from "./slash.js";
|
|
14
15
|
import { CORE_TOOLBAR_GROUPS, coreCommands } from "./toolbar-items.js";
|
|
15
16
|
import { Extension } from "@tiptap/core";
|
|
@@ -52,19 +53,22 @@ const coreBlockActions = (ctx) => [
|
|
|
52
53
|
/**
|
|
53
54
|
* The content base of every editor: StarterKit with StrikeX, block IDs and node flags, the verbatim `OpaqueFence` for
|
|
54
55
|
* Markdown fences of unknown kinds, Markdown, abilities and metadata infrastructure and the standard conversions.
|
|
56
|
+
* `options` drop optional StarterKit structures (headings, lists, blockquote, code block, rule) from the editor and
|
|
57
|
+
* from the server schema alike (`CoreContentOptions`); everything is on by default.
|
|
55
58
|
* StarterKit's undo history is off when a collaboration session brings its own. Its `schemaExtensions` are the base
|
|
56
59
|
* of the server schema (`schemaExtensionsOf` appends `BlockId`). A headless editor (Markdown conversion, viewer,
|
|
57
60
|
* validation) is `coreContentFeature()` plus other features without `coreUiFeature()`.
|
|
58
61
|
* Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.
|
|
59
62
|
*/
|
|
60
|
-
function coreContentFeature() {
|
|
63
|
+
function coreContentFeature(options = {}) {
|
|
61
64
|
return {
|
|
62
65
|
id: "core-content",
|
|
63
66
|
dimension: "content",
|
|
64
|
-
schemaExtensions: coreContentSchemaExtensions,
|
|
67
|
+
schemaExtensions: () => coreContentSchemaExtensions(options),
|
|
65
68
|
extensions: (ctx) => [
|
|
66
69
|
Abilities.configure(ctx.abilities),
|
|
67
70
|
StarterKit.configure({
|
|
71
|
+
...starterKitOf(options),
|
|
68
72
|
strike: false,
|
|
69
73
|
undoRedo: ctx.collaboration ? false : void 0,
|
|
70
74
|
dropcursor: {
|
|
@@ -77,6 +81,7 @@ function coreContentFeature() {
|
|
|
77
81
|
OpaqueFence,
|
|
78
82
|
Metadata.configure({ registry: ctx.metadata }),
|
|
79
83
|
BlockId,
|
|
84
|
+
BlockTransferStorage.configure({ documentId: ctx.documentId ?? null }),
|
|
80
85
|
DocumentMarkdown
|
|
81
86
|
],
|
|
82
87
|
conversions: [coreConversions]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core-feature.js","names":[],"sources":["../../src/core/core-feature.ts"],"sourcesContent":["import { type AnyExtension, Extension } from '@tiptap/core'\nimport { Placeholder } from '@tiptap/extensions'\nimport StarterKit from '@tiptap/starter-kit'\nimport { Abilities } from './abilities'\nimport { BlockConvert, coreConversions, convertMenuItems } from './block-convert'\nimport { BlockFlash } from './block-flash'\nimport { DocAnchors } from './doc-anchors'\nimport { DocumentMarkdown } from './document-markdown'\nimport type { BlockAction } from './extension-points'\nimport type { EditorFeature, FeatureContext } from './feature'\nimport { Metadata } from './metadata'\nimport { coreContentSchemaExtensions } from './schema'\nimport { ABILITY } from './schema/abilities'\nimport { BlockId } from './schema/block-ids'\nimport { NodeFlags } from './schema/node-flags'\nimport { OpaqueFence } from './schema/opaque-fence'\nimport { StrikeX } from './schema/strike'\nimport { SlashCommand } from './slash'\nimport { CORE_TOOLBAR_GROUPS, coreCommands } from './toolbar-items'\n\n/** Hover actions every editor with block UI has: „Umwandeln in …“, duplicate, delete (in this order, after features' own). */\nexport const coreBlockActions = (ctx: FeatureContext): readonly BlockAction[] => [\n {\n id: 'convert',\n label: ctx.t('block.convert'),\n icon: 'i-lucide-replace',\n ability: ABILITY.blockConvert,\n order: 1000,\n menu: ({ editor, node, pos }) => convertMenuItems(editor, { node, pos }, ctx.t),\n },\n {\n id: 'duplicate',\n label: ctx.t('block.duplicate'),\n icon: 'i-lucide-copy',\n ability: ABILITY.blockDuplicate,\n order: 1010,\n // BlockId's dedupe plugin gives the copy (and every block nested in it) fresh ids.\n run: ({ editor, node, pos }) => editor.chain().insertContentAt(pos + node.nodeSize, node.toJSON()).run(),\n },\n {\n id: 'delete',\n label: ctx.t('block.delete'),\n icon: 'i-lucide-trash-2',\n ability: ABILITY.blockDelete,\n order: 1020,\n run: ({ editor, node, pos }) => editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run(),\n },\n]\n\n/**\n * The content base of every editor: StarterKit with StrikeX, block IDs and node flags, the verbatim `OpaqueFence` for\n * Markdown fences of unknown kinds, Markdown, abilities and metadata infrastructure and the standard conversions.\n * StarterKit's undo history is off when a collaboration session brings its own. Its `schemaExtensions` are the base\n * of the server schema (`schemaExtensionsOf` appends `BlockId`). A headless editor (Markdown conversion, viewer,\n * validation) is `coreContentFeature()` plus other features without `coreUiFeature()`.\n * Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.\n */\nexport function coreContentFeature(): EditorFeature<never> {\n return {\n id: 'core-content',\n dimension: 'content',\n schemaExtensions: coreContentSchemaExtensions,\n extensions: ctx => [\n Abilities.configure(ctx.abilities),\n StarterKit.configure({ strike: false, undoRedo: ctx.collaboration ? false : undefined, dropcursor: { color: 'var(--sk-primary)', width: 2 } }),\n StrikeX,\n NodeFlags,\n OpaqueFence,\n Metadata.configure({ registry: ctx.metadata }),\n BlockId,\n DocumentMarkdown,\n ],\n conversions: [coreConversions],\n }\n}\n\n/**\n * What a framework layer binds to `core-ui`: the ⌘K palette component, mounted as an overlay of the surface (it reads\n * `ui.paletteOpen` and the editor's commands; `EditorCommandPalette` of `/vue` and `/react`). `C` is the layer's component type.\n */\nexport interface CoreUi<C> {\n palette: C\n}\n\n/** Mod-k toggles the palette of this editor's surface. */\nfunction paletteShortcut(ctx: FeatureContext): AnyExtension {\n return Extension.create({\n name: 'commandPaletteShortcut',\n addKeyboardShortcuts: () => ({\n 'Mod-k': () => {\n ctx.ui.togglePalette()\n return true\n },\n }),\n })\n}\n\n/**\n * The editing UX of a simple editor: Placeholder (marks empty textblocks `is-empty`), BlockFlash (`revealBlock`),\n * BlockConvert, local DocAnchors, the `/` menu (the commands with a `slash` placement: the block types here, more from\n * other features and the host), the undo/redo, formatting and block commands with their toolbar groups, and the\n * duplicate/delete/convert block actions. Needs `core-content`.\n * Given `ui` (the framework layer's palette component) it also has the ⌘K command palette: Mod-k and the overlay.\n * Without `ui` it contributes no components (`EditorFeature<never>`), so it fits a headless preset of any framework layer.\n */\nexport function coreUiFeature<C = never>(ui?: CoreUi<C>): EditorFeature<C> {\n return {\n id: 'core-ui',\n dimension: 'interaction',\n requires: ['core-content'],\n extensions: ctx => [\n Placeholder.configure({ includeChildren: true, showOnlyCurrent: false, placeholder: '' }),\n SlashCommand.configure({ commands: ctx.commands(), emptyText: ctx.t('slash.empty'), loadingText: ctx.t('common.loading') }),\n BlockFlash,\n BlockConvert,\n DocAnchors.configure({ resolver: ctx.anchorResolver }),\n ...(ui ? [paletteShortcut(ctx)] : []),\n ],\n toolbarGroups: CORE_TOOLBAR_GROUPS,\n commands: coreCommands,\n blockActions: ctx => [...coreBlockActions(ctx)],\n ...(ui && { overlays: [ui.palette] }),\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"core-feature.js","names":[],"sources":["../../src/core/core-feature.ts"],"sourcesContent":["import { type AnyExtension, Extension } from '@tiptap/core'\nimport { Placeholder } from '@tiptap/extensions'\nimport StarterKit from '@tiptap/starter-kit'\nimport { Abilities } from './abilities'\nimport { BlockConvert, coreConversions, convertMenuItems } from './block-convert'\nimport { BlockFlash } from './block-flash'\nimport { BlockTransferStorage } from './block-transfer'\nimport { DocAnchors } from './doc-anchors'\nimport { DocumentMarkdown } from './document-markdown'\nimport type { BlockAction } from './extension-points'\nimport type { EditorFeature, FeatureContext } from './feature'\nimport { Metadata } from './metadata'\nimport { type CoreContentOptions, coreContentSchemaExtensions, starterKitOf } from './schema'\nimport { ABILITY } from './schema/abilities'\nimport { BlockId } from './schema/block-ids'\nimport { NodeFlags } from './schema/node-flags'\nimport { OpaqueFence } from './schema/opaque-fence'\nimport { StrikeX } from './schema/strike'\nimport { SlashCommand } from './slash'\nimport { CORE_TOOLBAR_GROUPS, coreCommands } from './toolbar-items'\n\n/** Hover actions every editor with block UI has: „Umwandeln in …“, duplicate, delete (in this order, after features' own). */\nexport const coreBlockActions = (ctx: FeatureContext): readonly BlockAction[] => [\n {\n id: 'convert',\n label: ctx.t('block.convert'),\n icon: 'i-lucide-replace',\n ability: ABILITY.blockConvert,\n order: 1000,\n menu: ({ editor, node, pos }) => convertMenuItems(editor, { node, pos }, ctx.t),\n },\n {\n id: 'duplicate',\n label: ctx.t('block.duplicate'),\n icon: 'i-lucide-copy',\n ability: ABILITY.blockDuplicate,\n order: 1010,\n // BlockId's dedupe plugin gives the copy (and every block nested in it) fresh ids.\n run: ({ editor, node, pos }) => editor.chain().insertContentAt(pos + node.nodeSize, node.toJSON()).run(),\n },\n {\n id: 'delete',\n label: ctx.t('block.delete'),\n icon: 'i-lucide-trash-2',\n ability: ABILITY.blockDelete,\n order: 1020,\n run: ({ editor, node, pos }) => editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run(),\n },\n]\n\n/**\n * The content base of every editor: StarterKit with StrikeX, block IDs and node flags, the verbatim `OpaqueFence` for\n * Markdown fences of unknown kinds, Markdown, abilities and metadata infrastructure and the standard conversions.\n * `options` drop optional StarterKit structures (headings, lists, blockquote, code block, rule) from the editor and\n * from the server schema alike (`CoreContentOptions`); everything is on by default.\n * StarterKit's undo history is off when a collaboration session brings its own. Its `schemaExtensions` are the base\n * of the server schema (`schemaExtensionsOf` appends `BlockId`). A headless editor (Markdown conversion, viewer,\n * validation) is `coreContentFeature()` plus other features without `coreUiFeature()`.\n * Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.\n */\nexport function coreContentFeature(options: CoreContentOptions = {}): EditorFeature<never> {\n return {\n id: 'core-content',\n dimension: 'content',\n schemaExtensions: () => coreContentSchemaExtensions(options),\n extensions: ctx => [\n Abilities.configure(ctx.abilities),\n StarterKit.configure({ ...starterKitOf(options), strike: false, undoRedo: ctx.collaboration ? false : undefined, dropcursor: { color: 'var(--sk-primary)', width: 2 } }),\n StrikeX,\n NodeFlags,\n OpaqueFence,\n Metadata.configure({ registry: ctx.metadata }),\n BlockId,\n BlockTransferStorage.configure({ documentId: ctx.documentId ?? null }),\n DocumentMarkdown,\n ],\n conversions: [coreConversions],\n }\n}\n\n/**\n * What a framework layer binds to `core-ui`: the ⌘K palette component, mounted as an overlay of the surface (it reads\n * `ui.paletteOpen` and the editor's commands; `EditorCommandPalette` of `/vue` and `/react`). `C` is the layer's component type.\n */\nexport interface CoreUi<C> {\n palette: C\n}\n\n/** Mod-k toggles the palette of this editor's surface. */\nfunction paletteShortcut(ctx: FeatureContext): AnyExtension {\n return Extension.create({\n name: 'commandPaletteShortcut',\n addKeyboardShortcuts: () => ({\n 'Mod-k': () => {\n ctx.ui.togglePalette()\n return true\n },\n }),\n })\n}\n\n/**\n * The editing UX of a simple editor: Placeholder (marks empty textblocks `is-empty`), BlockFlash (`revealBlock`),\n * BlockConvert, local DocAnchors, the `/` menu (the commands with a `slash` placement: the block types here, more from\n * other features and the host), the undo/redo, formatting and block commands with their toolbar groups, and the\n * duplicate/delete/convert block actions. Needs `core-content`.\n * Given `ui` (the framework layer's palette component) it also has the ⌘K command palette: Mod-k and the overlay.\n * Without `ui` it contributes no components (`EditorFeature<never>`), so it fits a headless preset of any framework layer.\n */\nexport function coreUiFeature<C = never>(ui?: CoreUi<C>): EditorFeature<C> {\n return {\n id: 'core-ui',\n dimension: 'interaction',\n requires: ['core-content'],\n extensions: ctx => [\n Placeholder.configure({ includeChildren: true, showOnlyCurrent: false, placeholder: '' }),\n SlashCommand.configure({ commands: ctx.commands(), emptyText: ctx.t('slash.empty'), loadingText: ctx.t('common.loading') }),\n BlockFlash,\n BlockConvert,\n DocAnchors.configure({ resolver: ctx.anchorResolver }),\n ...(ui ? [paletteShortcut(ctx)] : []),\n ],\n toolbarGroups: CORE_TOOLBAR_GROUPS,\n commands: coreCommands,\n blockActions: ctx => [...coreBlockActions(ctx)],\n ...(ui && { overlays: [ui.palette] }),\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,oBAAoB,QAAgD;CAC/E;EACE,IAAI;EACJ,OAAO,IAAI,EAAE,eAAe;EAC5B,MAAM;EACN,SAAS,QAAQ;EACjB,OAAO;EACP,OAAO,EAAE,QAAQ,MAAM,UAAU,iBAAiB,QAAQ;GAAE;GAAM;EAAI,GAAG,IAAI,CAAC;CAChF;CACA;EACE,IAAI;EACJ,OAAO,IAAI,EAAE,iBAAiB;EAC9B,MAAM;EACN,SAAS,QAAQ;EACjB,OAAO;EAEP,MAAM,EAAE,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC,CAAC,gBAAgB,MAAM,KAAK,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI;CACzG;CACA;EACE,IAAI;EACJ,OAAO,IAAI,EAAE,cAAc;EAC3B,MAAM;EACN,SAAS,QAAQ;EACjB,OAAO;EACP,MAAM,EAAE,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,YAAY;GAAE,MAAM;GAAK,IAAI,MAAM,KAAK;EAAS,CAAC,CAAC,CAAC,IAAI;CACjH;AACF;;;;;;;;;;;AAYA,SAAgB,mBAAmB,UAA8B,CAAC,GAAyB;CACzF,OAAO;EACL,IAAI;EACJ,WAAW;EACX,wBAAwB,4BAA4B,OAAO;EAC3D,aAAY,QAAO;GACjB,UAAU,UAAU,IAAI,SAAS;GACjC,WAAW,UAAU;IAAE,GAAG,aAAa,OAAO;IAAG,QAAQ;IAAO,UAAU,IAAI,gBAAgB,QAAQ,KAAA;IAAW,YAAY;KAAE,OAAO;KAAqB,OAAO;IAAE;GAAE,CAAC;GACvK;GACA;GACA;GACA,SAAS,UAAU,EAAE,UAAU,IAAI,SAAS,CAAC;GAC7C;GACA,qBAAqB,UAAU,EAAE,YAAY,IAAI,cAAc,KAAK,CAAC;GACrE;EACF;EACA,aAAa,CAAC,eAAe;CAC/B;AACF;;AAWA,SAAS,gBAAgB,KAAmC;CAC1D,OAAO,UAAU,OAAO;EACtB,MAAM;EACN,6BAA6B,EAC3B,eAAe;GACb,IAAI,GAAG,cAAc;GACrB,OAAO;EACT,EACF;CACF,CAAC;AACH;;;;;;;;;AAUA,SAAgB,cAAyB,IAAkC;CACzE,OAAO;EACL,IAAI;EACJ,WAAW;EACX,UAAU,CAAC,cAAc;EACzB,aAAY,QAAO;GACjB,YAAY,UAAU;IAAE,iBAAiB;IAAM,iBAAiB;IAAO,aAAa;GAAG,CAAC;GACxF,aAAa,UAAU;IAAE,UAAU,IAAI,SAAS;IAAG,WAAW,IAAI,EAAE,aAAa;IAAG,aAAa,IAAI,EAAE,gBAAgB;GAAE,CAAC;GAC1H;GACA;GACA,WAAW,UAAU,EAAE,UAAU,IAAI,eAAe,CAAC;GACrD,GAAI,KAAK,CAAC,gBAAgB,GAAG,CAAC,IAAI,CAAC;EACrC;EACA,eAAe;EACf,UAAU;EACV,eAAc,QAAO,CAAC,GAAG,iBAAiB,GAAG,CAAC;EAC9C,GAAI,MAAM,EAAE,UAAU,CAAC,GAAG,OAAO,EAAE;CACrC;AACF"}
|
package/dist/core/editor-ui.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Notifier } from "./notify.js";
|
|
2
2
|
import { EditorContext } from "./context.js";
|
|
3
|
-
import { BlockAction } from "./extension-points.js";
|
|
3
|
+
import { BlockAction, BlockUiOptions } from "./extension-points.js";
|
|
4
4
|
import { EditorPreset, FeatureContext } from "./feature.js";
|
|
5
5
|
import { Editor } from "@tiptap/core";
|
|
6
6
|
//#region src/core/editor-ui.d.ts
|
|
@@ -32,6 +32,8 @@ export interface EditorEntry {
|
|
|
32
32
|
ctx: FeatureContext;
|
|
33
33
|
/** Features' block actions followed by the host's, for the overlay that renders them. */
|
|
34
34
|
blockActions: readonly BlockAction[];
|
|
35
|
+
/** Options of the block-ui feature when the preset has one (`EditorFeature.blockUi`); the overlay reads which surfaces to render from it. */
|
|
36
|
+
blockUi?: Readonly<BlockUiOptions> | undefined;
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
37
39
|
* Makes `entry` reachable through `editorEntryOf(editor)` (and the framework layers' `useEditorUi(editor)`); returns the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-ui.js","names":[],"sources":["../../src/core/editor-ui.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { EditorContext } from './context'\nimport type { EditorPreset, FeatureContext } from './feature'\nimport type { BlockAction } from './extension-points'\nimport { createMetadataRegistry } from './metadata'\nimport type { Notifier } from './notify'\n\n/** Editor chrome an `EditorSurface` owns, for the components around it. Settings tabs are the ids features contribute. */\nexport interface EditorUi {\n /** Opens the settings UI, on `tab` when given (else on the tab shown last). */\n openSettings: (tab?: string) => void\n /** Opens or closes the editor's AI sidebar (the surface's `sidebarOpen` model, which the host binds). */\n toggleSidebar: () => void\n /**\n * Opens or closes the editor's comments margin (the surface's `commentsOpen` model, which the host binds). Opening it\n * closes the AI sidebar and vice versa.\n */\n toggleComments: () => void\n /** Whether the comments margin is open (read-only; for toggles' active state). */\n readonly commentsOpen: boolean\n /**\n * Opens or closes the editor's ⌘K command palette (the surface's own state; the `core-ui` feature binds Mod-k to it\n * when it was given a palette component).\n */\n togglePalette: () => void\n /** Whether the command palette is open (read-only). */\n readonly paletteOpen: boolean\n}\n\n/** What a mounted surface registers for its editor instance: the components beside it read it by `editor`. */\nexport interface EditorEntry {\n ui: EditorUi\n preset: EditorPreset\n ctx: FeatureContext\n /** Features' block actions followed by the host's, for the overlay that renders them. */\n blockActions: readonly BlockAction[]\n}\n\n/** Each mounted surface's entry by its Tiptap editor (raw, unproxied instance), for components outside its subtree. */\nconst entries = new WeakMap<Editor, EditorEntry>()\n\n/**\n * Makes `entry` reachable through `editorEntryOf(editor)` (and the framework layers' `useEditorUi(editor)`); returns the\n * unregister function. The surface registers once its editor exists (before emitting `ready`) and unregisters on unmount.\n * Pass the raw editor: framework proxies (e.g. Vue reactive wrappers) are different WeakMap keys.\n */\nexport function registerEditor(editor: Editor, entry: EditorEntry): () => void {\n entries.set(editor, entry)\n return () => {\n if (entries.get(editor) === entry) entries.delete(editor)\n }\n}\n\n/** The surface entry registered for `editor` (the raw instance); undefined for an unregistered or missing editor. */\nexport function editorEntryOf(editor: Editor | undefined): EditorEntry | undefined {\n return editor ? entries.get(editor) : undefined\n}\n\n/**\n * The one notifier policy: a notice goes to the surface notifier of `editor` (`createEditor`/`EditorSurface` of the\n * host) while it has one, else to the app context's. Both are resolved when a notice is shown (also `dismiss`), so a\n * request that finishes after an await still reaches the current surface. Pass `editor` (the raw instance) and `app` as\n * getters when they change over the notifier's life.\n */\nexport function notifierOf(editor: Editor | undefined | (() => Editor | undefined), app: Pick<EditorContext, 'notify'> | (() => Pick<EditorContext, 'notify'>)): Notifier {\n const resolve = (): Notifier => {\n const instance = typeof editor === 'function' ? editor() : editor\n return editorEntryOf(instance)?.ctx.notify ?? (typeof app === 'function' ? app() : app).notify\n }\n const notify: Notifier = notice => resolve()(notice)\n notify.dismiss = (id) => { resolve().dismiss?.(id) }\n return notify\n}\n\nexport interface BorrowedFeatureContextOptions {\n app: EditorContext\n /** The editor the borrowing component works on, as its framework layer holds it; `undefined` while there is none. */\n getEditor: () => Editor | undefined\n /** Maps the held editor to the raw instance surfaces register (default: identity). Vue passes `toRaw`. */\n unwrap?: (editor: Editor) => Editor\n /** The `settings` the context exposes (default: the app's); a framework layer may wrap it so rendering tracks changes. */\n settings?: FeatureContext['settings']\n}\n\n/**\n * The `FeatureContext` a component placed beside an editor (the toolbar) hands feature factories. App-level members\n * (`app`, `locale`, `t`, `settings`, `backend`, `ai`) come from the app context; everything per-surface (`metadata`, `ui`, `documentId`,\n * `collaboration`, `anchorResolver`, `abilities`, `commands`) is read from the entry registered for\n * `getEditor()` each time it is used, so the context follows the editor while it changes and falls back to inert values\n * (an empty metadata registry, no-op chrome, `commentsOpen: false`, `paletteOpen: false`) while there is none. `notify` is `notifierOf`.\n */\nexport function borrowedFeatureContext(options: BorrowedFeatureContextOptions): FeatureContext {\n const { app, getEditor } = options\n const unwrap = options.unwrap ?? (editor => editor)\n const rawEditor = () => {\n const editor = getEditor()\n return editor && unwrap(editor)\n }\n const fallbackMetadata = createMetadataRegistry()\n const entry = () => editorEntryOf(rawEditor())\n const ui = () => entry()?.ui\n return {\n app,\n locale: app.locale,\n t: app.t,\n settings: options.settings ?? app.settings,\n notify: notifierOf(rawEditor, app),\n backend: app.backend,\n ai: app.ai,\n get metadata() { return entry()?.ctx.metadata ?? fallbackMetadata },\n ui: {\n openSettings: tab => ui()?.openSettings(tab),\n toggleSidebar: () => ui()?.toggleSidebar(),\n toggleComments: () => ui()?.toggleComments(),\n get commentsOpen() { return ui()?.commentsOpen ?? false },\n togglePalette: () => ui()?.togglePalette(),\n get paletteOpen() { return ui()?.paletteOpen ?? false },\n },\n get documentId() { return entry()?.ctx.documentId },\n get collaboration() { return entry()?.ctx.collaboration },\n get anchorResolver() { return entry()?.ctx.anchorResolver ?? null },\n get abilities() { return entry()?.ctx.abilities ?? {} },\n commands: () => entry()?.ctx.commands() ?? [],\n getEditor,\n }\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"editor-ui.js","names":[],"sources":["../../src/core/editor-ui.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { EditorContext } from './context'\nimport type { EditorPreset, FeatureContext } from './feature'\nimport type { BlockAction, BlockUiOptions } from './extension-points'\nimport { createMetadataRegistry } from './metadata'\nimport type { Notifier } from './notify'\n\n/** Editor chrome an `EditorSurface` owns, for the components around it. Settings tabs are the ids features contribute. */\nexport interface EditorUi {\n /** Opens the settings UI, on `tab` when given (else on the tab shown last). */\n openSettings: (tab?: string) => void\n /** Opens or closes the editor's AI sidebar (the surface's `sidebarOpen` model, which the host binds). */\n toggleSidebar: () => void\n /**\n * Opens or closes the editor's comments margin (the surface's `commentsOpen` model, which the host binds). Opening it\n * closes the AI sidebar and vice versa.\n */\n toggleComments: () => void\n /** Whether the comments margin is open (read-only; for toggles' active state). */\n readonly commentsOpen: boolean\n /**\n * Opens or closes the editor's ⌘K command palette (the surface's own state; the `core-ui` feature binds Mod-k to it\n * when it was given a palette component).\n */\n togglePalette: () => void\n /** Whether the command palette is open (read-only). */\n readonly paletteOpen: boolean\n}\n\n/** What a mounted surface registers for its editor instance: the components beside it read it by `editor`. */\nexport interface EditorEntry {\n ui: EditorUi\n preset: EditorPreset\n ctx: FeatureContext\n /** Features' block actions followed by the host's, for the overlay that renders them. */\n blockActions: readonly BlockAction[]\n /** Options of the block-ui feature when the preset has one (`EditorFeature.blockUi`); the overlay reads which surfaces to render from it. */\n blockUi?: Readonly<BlockUiOptions> | undefined\n}\n\n/** Each mounted surface's entry by its Tiptap editor (raw, unproxied instance), for components outside its subtree. */\nconst entries = new WeakMap<Editor, EditorEntry>()\n\n/**\n * Makes `entry` reachable through `editorEntryOf(editor)` (and the framework layers' `useEditorUi(editor)`); returns the\n * unregister function. The surface registers once its editor exists (before emitting `ready`) and unregisters on unmount.\n * Pass the raw editor: framework proxies (e.g. Vue reactive wrappers) are different WeakMap keys.\n */\nexport function registerEditor(editor: Editor, entry: EditorEntry): () => void {\n entries.set(editor, entry)\n return () => {\n if (entries.get(editor) === entry) entries.delete(editor)\n }\n}\n\n/** The surface entry registered for `editor` (the raw instance); undefined for an unregistered or missing editor. */\nexport function editorEntryOf(editor: Editor | undefined): EditorEntry | undefined {\n return editor ? entries.get(editor) : undefined\n}\n\n/**\n * The one notifier policy: a notice goes to the surface notifier of `editor` (`createEditor`/`EditorSurface` of the\n * host) while it has one, else to the app context's. Both are resolved when a notice is shown (also `dismiss`), so a\n * request that finishes after an await still reaches the current surface. Pass `editor` (the raw instance) and `app` as\n * getters when they change over the notifier's life.\n */\nexport function notifierOf(editor: Editor | undefined | (() => Editor | undefined), app: Pick<EditorContext, 'notify'> | (() => Pick<EditorContext, 'notify'>)): Notifier {\n const resolve = (): Notifier => {\n const instance = typeof editor === 'function' ? editor() : editor\n return editorEntryOf(instance)?.ctx.notify ?? (typeof app === 'function' ? app() : app).notify\n }\n const notify: Notifier = notice => resolve()(notice)\n notify.dismiss = (id) => { resolve().dismiss?.(id) }\n return notify\n}\n\nexport interface BorrowedFeatureContextOptions {\n app: EditorContext\n /** The editor the borrowing component works on, as its framework layer holds it; `undefined` while there is none. */\n getEditor: () => Editor | undefined\n /** Maps the held editor to the raw instance surfaces register (default: identity). Vue passes `toRaw`. */\n unwrap?: (editor: Editor) => Editor\n /** The `settings` the context exposes (default: the app's); a framework layer may wrap it so rendering tracks changes. */\n settings?: FeatureContext['settings']\n}\n\n/**\n * The `FeatureContext` a component placed beside an editor (the toolbar) hands feature factories. App-level members\n * (`app`, `locale`, `t`, `settings`, `backend`, `ai`) come from the app context; everything per-surface (`metadata`, `ui`, `documentId`,\n * `collaboration`, `anchorResolver`, `abilities`, `commands`) is read from the entry registered for\n * `getEditor()` each time it is used, so the context follows the editor while it changes and falls back to inert values\n * (an empty metadata registry, no-op chrome, `commentsOpen: false`, `paletteOpen: false`) while there is none. `notify` is `notifierOf`.\n */\nexport function borrowedFeatureContext(options: BorrowedFeatureContextOptions): FeatureContext {\n const { app, getEditor } = options\n const unwrap = options.unwrap ?? (editor => editor)\n const rawEditor = () => {\n const editor = getEditor()\n return editor && unwrap(editor)\n }\n const fallbackMetadata = createMetadataRegistry()\n const entry = () => editorEntryOf(rawEditor())\n const ui = () => entry()?.ui\n return {\n app,\n locale: app.locale,\n t: app.t,\n settings: options.settings ?? app.settings,\n notify: notifierOf(rawEditor, app),\n backend: app.backend,\n ai: app.ai,\n get metadata() { return entry()?.ctx.metadata ?? fallbackMetadata },\n ui: {\n openSettings: tab => ui()?.openSettings(tab),\n toggleSidebar: () => ui()?.toggleSidebar(),\n toggleComments: () => ui()?.toggleComments(),\n get commentsOpen() { return ui()?.commentsOpen ?? false },\n togglePalette: () => ui()?.togglePalette(),\n get paletteOpen() { return ui()?.paletteOpen ?? false },\n },\n get documentId() { return entry()?.ctx.documentId },\n get collaboration() { return entry()?.ctx.collaboration },\n get anchorResolver() { return entry()?.ctx.anchorResolver ?? null },\n get abilities() { return entry()?.ctx.abilities ?? {} },\n commands: () => entry()?.ctx.commands() ?? [],\n getEditor,\n }\n}\n"],"mappings":";;;AAyCA,MAAM,0BAAU,IAAI,QAA6B;;;;;;AAOjD,SAAgB,eAAe,QAAgB,OAAgC;CAC7E,QAAQ,IAAI,QAAQ,KAAK;CACzB,aAAa;EACX,IAAI,QAAQ,IAAI,MAAM,MAAM,OAAO,QAAQ,OAAO,MAAM;CAC1D;AACF;;AAGA,SAAgB,cAAc,QAAqD;CACjF,OAAO,SAAS,QAAQ,IAAI,MAAM,IAAI,KAAA;AACxC;;;;;;;AAQA,SAAgB,WAAW,QAAyD,KAAsF;CACxK,MAAM,gBAA0B;EAE9B,OAAO,cADU,OAAO,WAAW,aAAa,OAAO,IAAI,MAC9B,CAAC,EAAE,IAAI,WAAW,OAAO,QAAQ,aAAa,IAAI,IAAI,IAAA,CAAK;CAC1F;CACA,MAAM,UAAmB,WAAU,QAAQ,CAAC,CAAC,MAAM;CACnD,OAAO,WAAW,OAAO;EAAE,QAAQ,CAAC,CAAC,UAAU,EAAE;CAAE;CACnD,OAAO;AACT;;;;;;;;AAmBA,SAAgB,uBAAuB,SAAwD;CAC7F,MAAM,EAAE,KAAK,cAAc;CAC3B,MAAM,SAAS,QAAQ,YAAW,WAAU;CAC5C,MAAM,kBAAkB;EACtB,MAAM,SAAS,UAAU;EACzB,OAAO,UAAU,OAAO,MAAM;CAChC;CACA,MAAM,mBAAmB,uBAAuB;CAChD,MAAM,cAAc,cAAc,UAAU,CAAC;CAC7C,MAAM,WAAW,MAAM,CAAC,EAAE;CAC1B,OAAO;EACL;EACA,QAAQ,IAAI;EACZ,GAAG,IAAI;EACP,UAAU,QAAQ,YAAY,IAAI;EAClC,QAAQ,WAAW,WAAW,GAAG;EACjC,SAAS,IAAI;EACb,IAAI,IAAI;EACR,IAAI,WAAW;GAAE,OAAO,MAAM,CAAC,EAAE,IAAI,YAAY;EAAiB;EAClE,IAAI;GACF,eAAc,QAAO,GAAG,CAAC,EAAE,aAAa,GAAG;GAC3C,qBAAqB,GAAG,CAAC,EAAE,cAAc;GACzC,sBAAsB,GAAG,CAAC,EAAE,eAAe;GAC3C,IAAI,eAAe;IAAE,OAAO,GAAG,CAAC,EAAE,gBAAgB;GAAM;GACxD,qBAAqB,GAAG,CAAC,EAAE,cAAc;GACzC,IAAI,cAAc;IAAE,OAAO,GAAG,CAAC,EAAE,eAAe;GAAM;EACxD;EACA,IAAI,aAAa;GAAE,OAAO,MAAM,CAAC,EAAE,IAAI;EAAW;EAClD,IAAI,gBAAgB;GAAE,OAAO,MAAM,CAAC,EAAE,IAAI;EAAc;EACxD,IAAI,iBAAiB;GAAE,OAAO,MAAM,CAAC,EAAE,IAAI,kBAAkB;EAAK;EAClE,IAAI,YAAY;GAAE,OAAO,MAAM,CAAC,EAAE,IAAI,aAAa,CAAC;EAAE;EACtD,gBAAgB,MAAM,CAAC,EAAE,IAAI,SAAS,KAAK,CAAC;EAC5C;CACF;AACF"}
|
|
@@ -13,6 +13,17 @@ export interface BlockActionContext extends BlockTarget {
|
|
|
13
13
|
/** Abilities of the block itself (`abilitiesAt(editor, pos, node)`). */
|
|
14
14
|
abilities: ReadonlySet<Ability>;
|
|
15
15
|
}
|
|
16
|
+
/** A surface that renders block actions: the hover bar of the block under the pointer, or the toolbar of the block holding the selection. */
|
|
17
|
+
export type BlockActionSurface = 'hover' | 'toolbar';
|
|
18
|
+
/** Options of the block-ui feature a host passes through `blockUiFeature(options)` / `composeEditor({ blockUi })`. */
|
|
19
|
+
export interface BlockUiOptions {
|
|
20
|
+
/**
|
|
21
|
+
* The surfaces that render block actions: `'hover'` (default; the bar of the hovered block), `'toolbar'` (the bar of
|
|
22
|
+
* the block holding the selection), `'both'`, or `'none'` (drag handle and targeting only). Abilities are unaffected:
|
|
23
|
+
* an ability stays a permission ("can"), this option only chooses where the allowed actions are offered.
|
|
24
|
+
*/
|
|
25
|
+
actions?: BlockActionSurface | 'both' | 'none';
|
|
26
|
+
}
|
|
16
27
|
interface BlockActionBase {
|
|
17
28
|
id: string;
|
|
18
29
|
/** Tooltip and aria-label. */
|
|
@@ -25,9 +36,14 @@ interface BlockActionBase {
|
|
|
25
36
|
isVisible?: (ctx: BlockActionContext) => boolean;
|
|
26
37
|
/** Position among the actions (ascending, stable); built-ins use their visual sequence, host actions follow them. */
|
|
27
38
|
order?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Where the action may appear: the hover bar of the block under the pointer, the toolbar of the block holding the
|
|
41
|
+
* selection, or both (default). The block-ui feature decides which of these surfaces exist (`BlockUiOptions.actions`).
|
|
42
|
+
*/
|
|
43
|
+
placement?: 'hover' | 'toolbar' | 'both';
|
|
28
44
|
}
|
|
29
45
|
/**
|
|
30
|
-
* An entry of the
|
|
46
|
+
* An entry of the block actions shown top-right of a block (`BlockActionsOverlay`); hosts add their own via `blockActions`.
|
|
31
47
|
* Either `run` on click, or `menu`: a dropdown of the items it returns (hidden when it returns none).
|
|
32
48
|
*/
|
|
33
49
|
export type BlockAction = BlockActionBase & ({
|
package/dist/core/feature.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { Notifier } from "./notify.js";
|
|
|
7
7
|
import { EditorAiConfig, EditorContext } from "./context.js";
|
|
8
8
|
import { ConvertTarget } from "./convert.js";
|
|
9
9
|
import { CommandDef, InsertGuard, ToolbarGroupDef, TransactionGuard } from "./command-types.js";
|
|
10
|
-
import { AbilitiesOptions, BlockAction } from "./extension-points.js";
|
|
10
|
+
import { AbilitiesOptions, BlockAction, BlockUiOptions } from "./extension-points.js";
|
|
11
11
|
import { EditorUi } from "./editor-ui.js";
|
|
12
12
|
import { EditorMetadata } from "./metadata-types.js";
|
|
13
13
|
import { MetadataRegistry } from "./metadata.js";
|
|
@@ -206,6 +206,11 @@ export interface EditorFeature<C = unknown> {
|
|
|
206
206
|
backend?: readonly BackendCapability[];
|
|
207
207
|
extensions?: (ctx: FeatureContext) => AnyExtension[];
|
|
208
208
|
blockActions?: (ctx: FeatureContext) => BlockAction[];
|
|
209
|
+
/**
|
|
210
|
+
* Options of the block UI (`BlockUiOptions`: which surfaces render block actions). Set by the block-ui feature; the
|
|
211
|
+
* overlay components are mounted by the surface without props, so they read it from the editor entry (`EditorEntry.blockUi`).
|
|
212
|
+
*/
|
|
213
|
+
blockUi?: Readonly<BlockUiOptions>;
|
|
209
214
|
/** The feature's commands, each declared once with its toolbar and `/` placements (see `CommandDef`). */
|
|
210
215
|
commands?: (ctx: FeatureContext) => CommandDef<C>[];
|
|
211
216
|
/** Toolbar groups the feature declares; every command's `toolbar.group` must be declared by some feature. Ids are unique across features. */
|
package/dist/core/feature.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.js","names":[],"sources":["../../src/core/feature.ts"],"sourcesContent":["import type { HocuspocusProvider } from '@hocuspocus/provider'\nimport type { AnyExtension, Editor, JSONContent } from '@tiptap/core'\nimport type { Node as PMNode, ResolvedPos } from '@tiptap/pm/model'\nimport type { EditorState, Transaction } from '@tiptap/pm/state'\nimport type * as Y from 'yjs'\nimport type { Locale, Translate } from '../i18n'\nimport type { EditorSettings } from '../settings'\nimport type { EditorBackend } from './backend'\nimport type { BackendCapability } from './backend-capability'\nimport type { EditorAiConfig, EditorContext } from './context'\nimport type { ConvertTarget } from './convert'\nimport type { EditorUi } from './editor-ui'\nimport type { AbilitiesOptions, BlockAction } from './extension-points'\nimport type { MetadataRegistry } from './metadata'\nimport type { CommandDef, InsertGuard, ToolbarGroupDef, TransactionGuard } from './command-types'\nimport type { Notifier } from './notify'\nimport type { EditorMetadata } from './metadata-types'\nimport type { Store } from './store'\n\n// The composition contracts: an `EditorFeature` contributes extensions and descriptors, `defineEditor` validates a\n// feature list into an immutable `EditorPreset`, and `EditorSurface` mounts one. Everything here is inert data and\n// types; features only differ by which of these seams they fill.\n\n/**\n * The user writing in an editor: the collaboration session's `user`, labelled on remote carets, and (authors and comments\n * features) copied into the author mark of every text run and into every comment the user writes.\n */\nexport interface AuthorIdentity {\n id: string\n name: string\n color: string\n}\n\n/** A host-owned collaboration session handed to the surface; only the collab feature turns it into extensions. */\nexport interface CollaborationSession {\n ydoc: Y.Doc\n provider?: HocuspocusProvider\n user?: AuthorIdentity\n /**\n * Token the collaboration server's `Identity.authenticate` receives (`{ headers, token }`), for clients without\n * the session cookie of the server's origin (scripts, agents, cross-origin hosts). A string, or a function called\n * every time the socket opens (so it can refresh an expired token). Applied to `provider`'s configuration when the\n * surface is set up, which the provider reads when its socket opens: create the provider and mount the surface in the\n * same tick, or give the provider its own `token` option instead.\n */\n token?: string | (() => string) | (() => Promise<string>)\n}\n\n/**\n * A mapping between document positions and positions that survive collaborative edits. The collab feature supplies\n * the Yjs implementation; without one, `DocAnchors` maps positions through local transactions only.\n */\nexport interface AnchorResolver {\n /** The transaction applies a Yjs change to the document: remote edits and Yjs undo/redo. */\n isYSync: (tr: Transaction) => boolean\n /** The transaction applies an edit of another client (excludes Yjs undo/redo). */\n isRemoteChange: (tr: Transaction) => boolean\n /** Opaque token of `range` that `resolve` can turn back into positions after remote changes; null when unavailable. */\n capture: (state: EditorState, range: { from: number, to: number }, startInside: boolean) => unknown | null\n resolve: (state: EditorState, token: unknown) => { from: number, to: number } | null\n /** Stops undo capturing around a programmatic change; true iff a Yjs undo manager exists. */\n stopUndoCapture: (state: EditorState) => boolean\n}\n\n/** Per-surface dependencies of a feature's factories. Created when a surface is set up; never shared between editors. */\nexport interface FeatureContext {\n /** The app context the surface belongs to (shared by all editors of the app): app-level models key on it. */\n readonly app: EditorContext\n /** The app's shared settings (see `createEditorContext`): read with `get()`, write with `update()`, observe with `subscribe()`. */\n readonly settings: Store<EditorSettings>\n readonly metadata: MetadataRegistry\n /** Chrome of the editor this context belongs to (settings, sidebar). */\n readonly ui: EditorUi\n /** Shows a notice through the surface's framework (Nuxt UI toast, sonner). */\n readonly notify: Notifier\n /** The app's UI language (`createEditorContext({ locale })`). */\n readonly locale: Locale\n /** Renders a catalog message in the app's language; every label a feature contributes goes through it. */\n readonly t: Translate\n /** The data layer of the app (`createEditorContext({ backend })`): uploads, mention lookups, themes, AI. */\n readonly backend: EditorBackend\n /** Inert public AI configuration of the app context; absent when none was supplied. */\n readonly ai: EditorAiConfig | undefined\n /** Document whose package stores uploads (media feature). */\n readonly documentId: string | undefined\n readonly collaboration: CollaborationSession | undefined\n /** The collaboration-aware position mapping of the preset's `anchorResolver` feature; null means local mapping only. */\n readonly anchorResolver: AnchorResolver | null\n /** Host options for the ability extension (`abilities` prop). */\n readonly abilities: Partial<AbilitiesOptions>\n /** The final command list: the features' commands after the host's `commands` transformer. The `/` menu lists the ones with a `slash` placement. */\n readonly commands: () => CommandDef[]\n /** Undefined while extensions are constructed; set once the editor exists. Use it in event handlers. */\n getEditor: () => Editor | undefined\n}\n\n/** A tab of the settings panel/modal, contributed by the feature that owns those settings. */\nexport interface SettingsTab<C = unknown> {\n id: string\n /** Already translated (`ctx.t(...)` in the feature's `settingsTabs` factory). */\n label: string\n icon?: string\n component: C\n /** Props handed to `component`, e.g. the configured composite kinds. */\n props?: Record<string, unknown>\n}\n\n/** The stages `parseBlocks` and the import/AI insertion paths run; features replace stages, defaults are identity/Markdown. */\nexport interface BlockPipeline {\n parseMarkdown: (editor: Editor, markdown: string) => JSONContent[]\n normalize: (editor: Editor, blocks: JSONContent[]) => JSONContent[]\n segment: (editor: Editor, blocks: JSONContent[]) => JSONContent[]\n structure: (editor: Editor, blocks: JSONContent[]) => JSONContent[]\n wrapImport: (editor: Editor, file: File, blocks: JSONContent[]) => JSONContent[]\n migrateContent: (content: JSONContent) => JSONContent\n}\n\n/** Pipeline hooks that allow a single contributor; `normalize` chains instead. */\nexport const SINGLETON_PIPELINE_HOOKS = ['parseMarkdown', 'segment', 'structure', 'wrapImport', 'migrateContent'] as const satisfies readonly (keyof BlockPipeline)[]\n\n/**\n * A feature's block conversions. Targets keep the inert `ConvertTarget` shapes; the provider that parses a target id\n * owns that target (labels, execution). `handlesSource` claims all conversions of source blocks it understands.\n */\nexport interface ConversionProvider {\n /** Source blocks this provider converts to any target it can; the first provider claiming a node executes. */\n handlesSource?: (node: PMNode) => boolean\n /** Targets offered for `node` in menus (excluding its current type). */\n targets: (editor: Editor, node: PMNode) => ConvertTarget[]\n /** The target of `id` when this provider owns it, else null. */\n parseTarget: (id: string) => ConvertTarget | null\n /** Menu label (in the language of `t`) and icon of a target this provider owns. */\n label: (target: ConvertTarget, t: Translate) => { label: string, icon: string } | null\n /** The conversion of the block after `start` into `target` (absolute range replaced and the converted blocks), or null. */\n convert: (state: EditorState, start: ResolvedPos, target: ConvertTarget) => { from: number, to: number, blocks: PMNode[] } | null\n}\n\n/**\n * Inert public data AI reads about the editor's document model. A kind's `fields` carry what the model needs to write\n * them (`type`, and the choices of a `select`).\n */\nexport interface AiContextKind {\n id: string\n label: string\n fields: readonly { key: string, label: string, type?: 'text' | 'textarea' | 'date' | 'number' | 'select' | 'multiselect' | 'reference' | 'bibliography' | 'derivation', options?: readonly { value: string, label: string }[] }[]\n /** The kind's `ai.purpose`, when configured. */\n purpose?: string\n}\n\n/** A host block as AI learns it: what it is for, its Markdown form and one example. `id` is the node type name and the `convert` target id. */\nexport interface AiBlock {\n id: string\n label: string\n /** One or two German sentences: purpose and what it may contain. */\n description: string\n /** The Markdown form, placeholders in angle brackets, e.g. `!!! <info|warning|success>\\n<Inhalt>`. */\n syntax: string\n /** One complete Markdown example of the block. */\n example: string\n}\n\nexport interface AiContext {\n kinds?: readonly AiContextKind[]\n blocks?: readonly AiBlock[]\n structure?: 'flat' | 'composite'\n}\n\n/** One piece of material a feature fetched for a block before the model fills it (a host's examples, a record). */\nexport interface AiBlockDatum { label: string, text: string }\nexport interface AiBlockDataRequest { editor: Editor, node: PMNode, pos: number, signal: AbortSignal }\n/** Material for one block; `signal` aborts when the editor is destroyed or the run is cancelled. */\nexport type AiBlockDataProvider = (request: AiBlockDataRequest) => Promise<readonly AiBlockDatum[]> | readonly AiBlockDatum[]\n\n/**\n * The axis a feature belongs to: `content` (nodes, marks and structure rules the document stores: the server schema comes\n * from here), `interaction` (editing UX, backend-bound UI such as comments and collaboration) or `ai`. A feature\n * belongs to the dimension without which it makes no sense.\n */\nexport type Dimension = 'content' | 'interaction' | 'ai'\n\n/** One named contribution to an editor (blocks, toolbar entries, menus, settings, AI knowledge). Every member is optional but `dimension`; identity and ordering come from the feature list. */\nexport interface EditorFeature<C = unknown> {\n id: string\n /** The feature's primary dimension. */\n dimension: Dimension\n /**\n * The Vue-free schema-bearing extensions of this feature (no node views, no UI), collected by `schemaExtensionsOf` for\n * the server schema. Present on every feature whose nodes or marks the stored document can contain.\n */\n schemaExtensions?: () => AnyExtension[]\n /** Ids of features that must also be installed (`defineEditor` does not install them). */\n requires?: readonly string[]\n /** Backend capabilities the feature cannot work without; a surface session over a backend lacking one throws. Optional capabilities (users and tags for mentions) are not listed. */\n backend?: readonly BackendCapability[]\n extensions?: (ctx: FeatureContext) => AnyExtension[]\n blockActions?: (ctx: FeatureContext) => BlockAction[]\n /** The feature's commands, each declared once with its toolbar and `/` placements (see `CommandDef`). */\n commands?: (ctx: FeatureContext) => CommandDef<C>[]\n /** Toolbar groups the feature declares; every command's `toolbar.group` must be declared by some feature. Ids are unique across features. */\n toolbarGroups?: ToolbarGroupDef[]\n /** Rules a command's `creates` must pass at the caret besides the schema (e.g. a Baustein's accepted children). */\n insertGuards?: (ctx: FeatureContext) => InsertGuard[]\n /**\n * Refusals of whole transactions: every transaction that changes the document runs through the guards of all\n * features, in feature order; the first one answering a string refuses it and shows that string as a notice. For rules\n * about the document as a whole that no schema expresses (a locked paragraph, a required document shape).\n */\n transactionGuards?: (ctx: FeatureContext) => TransactionGuard[]\n /** Components mounted beside the editor content; they receive `{ editor }`. `C` is the component type of the framework layer that renders them. */\n overlays?: readonly C[]\n /**\n * Runs once when a surface mounts (per-app side effects, e.g. a theme runtime; dedupe inside). Returns the teardown,\n * called when the surface unmounts; it must be idempotent (React StrictMode mounts twice).\n */\n setup?: (ctx: FeatureContext) => void | (() => void)\n /** The feature's settings tabs. A factory over the app context, so labels render in the context's language. */\n settingsTabs?: (ctx: EditorContext) => readonly SettingsTab<C>[]\n /** Built-in metadata lists (`mentions`, `hashtags`, `speakers`, …) used for every list name the host does not supply; searched only when a suggestion uses them. */\n metadataDefaults?: (ctx: FeatureContext) => EditorMetadata\n blocks?: Partial<BlockPipeline>\n conversions?: readonly ConversionProvider[]\n /** At most one contributor: supplies the collaboration-aware position mapping to `DocAnchors`. */\n anchorResolver?: (ctx: FeatureContext) => AnchorResolver\n /** Additive: the contributions of all features are merged (`kinds` and `blocks` concatenated in feature order; `structure` may be set by one feature only). Reads AI's view of the document model. */\n aiContext?: (editor: Editor) => AiContext\n /** Additive: every contributor runs for the block (in parallel), their data concatenate in feature order; one rejection fails the whole fill. Read by „Baustein füllen (KI)“. */\n aiBlockData?: AiBlockDataProvider\n /** At most one contributor: formats an AI result's markdown before the generic pipeline. */\n formatAiResult?: (editor: Editor, markdown: string, heading?: string) => string\n}\n\n/** An immutable, validated feature list with its resolved pipeline, conversion providers and AI hooks. */\nexport interface EditorPreset<C = unknown> {\n readonly features: readonly EditorFeature<C>[]\n readonly pipeline: BlockPipeline\n readonly conversions: readonly ConversionProvider[]\n /** All `aiContext` contributions merged (`aiContextOf`). */\n readonly aiContext?: EditorFeature['aiContext']\n /** All `aiBlockData` contributions merged (`aiBlockDataOf`). */\n readonly aiBlockData?: AiBlockDataProvider\n /** The single `formatAiResult` contributor, if any. */\n readonly formatAiResult?: EditorFeature['formatAiResult']\n}\n"],"mappings":";;AAsHA,MAAa,2BAA2B;CAAC;CAAiB;CAAW;CAAa;CAAc;AAAgB"}
|
|
1
|
+
{"version":3,"file":"feature.js","names":[],"sources":["../../src/core/feature.ts"],"sourcesContent":["import type { HocuspocusProvider } from '@hocuspocus/provider'\nimport type { AnyExtension, Editor, JSONContent } from '@tiptap/core'\nimport type { Node as PMNode, ResolvedPos } from '@tiptap/pm/model'\nimport type { EditorState, Transaction } from '@tiptap/pm/state'\nimport type * as Y from 'yjs'\nimport type { Locale, Translate } from '../i18n'\nimport type { EditorSettings } from '../settings'\nimport type { EditorBackend } from './backend'\nimport type { BackendCapability } from './backend-capability'\nimport type { EditorAiConfig, EditorContext } from './context'\nimport type { ConvertTarget } from './convert'\nimport type { EditorUi } from './editor-ui'\nimport type { AbilitiesOptions, BlockAction, BlockUiOptions } from './extension-points'\nimport type { MetadataRegistry } from './metadata'\nimport type { CommandDef, InsertGuard, ToolbarGroupDef, TransactionGuard } from './command-types'\nimport type { Notifier } from './notify'\nimport type { EditorMetadata } from './metadata-types'\nimport type { Store } from './store'\n\n// The composition contracts: an `EditorFeature` contributes extensions and descriptors, `defineEditor` validates a\n// feature list into an immutable `EditorPreset`, and `EditorSurface` mounts one. Everything here is inert data and\n// types; features only differ by which of these seams they fill.\n\n/**\n * The user writing in an editor: the collaboration session's `user`, labelled on remote carets, and (authors and comments\n * features) copied into the author mark of every text run and into every comment the user writes.\n */\nexport interface AuthorIdentity {\n id: string\n name: string\n color: string\n}\n\n/** A host-owned collaboration session handed to the surface; only the collab feature turns it into extensions. */\nexport interface CollaborationSession {\n ydoc: Y.Doc\n provider?: HocuspocusProvider\n user?: AuthorIdentity\n /**\n * Token the collaboration server's `Identity.authenticate` receives (`{ headers, token }`), for clients without\n * the session cookie of the server's origin (scripts, agents, cross-origin hosts). A string, or a function called\n * every time the socket opens (so it can refresh an expired token). Applied to `provider`'s configuration when the\n * surface is set up, which the provider reads when its socket opens: create the provider and mount the surface in the\n * same tick, or give the provider its own `token` option instead.\n */\n token?: string | (() => string) | (() => Promise<string>)\n}\n\n/**\n * A mapping between document positions and positions that survive collaborative edits. The collab feature supplies\n * the Yjs implementation; without one, `DocAnchors` maps positions through local transactions only.\n */\nexport interface AnchorResolver {\n /** The transaction applies a Yjs change to the document: remote edits and Yjs undo/redo. */\n isYSync: (tr: Transaction) => boolean\n /** The transaction applies an edit of another client (excludes Yjs undo/redo). */\n isRemoteChange: (tr: Transaction) => boolean\n /** Opaque token of `range` that `resolve` can turn back into positions after remote changes; null when unavailable. */\n capture: (state: EditorState, range: { from: number, to: number }, startInside: boolean) => unknown | null\n resolve: (state: EditorState, token: unknown) => { from: number, to: number } | null\n /** Stops undo capturing around a programmatic change; true iff a Yjs undo manager exists. */\n stopUndoCapture: (state: EditorState) => boolean\n}\n\n/** Per-surface dependencies of a feature's factories. Created when a surface is set up; never shared between editors. */\nexport interface FeatureContext {\n /** The app context the surface belongs to (shared by all editors of the app): app-level models key on it. */\n readonly app: EditorContext\n /** The app's shared settings (see `createEditorContext`): read with `get()`, write with `update()`, observe with `subscribe()`. */\n readonly settings: Store<EditorSettings>\n readonly metadata: MetadataRegistry\n /** Chrome of the editor this context belongs to (settings, sidebar). */\n readonly ui: EditorUi\n /** Shows a notice through the surface's framework (Nuxt UI toast, sonner). */\n readonly notify: Notifier\n /** The app's UI language (`createEditorContext({ locale })`). */\n readonly locale: Locale\n /** Renders a catalog message in the app's language; every label a feature contributes goes through it. */\n readonly t: Translate\n /** The data layer of the app (`createEditorContext({ backend })`): uploads, mention lookups, themes, AI. */\n readonly backend: EditorBackend\n /** Inert public AI configuration of the app context; absent when none was supplied. */\n readonly ai: EditorAiConfig | undefined\n /** Document whose package stores uploads (media feature). */\n readonly documentId: string | undefined\n readonly collaboration: CollaborationSession | undefined\n /** The collaboration-aware position mapping of the preset's `anchorResolver` feature; null means local mapping only. */\n readonly anchorResolver: AnchorResolver | null\n /** Host options for the ability extension (`abilities` prop). */\n readonly abilities: Partial<AbilitiesOptions>\n /** The final command list: the features' commands after the host's `commands` transformer. The `/` menu lists the ones with a `slash` placement. */\n readonly commands: () => CommandDef[]\n /** Undefined while extensions are constructed; set once the editor exists. Use it in event handlers. */\n getEditor: () => Editor | undefined\n}\n\n/** A tab of the settings panel/modal, contributed by the feature that owns those settings. */\nexport interface SettingsTab<C = unknown> {\n id: string\n /** Already translated (`ctx.t(...)` in the feature's `settingsTabs` factory). */\n label: string\n icon?: string\n component: C\n /** Props handed to `component`, e.g. the configured composite kinds. */\n props?: Record<string, unknown>\n}\n\n/** The stages `parseBlocks` and the import/AI insertion paths run; features replace stages, defaults are identity/Markdown. */\nexport interface BlockPipeline {\n parseMarkdown: (editor: Editor, markdown: string) => JSONContent[]\n normalize: (editor: Editor, blocks: JSONContent[]) => JSONContent[]\n segment: (editor: Editor, blocks: JSONContent[]) => JSONContent[]\n structure: (editor: Editor, blocks: JSONContent[]) => JSONContent[]\n wrapImport: (editor: Editor, file: File, blocks: JSONContent[]) => JSONContent[]\n migrateContent: (content: JSONContent) => JSONContent\n}\n\n/** Pipeline hooks that allow a single contributor; `normalize` chains instead. */\nexport const SINGLETON_PIPELINE_HOOKS = ['parseMarkdown', 'segment', 'structure', 'wrapImport', 'migrateContent'] as const satisfies readonly (keyof BlockPipeline)[]\n\n/**\n * A feature's block conversions. Targets keep the inert `ConvertTarget` shapes; the provider that parses a target id\n * owns that target (labels, execution). `handlesSource` claims all conversions of source blocks it understands.\n */\nexport interface ConversionProvider {\n /** Source blocks this provider converts to any target it can; the first provider claiming a node executes. */\n handlesSource?: (node: PMNode) => boolean\n /** Targets offered for `node` in menus (excluding its current type). */\n targets: (editor: Editor, node: PMNode) => ConvertTarget[]\n /** The target of `id` when this provider owns it, else null. */\n parseTarget: (id: string) => ConvertTarget | null\n /** Menu label (in the language of `t`) and icon of a target this provider owns. */\n label: (target: ConvertTarget, t: Translate) => { label: string, icon: string } | null\n /** The conversion of the block after `start` into `target` (absolute range replaced and the converted blocks), or null. */\n convert: (state: EditorState, start: ResolvedPos, target: ConvertTarget) => { from: number, to: number, blocks: PMNode[] } | null\n}\n\n/**\n * Inert public data AI reads about the editor's document model. A kind's `fields` carry what the model needs to write\n * them (`type`, and the choices of a `select`).\n */\nexport interface AiContextKind {\n id: string\n label: string\n fields: readonly { key: string, label: string, type?: 'text' | 'textarea' | 'date' | 'number' | 'select' | 'multiselect' | 'reference' | 'bibliography' | 'derivation', options?: readonly { value: string, label: string }[] }[]\n /** The kind's `ai.purpose`, when configured. */\n purpose?: string\n}\n\n/** A host block as AI learns it: what it is for, its Markdown form and one example. `id` is the node type name and the `convert` target id. */\nexport interface AiBlock {\n id: string\n label: string\n /** One or two German sentences: purpose and what it may contain. */\n description: string\n /** The Markdown form, placeholders in angle brackets, e.g. `!!! <info|warning|success>\\n<Inhalt>`. */\n syntax: string\n /** One complete Markdown example of the block. */\n example: string\n}\n\nexport interface AiContext {\n kinds?: readonly AiContextKind[]\n blocks?: readonly AiBlock[]\n structure?: 'flat' | 'composite'\n}\n\n/** One piece of material a feature fetched for a block before the model fills it (a host's examples, a record). */\nexport interface AiBlockDatum { label: string, text: string }\nexport interface AiBlockDataRequest { editor: Editor, node: PMNode, pos: number, signal: AbortSignal }\n/** Material for one block; `signal` aborts when the editor is destroyed or the run is cancelled. */\nexport type AiBlockDataProvider = (request: AiBlockDataRequest) => Promise<readonly AiBlockDatum[]> | readonly AiBlockDatum[]\n\n/**\n * The axis a feature belongs to: `content` (nodes, marks and structure rules the document stores: the server schema comes\n * from here), `interaction` (editing UX, backend-bound UI such as comments and collaboration) or `ai`. A feature\n * belongs to the dimension without which it makes no sense.\n */\nexport type Dimension = 'content' | 'interaction' | 'ai'\n\n/** One named contribution to an editor (blocks, toolbar entries, menus, settings, AI knowledge). Every member is optional but `dimension`; identity and ordering come from the feature list. */\nexport interface EditorFeature<C = unknown> {\n id: string\n /** The feature's primary dimension. */\n dimension: Dimension\n /**\n * The Vue-free schema-bearing extensions of this feature (no node views, no UI), collected by `schemaExtensionsOf` for\n * the server schema. Present on every feature whose nodes or marks the stored document can contain.\n */\n schemaExtensions?: () => AnyExtension[]\n /** Ids of features that must also be installed (`defineEditor` does not install them). */\n requires?: readonly string[]\n /** Backend capabilities the feature cannot work without; a surface session over a backend lacking one throws. Optional capabilities (users and tags for mentions) are not listed. */\n backend?: readonly BackendCapability[]\n extensions?: (ctx: FeatureContext) => AnyExtension[]\n blockActions?: (ctx: FeatureContext) => BlockAction[]\n /**\n * Options of the block UI (`BlockUiOptions`: which surfaces render block actions). Set by the block-ui feature; the\n * overlay components are mounted by the surface without props, so they read it from the editor entry (`EditorEntry.blockUi`).\n */\n blockUi?: Readonly<BlockUiOptions>\n /** The feature's commands, each declared once with its toolbar and `/` placements (see `CommandDef`). */\n commands?: (ctx: FeatureContext) => CommandDef<C>[]\n /** Toolbar groups the feature declares; every command's `toolbar.group` must be declared by some feature. Ids are unique across features. */\n toolbarGroups?: ToolbarGroupDef[]\n /** Rules a command's `creates` must pass at the caret besides the schema (e.g. a Baustein's accepted children). */\n insertGuards?: (ctx: FeatureContext) => InsertGuard[]\n /**\n * Refusals of whole transactions: every transaction that changes the document runs through the guards of all\n * features, in feature order; the first one answering a string refuses it and shows that string as a notice. For rules\n * about the document as a whole that no schema expresses (a locked paragraph, a required document shape).\n */\n transactionGuards?: (ctx: FeatureContext) => TransactionGuard[]\n /** Components mounted beside the editor content; they receive `{ editor }`. `C` is the component type of the framework layer that renders them. */\n overlays?: readonly C[]\n /**\n * Runs once when a surface mounts (per-app side effects, e.g. a theme runtime; dedupe inside). Returns the teardown,\n * called when the surface unmounts; it must be idempotent (React StrictMode mounts twice).\n */\n setup?: (ctx: FeatureContext) => void | (() => void)\n /** The feature's settings tabs. A factory over the app context, so labels render in the context's language. */\n settingsTabs?: (ctx: EditorContext) => readonly SettingsTab<C>[]\n /** Built-in metadata lists (`mentions`, `hashtags`, `speakers`, …) used for every list name the host does not supply; searched only when a suggestion uses them. */\n metadataDefaults?: (ctx: FeatureContext) => EditorMetadata\n blocks?: Partial<BlockPipeline>\n conversions?: readonly ConversionProvider[]\n /** At most one contributor: supplies the collaboration-aware position mapping to `DocAnchors`. */\n anchorResolver?: (ctx: FeatureContext) => AnchorResolver\n /** Additive: the contributions of all features are merged (`kinds` and `blocks` concatenated in feature order; `structure` may be set by one feature only). Reads AI's view of the document model. */\n aiContext?: (editor: Editor) => AiContext\n /** Additive: every contributor runs for the block (in parallel), their data concatenate in feature order; one rejection fails the whole fill. Read by „Baustein füllen (KI)“. */\n aiBlockData?: AiBlockDataProvider\n /** At most one contributor: formats an AI result's markdown before the generic pipeline. */\n formatAiResult?: (editor: Editor, markdown: string, heading?: string) => string\n}\n\n/** An immutable, validated feature list with its resolved pipeline, conversion providers and AI hooks. */\nexport interface EditorPreset<C = unknown> {\n readonly features: readonly EditorFeature<C>[]\n readonly pipeline: BlockPipeline\n readonly conversions: readonly ConversionProvider[]\n /** All `aiContext` contributions merged (`aiContextOf`). */\n readonly aiContext?: EditorFeature['aiContext']\n /** All `aiBlockData` contributions merged (`aiBlockDataOf`). */\n readonly aiBlockData?: AiBlockDataProvider\n /** The single `formatAiResult` contributor, if any. */\n readonly formatAiResult?: EditorFeature['formatAiResult']\n}\n"],"mappings":";;AAsHA,MAAa,2BAA2B;CAAC;CAAiB;CAAW;CAAa;CAAc;AAAgB"}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Notice, Notifier, silentNotifier } from "./notify.js";
|
|
|
6
6
|
import { EditorAiConfig, EditorContext, EditorContextOptions, createEditorContext } from "./context.js";
|
|
7
7
|
import { CORE_TARGET_IDS, ConvertTarget, Converter, ForeignBlocks, TextblockSpec, childrenOf, convertCoreBlocks, convertTargetId, coreConversionTargets, createConverter, finishConversion, isCoreSource, isCoreTarget, isCurrentTarget, isListNode, isQuoteNode, isTextblockNode, isWrapperBlock, parseCoreTarget, textblockSpecOf } from "./convert.js";
|
|
8
8
|
import { CommandBase, CommandDef, CommandState, Creates, InsertGuard, MenuItem, PlacedToolbarGroup, ToolbarGroupDef, TransactionGuard } from "./command-types.js";
|
|
9
|
-
import { AbilitiesOptions, BlockAction, BlockActionContext, BlockTarget } from "./extension-points.js";
|
|
9
|
+
import { AbilitiesOptions, BlockAction, BlockActionContext, BlockActionSurface, BlockTarget, BlockUiOptions } from "./extension-points.js";
|
|
10
10
|
import { EditorEntry, EditorUi, editorEntryOf, notifierOf, registerEditor } from "./editor-ui.js";
|
|
11
11
|
import { EditorMetadata, METADATA_LIST, MetadataItem, MetadataQuery, MetadataSource } from "./metadata-types.js";
|
|
12
12
|
import { Metadata, MetadataRegistry, createMetadataRegistry, filterMetadataItems, metadataOf } from "./metadata.js";
|
|
@@ -14,7 +14,9 @@ import { AiBlock, AiBlockDataProvider, AiBlockDataRequest, AiBlockDatum, AiConte
|
|
|
14
14
|
import { Abilities, AbilitiesStorage, DEFAULT_ABILITY_RULES, abilitiesAt, abilitiesAtSelection, hasAbility } from "./abilities.js";
|
|
15
15
|
import { BlockConvert, blockConversion, conversionLabel, conversionTargetGroups, convertMenuItems, coreConversions, parseConversionTarget, targetOwner } from "./block-convert.js";
|
|
16
16
|
import { startBlockDrag } from "./block-drag.js";
|
|
17
|
+
import { BLOCK_MIME, BlockTransfer, BlockTransferStorage, BlockTransferStorageData, blockTransferOf, readBlockTransfer, writeBlockTransfer } from "./block-transfer.js";
|
|
17
18
|
import { BlockFlash } from "./block-flash.js";
|
|
19
|
+
import { CoreContentOptions } from "./schema/index.js";
|
|
18
20
|
import { CoreUi, coreBlockActions, coreContentFeature, coreUiFeature } from "./core-feature.js";
|
|
19
21
|
import { canCreateAt, command, commandState, commandsOf, guardsAccept, toolbarOf } from "./commands.js";
|
|
20
22
|
import { TransactionGuards, TransactionGuardsOptions, transactionGuardExtensions } from "./transaction-guards.js";
|
|
@@ -31,4 +33,4 @@ import { selectionRange } from "./selection-range.js";
|
|
|
31
33
|
import { SlashCommand, SlashCommandOptions, SlashCommandPluginKey } from "./slash.js";
|
|
32
34
|
import { SuggestionDisplay, SuggestionListProps, SuggestionPopup, SuggestionPopupHandle, SuggestionPopupRenderer, createSuggestionRender, suggestionPopupOf } from "./suggestion-popup.js";
|
|
33
35
|
import { CORE_TOOLBAR_GROUPS, coreCommands } from "./toolbar-items.js";
|
|
34
|
-
export { Abilities, type AbilitiesOptions, type AbilitiesStorage, type AiBlock, type AiBlockDataProvider, type AiBlockDataRequest, type AiBlockDatum, type AiContext, type AiContextKind, type AiStreamEvent, type AnchorResolver, type AuthorIdentity, type BackendCapability, type BackendCommentsOptions, BackendError, type BlockAction, type BlockActionContext, BlockConvert, BlockFlash, type BlockPipeline, type BlockTarget, CATALOGS, CORE_TARGET_IDS, CORE_TOOLBAR_GROUPS, type CollaborationSession, type CollaborationToken, type CommandBase, type CommandDef, type CommandState, type ConversionProvider, type ConvertTarget, type Converter, type CoreUi, type Creates, DEFAULT_ABILITY_RULES, DEFAULT_BLOCK_PIPELINE, DEFAULT_LOCALE, type Dimension, type DocAnchor, DocAnchors, type DocAnchorsMeta, type DocAnchorsOptions, DocumentMarkdown, type EditorAiConfig, type EditorBackend, type EditorContext, type EditorContextOptions, type EditorEntry, type EditorFeature, type EditorMetadata, type EditorPreset, type EditorUi, type FeatureContext, type FeatureContextOptions, type ForeignBlocks, type InsertGuard, LOCALES, type Locale, METADATA_LIST, type MenuItem, type MessageKey, type MessageParams, type Messages, Metadata, type MetadataItem, type MetadataQuery, type MetadataRegistry, type MetadataSource, type Notice, type Notifier, PALETTE_OTHER_GROUP, type PaletteEntry, type PaletteGroup, type PlacedToolbarGroup, type PresetStorage, type ReadonlyStore, SINGLETON_PIPELINE_HOOKS, type SettingsTab, SlashCommand, type SlashCommandOptions, SlashCommandPluginKey, type Store, type SuggestionDisplay, type SuggestionListProps, SuggestionPopup, type SuggestionPopupHandle, type SuggestionPopupRenderer, type TextblockSpec, type ToolbarGroupDef, type TransactionGuard, TransactionGuards, type TransactionGuardsOptions, type Translate, abilitiesAt, abilitiesAtSelection, aiBlockDataOf, aiContextOf, anchorResolverOf, blockActionsOf, blockConversion, canCreateAt, captureRange, childrenOf, command, commandState, commandsOf, contributions, conversionLabel, conversionTargetGroups, conversionsOf, convertCoreBlocks, convertMenuItems, convertTargetId, coreBlockActions, coreCommands, coreContentFeature, coreConversionTargets, coreConversions, coreUiFeature, createConverter, createEditorContext, createFeatureContext, createMetadataRegistry, createStore, createSuggestionRender, createTranslator, defineEditor, derived, docAnchorsKey, downloadText, editorEntryOf, fetchErrorMessage, filterMetadataItems, filterPalette, finishConversion, formatAiResultOf, guardsAccept, hasAbility, isCoreSource, isCoreTarget, isCurrentTarget, isListNode, isLocale, isQuoteNode, isTextblockNode, isWrapperBlock, metadataOf, notifierOf, paletteGroupsOf, parseBlocks, parseConversionTarget, parseCoreTarget, pipelineOf, presetExtension, registerEditor, releaseRanges, resolveRange, selectionRange, settingsTabsOf, sha256Hex, silentNotifier, startBlockDrag, structureBlocks, suggestionPopupOf, targetOwner, textblockSpecOf, toolbarOf, transactionGuardExtensions, wrapImportBlocks };
|
|
36
|
+
export { Abilities, type AbilitiesOptions, type AbilitiesStorage, type AiBlock, type AiBlockDataProvider, type AiBlockDataRequest, type AiBlockDatum, type AiContext, type AiContextKind, type AiStreamEvent, type AnchorResolver, type AuthorIdentity, BLOCK_MIME, type BackendCapability, type BackendCommentsOptions, BackendError, type BlockAction, type BlockActionContext, type BlockActionSurface, BlockConvert, BlockFlash, type BlockPipeline, type BlockTarget, type BlockTransfer, BlockTransferStorage, type BlockTransferStorageData, type BlockUiOptions, CATALOGS, CORE_TARGET_IDS, CORE_TOOLBAR_GROUPS, type CollaborationSession, type CollaborationToken, type CommandBase, type CommandDef, type CommandState, type ConversionProvider, type ConvertTarget, type Converter, type CoreContentOptions, type CoreUi, type Creates, DEFAULT_ABILITY_RULES, DEFAULT_BLOCK_PIPELINE, DEFAULT_LOCALE, type Dimension, type DocAnchor, DocAnchors, type DocAnchorsMeta, type DocAnchorsOptions, DocumentMarkdown, type EditorAiConfig, type EditorBackend, type EditorContext, type EditorContextOptions, type EditorEntry, type EditorFeature, type EditorMetadata, type EditorPreset, type EditorUi, type FeatureContext, type FeatureContextOptions, type ForeignBlocks, type InsertGuard, LOCALES, type Locale, METADATA_LIST, type MenuItem, type MessageKey, type MessageParams, type Messages, Metadata, type MetadataItem, type MetadataQuery, type MetadataRegistry, type MetadataSource, type Notice, type Notifier, PALETTE_OTHER_GROUP, type PaletteEntry, type PaletteGroup, type PlacedToolbarGroup, type PresetStorage, type ReadonlyStore, SINGLETON_PIPELINE_HOOKS, type SettingsTab, SlashCommand, type SlashCommandOptions, SlashCommandPluginKey, type Store, type SuggestionDisplay, type SuggestionListProps, SuggestionPopup, type SuggestionPopupHandle, type SuggestionPopupRenderer, type TextblockSpec, type ToolbarGroupDef, type TransactionGuard, TransactionGuards, type TransactionGuardsOptions, type Translate, abilitiesAt, abilitiesAtSelection, aiBlockDataOf, aiContextOf, anchorResolverOf, blockActionsOf, blockConversion, blockTransferOf, canCreateAt, captureRange, childrenOf, command, commandState, commandsOf, contributions, conversionLabel, conversionTargetGroups, conversionsOf, convertCoreBlocks, convertMenuItems, convertTargetId, coreBlockActions, coreCommands, coreContentFeature, coreConversionTargets, coreConversions, coreUiFeature, createConverter, createEditorContext, createFeatureContext, createMetadataRegistry, createStore, createSuggestionRender, createTranslator, defineEditor, derived, docAnchorsKey, downloadText, editorEntryOf, fetchErrorMessage, filterMetadataItems, filterPalette, finishConversion, formatAiResultOf, guardsAccept, hasAbility, isCoreSource, isCoreTarget, isCurrentTarget, isListNode, isLocale, isQuoteNode, isTextblockNode, isWrapperBlock, metadataOf, notifierOf, paletteGroupsOf, parseBlocks, parseConversionTarget, parseCoreTarget, pipelineOf, presetExtension, readBlockTransfer, registerEditor, releaseRanges, resolveRange, selectionRange, settingsTabsOf, sha256Hex, silentNotifier, startBlockDrag, structureBlocks, suggestionPopupOf, targetOwner, textblockSpecOf, toolbarOf, transactionGuardExtensions, wrapImportBlocks, writeBlockTransfer };
|
package/dist/core/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BackendError } from "./backend.js";
|
|
1
2
|
import { Abilities, DEFAULT_ABILITY_RULES, abilitiesAt, abilitiesAtSelection, hasAbility } from "./abilities.js";
|
|
2
3
|
import { SINGLETON_PIPELINE_HOOKS } from "./feature.js";
|
|
3
4
|
import { DEFAULT_BLOCK_PIPELINE, aiBlockDataOf, aiContextOf, conversionsOf, formatAiResultOf, parseBlocks, pipelineOf, presetExtension, structureBlocks, wrapImportBlocks } from "./pipeline.js";
|
|
@@ -7,9 +8,9 @@ import { editorEntryOf, notifierOf, registerEditor } from "./editor-ui.js";
|
|
|
7
8
|
import { canCreateAt, command, commandState, commandsOf, guardsAccept, toolbarOf } from "./commands.js";
|
|
8
9
|
import { CORE_TARGET_IDS, childrenOf, convertCoreBlocks, convertTargetId, coreConversionTargets, createConverter, finishConversion, isCoreSource, isCoreTarget, isCurrentTarget, isListNode, isQuoteNode, isTextblockNode, isWrapperBlock, parseCoreTarget, textblockSpecOf } from "./convert.js";
|
|
9
10
|
import { BlockConvert, blockConversion, conversionLabel, conversionTargetGroups, convertMenuItems, coreConversions, parseConversionTarget, targetOwner } from "./block-convert.js";
|
|
11
|
+
import { BLOCK_MIME, BlockTransferStorage, blockTransferOf, readBlockTransfer, writeBlockTransfer } from "./block-transfer.js";
|
|
10
12
|
import { startBlockDrag } from "./block-drag.js";
|
|
11
13
|
import { BlockFlash } from "./block-flash.js";
|
|
12
|
-
import { BackendError } from "./backend.js";
|
|
13
14
|
import { CATALOGS, DEFAULT_LOCALE, LOCALES, createTranslator, isLocale } from "../i18n/index.js";
|
|
14
15
|
import { silentNotifier } from "./notify.js";
|
|
15
16
|
import { createStore, derived } from "./store.js";
|
|
@@ -28,4 +29,4 @@ import { sha256Hex } from "./hash.js";
|
|
|
28
29
|
import { METADATA_LIST } from "./metadata-types.js";
|
|
29
30
|
import { PALETTE_OTHER_GROUP, filterPalette, paletteGroupsOf } from "./palette.js";
|
|
30
31
|
import { selectionRange } from "./selection-range.js";
|
|
31
|
-
export { Abilities, BackendError, BlockConvert, BlockFlash, CATALOGS, CORE_TARGET_IDS, CORE_TOOLBAR_GROUPS, DEFAULT_ABILITY_RULES, DEFAULT_BLOCK_PIPELINE, DEFAULT_LOCALE, DocAnchors, DocumentMarkdown, LOCALES, METADATA_LIST, Metadata, PALETTE_OTHER_GROUP, SINGLETON_PIPELINE_HOOKS, SlashCommand, SlashCommandPluginKey, SuggestionPopup, TransactionGuards, abilitiesAt, abilitiesAtSelection, aiBlockDataOf, aiContextOf, anchorResolverOf, blockActionsOf, blockConversion, canCreateAt, captureRange, childrenOf, command, commandState, commandsOf, contributions, conversionLabel, conversionTargetGroups, conversionsOf, convertCoreBlocks, convertMenuItems, convertTargetId, coreBlockActions, coreCommands, coreContentFeature, coreConversionTargets, coreConversions, coreUiFeature, createConverter, createEditorContext, createFeatureContext, createMetadataRegistry, createStore, createSuggestionRender, createTranslator, defineEditor, derived, docAnchorsKey, downloadText, editorEntryOf, fetchErrorMessage, filterMetadataItems, filterPalette, finishConversion, formatAiResultOf, guardsAccept, hasAbility, isCoreSource, isCoreTarget, isCurrentTarget, isListNode, isLocale, isQuoteNode, isTextblockNode, isWrapperBlock, metadataOf, notifierOf, paletteGroupsOf, parseBlocks, parseConversionTarget, parseCoreTarget, pipelineOf, presetExtension, registerEditor, releaseRanges, resolveRange, selectionRange, settingsTabsOf, sha256Hex, silentNotifier, startBlockDrag, structureBlocks, suggestionPopupOf, targetOwner, textblockSpecOf, toolbarOf, transactionGuardExtensions, wrapImportBlocks };
|
|
32
|
+
export { Abilities, BLOCK_MIME, BackendError, BlockConvert, BlockFlash, BlockTransferStorage, CATALOGS, CORE_TARGET_IDS, CORE_TOOLBAR_GROUPS, DEFAULT_ABILITY_RULES, DEFAULT_BLOCK_PIPELINE, DEFAULT_LOCALE, DocAnchors, DocumentMarkdown, LOCALES, METADATA_LIST, Metadata, PALETTE_OTHER_GROUP, SINGLETON_PIPELINE_HOOKS, SlashCommand, SlashCommandPluginKey, SuggestionPopup, TransactionGuards, abilitiesAt, abilitiesAtSelection, aiBlockDataOf, aiContextOf, anchorResolverOf, blockActionsOf, blockConversion, blockTransferOf, canCreateAt, captureRange, childrenOf, command, commandState, commandsOf, contributions, conversionLabel, conversionTargetGroups, conversionsOf, convertCoreBlocks, convertMenuItems, convertTargetId, coreBlockActions, coreCommands, coreContentFeature, coreConversionTargets, coreConversions, coreUiFeature, createConverter, createEditorContext, createFeatureContext, createMetadataRegistry, createStore, createSuggestionRender, createTranslator, defineEditor, derived, docAnchorsKey, downloadText, editorEntryOf, fetchErrorMessage, filterMetadataItems, filterPalette, finishConversion, formatAiResultOf, guardsAccept, hasAbility, isCoreSource, isCoreTarget, isCurrentTarget, isListNode, isLocale, isQuoteNode, isTextblockNode, isWrapperBlock, metadataOf, notifierOf, paletteGroupsOf, parseBlocks, parseConversionTarget, parseCoreTarget, pipelineOf, presetExtension, readBlockTransfer, registerEditor, releaseRanges, resolveRange, selectionRange, settingsTabsOf, sha256Hex, silentNotifier, startBlockDrag, structureBlocks, suggestionPopupOf, targetOwner, textblockSpecOf, toolbarOf, transactionGuardExtensions, wrapImportBlocks, writeBlockTransfer };
|
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
import { ABILITY, Ability, AbilityRule, AbilityRuleSource, ROOT_ABILITIES, TEXT_ABILITIES, TYPE_ABILITIES, resolveAbilities } from "./abilities.js";
|
|
2
|
-
import "./mark-flags.js";
|
|
3
2
|
import { BLOCK_ID_ATTR, BLOCK_ID_TYPES, BlockId, assignBlockIds, blockIdOf, findBlockById, newBlockId } from "./block-ids.js";
|
|
4
3
|
import { HEX_COLOR } from "./hex-color.js";
|
|
4
|
+
import "./mark-flags.js";
|
|
5
5
|
import { NodeFlags, isBlockContainer, isChromeNode, isEditorBlock } from "./node-flags.js";
|
|
6
6
|
import { OpaqueFence } from "./opaque-fence.js";
|
|
7
7
|
import { StrikeX } from "./strike.js";
|
|
8
8
|
import { schemaExtensionsOf } from "./of-features.js";
|
|
9
9
|
import { AnyExtension, JSONContent } from "@tiptap/core";
|
|
10
|
+
import { StarterKitOptions } from "@tiptap/starter-kit";
|
|
10
11
|
//#region src/core/schema/index.d.ts
|
|
12
|
+
/**
|
|
13
|
+
* Which optional StarterKit structures the content base installs; everything is on by default. A structure that is off
|
|
14
|
+
* is not in the schema, in the editor and on the server alike (one options object feeds both).
|
|
15
|
+
*/
|
|
16
|
+
export interface CoreContentOptions {
|
|
17
|
+
/** `false`: no headings; `{ levels }`: only these heading levels. */
|
|
18
|
+
headings?: false | {
|
|
19
|
+
levels: (1 | 2 | 3 | 4 | 5 | 6)[];
|
|
20
|
+
};
|
|
21
|
+
/** `false`: no bullet or ordered lists (and list items). */
|
|
22
|
+
lists?: false;
|
|
23
|
+
/** `false`: no blockquote. */
|
|
24
|
+
blockquote?: false;
|
|
25
|
+
/** `false`: no code block. */
|
|
26
|
+
codeBlock?: false;
|
|
27
|
+
/** `false`: no horizontal rule. */
|
|
28
|
+
rule?: false;
|
|
29
|
+
}
|
|
30
|
+
/** The StarterKit options `options` stand for: the one mapping the editor feature and the schema-extension list share, so schemas cannot drift. */
|
|
31
|
+
export declare function starterKitOf(options?: CoreContentOptions): Partial<StarterKitOptions>;
|
|
11
32
|
/**
|
|
12
33
|
* The Vue-free nodes and marks of the content base (`core-content` feature): StarterKit (history off: the surface enables
|
|
13
34
|
* it for standalone editors, collaboration brings its own), StrikeX, the node-flags extension and `OpaqueFence` (Markdown
|
|
14
|
-
* fences of unknown kinds, kept verbatim). Without `BlockId`: `schemaExtensionsOf` appends it last.
|
|
35
|
+
* fences of unknown kinds, kept verbatim). `options` drop optional StarterKit structures. Without `BlockId`: `schemaExtensionsOf` appends it last.
|
|
15
36
|
*/
|
|
16
|
-
export declare const coreContentSchemaExtensions: () => AnyExtension[];
|
|
37
|
+
export declare const coreContentSchemaExtensions: (options?: CoreContentOptions) => AnyExtension[];
|
|
17
38
|
/**
|
|
18
39
|
* Base Vue-free nodes and marks of every editor: `coreContentSchemaExtensions()` and `BlockId`, which adds the `id`
|
|
19
40
|
* attribute to every block type (its ID-assigning plugins only run in an editor). The server builds its schema from
|
|
@@ -7,15 +7,32 @@ import { HEX_COLOR } from "./hex-color.js";
|
|
|
7
7
|
import { schemaExtensionsOf } from "./of-features.js";
|
|
8
8
|
import StarterKit from "@tiptap/starter-kit";
|
|
9
9
|
//#region src/core/schema/index.ts
|
|
10
|
+
/** The StarterKit options `options` stand for: the one mapping the editor feature and the schema-extension list share, so schemas cannot drift. */
|
|
11
|
+
function starterKitOf(options = {}) {
|
|
12
|
+
const kit = {};
|
|
13
|
+
if (options.headings === false) kit.heading = false;
|
|
14
|
+
else if (options.headings) kit.heading = { levels: options.headings.levels };
|
|
15
|
+
if (options.lists === false) Object.assign(kit, {
|
|
16
|
+
bulletList: false,
|
|
17
|
+
orderedList: false,
|
|
18
|
+
listItem: false,
|
|
19
|
+
listKeymap: false
|
|
20
|
+
});
|
|
21
|
+
if (options.blockquote === false) kit.blockquote = false;
|
|
22
|
+
if (options.codeBlock === false) kit.codeBlock = false;
|
|
23
|
+
if (options.rule === false) kit.horizontalRule = false;
|
|
24
|
+
return kit;
|
|
25
|
+
}
|
|
10
26
|
/**
|
|
11
27
|
* The Vue-free nodes and marks of the content base (`core-content` feature): StarterKit (history off: the surface enables
|
|
12
28
|
* it for standalone editors, collaboration brings its own), StrikeX, the node-flags extension and `OpaqueFence` (Markdown
|
|
13
|
-
* fences of unknown kinds, kept verbatim). Without `BlockId`: `schemaExtensionsOf` appends it last.
|
|
29
|
+
* fences of unknown kinds, kept verbatim). `options` drop optional StarterKit structures. Without `BlockId`: `schemaExtensionsOf` appends it last.
|
|
14
30
|
*/
|
|
15
|
-
const coreContentSchemaExtensions = () => [
|
|
31
|
+
const coreContentSchemaExtensions = (options = {}) => [
|
|
16
32
|
StarterKit.configure({
|
|
17
33
|
strike: false,
|
|
18
|
-
undoRedo: false
|
|
34
|
+
undoRedo: false,
|
|
35
|
+
...starterKitOf(options)
|
|
19
36
|
}),
|
|
20
37
|
StrikeX,
|
|
21
38
|
NodeFlags,
|
|
@@ -33,6 +50,6 @@ const EMPTY_EDITOR_CONTENT = {
|
|
|
33
50
|
content: [{ type: "paragraph" }]
|
|
34
51
|
};
|
|
35
52
|
//#endregion
|
|
36
|
-
export { ABILITY, BLOCK_ID_ATTR, BLOCK_ID_TYPES, BlockId, EMPTY_EDITOR_CONTENT, HEX_COLOR, NodeFlags, OpaqueFence, ROOT_ABILITIES, StrikeX, TEXT_ABILITIES, TYPE_ABILITIES, assignBlockIds, blockIdOf, coreContentSchemaExtensions, coreSchemaExtensions, findBlockById, isBlockContainer, isChromeNode, isEditorBlock, newBlockId, resolveAbilities, schemaExtensionsOf };
|
|
53
|
+
export { ABILITY, BLOCK_ID_ATTR, BLOCK_ID_TYPES, BlockId, EMPTY_EDITOR_CONTENT, HEX_COLOR, NodeFlags, OpaqueFence, ROOT_ABILITIES, StrikeX, TEXT_ABILITIES, TYPE_ABILITIES, assignBlockIds, blockIdOf, coreContentSchemaExtensions, coreSchemaExtensions, findBlockById, isBlockContainer, isChromeNode, isEditorBlock, newBlockId, resolveAbilities, schemaExtensionsOf, starterKitOf };
|
|
37
54
|
|
|
38
55
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/core/schema/index.ts"],"sourcesContent":["import type { AnyExtension, JSONContent } from '@tiptap/core'\nimport StarterKit from '@tiptap/starter-kit'\nimport { BlockId } from './block-ids'\nimport { NodeFlags } from './node-flags'\nimport { OpaqueFence } from './opaque-fence'\nimport { StrikeX } from './strike'\n\nexport * from './abilities'\nexport * from './block-ids'\nexport * from './hex-color'\nexport * from './mark-flags'\nexport * from './node-flags'\nexport { OpaqueFence } from './opaque-fence'\nexport { StrikeX } from './strike'\nexport { schemaExtensionsOf } from './of-features'\n\n/**\n * The Vue-free nodes and marks of the content base (`core-content` feature): StarterKit (history off: the surface enables\n * it for standalone editors, collaboration brings its own), StrikeX, the node-flags extension and `OpaqueFence` (Markdown\n * fences of unknown kinds, kept verbatim). Without `BlockId`: `schemaExtensionsOf` appends it last.\n */\nexport const coreContentSchemaExtensions = (): AnyExtension[] => [StarterKit.configure({ strike: false, undoRedo: false }), StrikeX, NodeFlags, OpaqueFence]\n\n/**\n * Base Vue-free nodes and marks of every editor: `coreContentSchemaExtensions()` and `BlockId`, which adds the `id`\n * attribute to every block type (its ID-assigning plugins only run in an editor). The server builds its schema from\n * these as well (`getSchema(...)`).\n */\nexport const coreSchemaExtensions = (): AnyExtension[] => [...coreContentSchemaExtensions(), BlockId]\n\n/** Content of a fresh, empty document. */\nexport const EMPTY_EDITOR_CONTENT: JSONContent = { type: 'doc', content: [{ type: 'paragraph' }] }\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/core/schema/index.ts"],"sourcesContent":["import type { AnyExtension, JSONContent } from '@tiptap/core'\nimport StarterKit, { type StarterKitOptions } from '@tiptap/starter-kit'\nimport { BlockId } from './block-ids'\nimport { NodeFlags } from './node-flags'\nimport { OpaqueFence } from './opaque-fence'\nimport { StrikeX } from './strike'\n\nexport * from './abilities'\nexport * from './block-ids'\nexport * from './hex-color'\nexport * from './mark-flags'\nexport * from './node-flags'\nexport { OpaqueFence } from './opaque-fence'\nexport { StrikeX } from './strike'\nexport { schemaExtensionsOf } from './of-features'\n\n/**\n * Which optional StarterKit structures the content base installs; everything is on by default. A structure that is off\n * is not in the schema, in the editor and on the server alike (one options object feeds both).\n */\nexport interface CoreContentOptions {\n /** `false`: no headings; `{ levels }`: only these heading levels. */\n headings?: false | { levels: (1 | 2 | 3 | 4 | 5 | 6)[] }\n /** `false`: no bullet or ordered lists (and list items). */\n lists?: false\n /** `false`: no blockquote. */\n blockquote?: false\n /** `false`: no code block. */\n codeBlock?: false\n /** `false`: no horizontal rule. */\n rule?: false\n}\n\n/** The StarterKit options `options` stand for: the one mapping the editor feature and the schema-extension list share, so schemas cannot drift. */\nexport function starterKitOf(options: CoreContentOptions = {}): Partial<StarterKitOptions> {\n const kit: Partial<StarterKitOptions> = {}\n if (options.headings === false) kit.heading = false\n else if (options.headings) kit.heading = { levels: options.headings.levels }\n if (options.lists === false) Object.assign(kit, { bulletList: false, orderedList: false, listItem: false, listKeymap: false })\n if (options.blockquote === false) kit.blockquote = false\n if (options.codeBlock === false) kit.codeBlock = false\n if (options.rule === false) kit.horizontalRule = false\n return kit\n}\n\n/**\n * The Vue-free nodes and marks of the content base (`core-content` feature): StarterKit (history off: the surface enables\n * it for standalone editors, collaboration brings its own), StrikeX, the node-flags extension and `OpaqueFence` (Markdown\n * fences of unknown kinds, kept verbatim). `options` drop optional StarterKit structures. Without `BlockId`: `schemaExtensionsOf` appends it last.\n */\nexport const coreContentSchemaExtensions = (options: CoreContentOptions = {}): AnyExtension[] => [StarterKit.configure({ strike: false, undoRedo: false, ...starterKitOf(options) }), StrikeX, NodeFlags, OpaqueFence]\n\n/**\n * Base Vue-free nodes and marks of every editor: `coreContentSchemaExtensions()` and `BlockId`, which adds the `id`\n * attribute to every block type (its ID-assigning plugins only run in an editor). The server builds its schema from\n * these as well (`getSchema(...)`).\n */\nexport const coreSchemaExtensions = (): AnyExtension[] => [...coreContentSchemaExtensions(), BlockId]\n\n/** Content of a fresh, empty document. */\nexport const EMPTY_EDITOR_CONTENT: JSONContent = { type: 'doc', content: [{ type: 'paragraph' }] }\n"],"mappings":";;;;;;;;;;AAkCA,SAAgB,aAAa,UAA8B,CAAC,GAA+B;CACzF,MAAM,MAAkC,CAAC;CACzC,IAAI,QAAQ,aAAa,OAAO,IAAI,UAAU;MACzC,IAAI,QAAQ,UAAU,IAAI,UAAU,EAAE,QAAQ,QAAQ,SAAS,OAAO;CAC3E,IAAI,QAAQ,UAAU,OAAO,OAAO,OAAO,KAAK;EAAE,YAAY;EAAO,aAAa;EAAO,UAAU;EAAO,YAAY;CAAM,CAAC;CAC7H,IAAI,QAAQ,eAAe,OAAO,IAAI,aAAa;CACnD,IAAI,QAAQ,cAAc,OAAO,IAAI,YAAY;CACjD,IAAI,QAAQ,SAAS,OAAO,IAAI,iBAAiB;CACjD,OAAO;AACT;;;;;;AAOA,MAAa,+BAA+B,UAA8B,CAAC,MAAsB;CAAC,WAAW,UAAU;EAAE,QAAQ;EAAO,UAAU;EAAO,GAAG,aAAa,OAAO;CAAE,CAAC;CAAG;CAAS;CAAW;AAAW;;;;;;AAOrN,MAAa,6BAA6C,CAAC,GAAG,4BAA4B,GAAG,OAAO;;AAGpG,MAAa,uBAAoC;CAAE,MAAM;CAAO,SAAS,CAAC,EAAE,MAAM,YAAY,CAAC;AAAE"}
|
|
@@ -51,6 +51,7 @@ function createSurfaceSession(options) {
|
|
|
51
51
|
const schema = getSchema(extensions);
|
|
52
52
|
const overlays = preset.features.flatMap((feature) => [...feature.overlays ?? []]);
|
|
53
53
|
const blockActions = blockActionsOf(preset, ctx, options.blockActions);
|
|
54
|
+
const blockUi = preset.features.find((feature) => feature.blockUi)?.blockUi;
|
|
54
55
|
const validated = (json) => {
|
|
55
56
|
try {
|
|
56
57
|
const migrated = preset.pipeline.migrateContent(json);
|
|
@@ -108,7 +109,8 @@ function createSurfaceSession(options) {
|
|
|
108
109
|
ui,
|
|
109
110
|
preset,
|
|
110
111
|
ctx,
|
|
111
|
-
blockActions
|
|
112
|
+
blockActions,
|
|
113
|
+
blockUi
|
|
112
114
|
});
|
|
113
115
|
if (announced !== editor) {
|
|
114
116
|
announced = editor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface-session.js","names":[],"sources":["../../src/core/surface-session.ts"],"sourcesContent":["import { type AnyExtension, type Editor, type EditorOptions, getSchema, type JSONContent } from '@tiptap/core'\nimport type { EditorContext } from './context'\nimport { contributions } from './define-editor'\nimport { type EditorUi, registerEditor } from './editor-ui'\nimport { type CommandDef, toolbarOf } from './commands'\nimport type { AbilitiesOptions, BlockAction } from './extension-points'\nimport type { CollaborationSession, EditorPreset } from './feature'\nimport { blockActionsOf, createFeatureContext } from './feature-context'\nimport { createMetadataRegistry } from './metadata'\nimport type { EditorMetadata, MetadataItem } from './metadata-types'\nimport type { Notifier } from './notify'\nimport { presetExtension } from './pipeline'\nimport { EMPTY_EDITOR_CONTENT } from './schema'\nimport { ROOT_ABILITIES } from './schema/abilities'\nimport { SuggestionPopup, type SuggestionPopupRenderer } from './suggestion-popup'\nimport { transactionGuardExtensions } from './transaction-guards'\n\n/**\n * What a surface hands its session. The framework layer owns everything that lives in its reactivity: the chrome\n * (`ui`), the notice renderer, the suggestion popup renderer and the editor instance (`getEditor`).\n * `preset`, `collaboration`, `documentId`, `abilities`, `extensions`, `blockActions`, `commands`, `value` and\n * `editable` are read once, when the session is created; later changes reach it through `setValue`, `setEditable` and\n * `setMetadata`.\n */\nexport interface SurfaceSessionOptions<C = unknown> {\n app: EditorContext\n /** The features the editor is made of. With `collaboration` it must contain the collaboration feature (else creating the session throws). */\n preset: EditorPreset<C>\n /** The chrome the features reach through `FeatureContext.ui`; registered for the editor by `register`. */\n ui: EditorUi\n /** The surface's notice renderer (`FeatureContext.notify`). */\n notify: Notifier\n /** The framework's popup for `@` / `#` / `/` suggestions. */\n suggestionRenderer: SuggestionPopupRenderer\n /** The mounted editor, `undefined` until the framework layer created it; used by `FeatureContext.getEditor`, `setValue` and `setEditable`. */\n getEditor: () => Editor | undefined\n /** Collaborative mode: the Y.Doc is the document, so there is no `value` and no `onChange`. */\n collaboration?: CollaborationSession | undefined\n /** Standalone initial document (TipTap JSON, unproxied); absent = an empty document. */\n value?: JSONContent | undefined\n /** Default `true`. */\n editable?: boolean | undefined\n /** The host's lists; later ones go through `setMetadata`. */\n metadata?: EditorMetadata | undefined\n documentId?: string | undefined\n abilities?: Partial<AbilitiesOptions> | undefined\n commands?: ((defaults: CommandDef[]) => CommandDef[]) | undefined\n /** Added after the features' extensions. */\n extensions?: readonly AnyExtension[] | undefined\n /** Added after the features' block actions. */\n blockActions?: readonly BlockAction[] | undefined\n /** Standalone: the document after every edit (the JSON the session remembers as its own, see `setValue`). */\n onChange?: ((content: JSONContent) => void) | undefined\n /** A new item was created in a metadata list. */\n onMetadataCreate?: ((list: string, item: MetadataItem) => void) | undefined\n /** A document handed in (initially or through `setValue`) does not fit the schema. */\n onContentError?: ((error: Error) => void) | undefined\n /** A mounted editor was registered for the first time (see `register`). */\n onReady?: ((editor: Editor) => void) | undefined\n}\n\n/**\n * Everything of an `EditorSurface` that does not depend on the UI framework: the preset's extensions and context,\n * the document's validation and echo handling, the features' `setup` lifecycle and the registration of the editor's\n * chrome. The framework layer creates the session once (state of the component instance), mounts a TipTap editor from\n * `editorOptions`, and forwards prop changes and lifecycle moments to it.\n */\nexport interface SurfaceSession<C = unknown> {\n /**\n * Options for the TipTap editor. Standalone mode (no `collaboration`) carries the initial document (`content`, already\n * migrated; an empty document when the given one was rejected) and the `onUpdate` that feeds `onChange`. The framework\n * layer may add its own options (rendering flags) but must not replace these.\n */\n readonly editorOptions: Partial<EditorOptions> & { extensions: AnyExtension[] }\n /** The features' overlay components, in feature order; the framework layer renders them beside the editor with `{ editor }`. */\n readonly overlays: readonly C[]\n /**\n * Starts the features' `setup` contributions, then reports a rejected initial document through `onContentError` (once\n * per session, not per activation). A no-op while active. Pair every activation with `deactivate`: React StrictMode\n * mounts, cleans up and mounts again, which must leave exactly one active setup.\n */\n activate: () => void\n /** Runs the teardowns of the active `setup` contributions, in the order they were started; a no-op while inactive. */\n deactivate: () => void\n /**\n * Registers the chrome of `editor` (the raw instance, see `registerEditor`) and announces it through `onReady` the\n * first time this editor is registered, so a host toolbar that receives it finds the chrome. Returns the unregister\n * function (idempotent). Call it again after the framework layer replaced the editor instance.\n */\n register: (editor: Editor) => () => void\n /**\n * The host's document changed (standalone only; ignored with `collaboration`). Not an edit: no `onChange`, no update\n * event. Ignored when `value` is the very object `onChange` last received (the host handing the surface's own JSON\n * back), is absent, or was already given; otherwise it is migrated and checked against the schema and applied, or\n * reported through `onContentError` with the current document kept. A missing or destroyed editor ignores it.\n */\n setValue: (value: JSONContent | undefined) => void\n /**\n * Switches the editor's editable state; ignored when it equals the last given one, and while no (or a destroyed)\n * editor exists. TipTap's `setEditable(…, false)` changes the flag without any event, so it is followed by an empty\n * `addToHistory: false` transaction: components that follow `isEditable` on `transaction` (block UI, the composite\n * inspector) notice. It has no steps, so nothing syncs and the history is untouched.\n */\n setEditable: (editable: boolean) => void\n /** Replaces the host's metadata lists; ignored when `metadata` is the object that was last given. */\n setMetadata: (metadata: EditorMetadata | undefined) => void\n}\n\n/**\n * Builds the session of one mounted surface: metadata registry, feature context, the extension list, the block actions\n * and the initial document. Throws when `collaboration` is given and the preset has no collaboration feature. Extension\n * order is fixed: the preset extension, the suggestion popup, the features' extensions in feature order, the host's.\n * The document is checked as `preset.pipeline.migrateContent` followed by the schema's `check()`; a failure is an error\n * value, never a throw.\n */\nexport function createSurfaceSession<C = unknown>(options: SurfaceSessionOptions<C>): SurfaceSession<C> {\n const { app, preset, ui, collaboration, getEditor } = options\n if (collaboration && !preset.features.some(feature => feature.id === 'collab')) {\n throw new Error('@ecsplico/editor: `collaboration` needs a preset with the collaboration feature')\n }\n // The surface only consumes a `collaboration` session the host opened (`useEditorDocument(id, { collaboration })`\n // opens it through `backend.collaboration` and checks that itself), so the collaboration capability is never needed\n // here: a host with its own provider mounts the collaboration feature over a backend that offers none.\n for (const feature of preset.features) {\n for (const capability of feature.backend ?? []) {\n if (capability === 'collaboration') continue\n if (!app.backend[capability]) {\n throw new Error(`@ecsplico/editor: feature \"${feature.id}\" needs backend capability \"${capability}\", which \"${app.backend.name}\" lacks`)\n }\n }\n }\n\n const registry = createMetadataRegistry({ onCreate: (list, item) => options.onMetadataCreate?.(list, item) })\n registry.replace({ ...options.metadata })\n\n const ctx = createFeatureContext({\n app,\n preset,\n ui,\n metadata: registry,\n notify: options.notify,\n getEditor,\n ...(options.documentId !== undefined && { documentId: options.documentId }),\n ...(collaboration && { collaboration }),\n ...(options.abilities && { abilities: options.abilities }),\n ...(options.commands && { commands: options.commands }),\n })\n // Fails here, not on the first toolbar render, when a command names a toolbar group no feature declares.\n toolbarOf(preset, ctx)\n // A command whose ability the document root does not grant never shows (its state is permanently disabled): say so once.\n const root = new Set<string>(options.abilities?.root ?? ROOT_ABILITIES)\n for (const cmd of ctx.commands()) {\n if (cmd.ability && !root.has(cmd.ability)) {\n console.warn(`@ecsplico/editor: command \"${cmd.id}\" needs ability \"${cmd.ability}\", which neither ROOT_ABILITIES nor \\`abilities.root\\` grants; it stays unavailable at document root`)\n }\n }\n\n const extensions: AnyExtension[] = [\n presetExtension(preset),\n SuggestionPopup.configure({ renderer: options.suggestionRenderer }),\n ...contributions(preset, 'extensions').flatMap(factory => factory(ctx)),\n ...transactionGuardExtensions(preset, ctx),\n ...(options.extensions ?? []),\n ]\n const schema = getSchema(extensions)\n const overlays = preset.features.flatMap(feature => [...(feature.overlays ?? [])])\n const blockActions = blockActionsOf(preset, ctx, options.blockActions)\n\n const validated = (json: JSONContent): { json: JSONContent } | { error: Error } => {\n try {\n const migrated = preset.pipeline.migrateContent(json)\n schema.nodeFromJSON(migrated).check()\n return { json: migrated }\n }\n catch (error) {\n return { error: error instanceof Error ? error : new Error(String(error)) }\n }\n }\n\n // Standalone: the JSON last sent through `onChange`; the host handing it back is not an external change.\n let lastEmitted: JSONContent | undefined\n let pendingContentError: Error | undefined\n const editorOptions: SurfaceSession<C>['editorOptions'] = {\n extensions,\n editable: options.editable ?? true,\n editorProps: { attributes: { class: 'tiptap' } },\n }\n if (!collaboration) {\n const initial = options.value ? validated(options.value) : { json: EMPTY_EDITOR_CONTENT }\n if ('error' in initial) pendingContentError = initial.error\n editorOptions.content = 'json' in initial ? initial.json : EMPTY_EDITOR_CONTENT\n editorOptions.onUpdate = ({ editor }) => {\n lastEmitted = editor.getJSON()\n options.onChange?.(lastEmitted)\n }\n }\n\n // What the setters below already applied; they only react to a change.\n let appliedValue = options.value\n let appliedEditable = options.editable ?? true\n let appliedMetadata = options.metadata\n // Teardowns of the `setup` contributions that are currently active; undefined while inactive.\n let teardowns: (() => void)[] | undefined\n let announced: Editor | undefined\n\n return {\n editorOptions,\n overlays,\n activate() {\n if (teardowns) return\n const active: (() => void)[] = []\n teardowns = active\n for (const run of contributions(preset, 'setup')) {\n const teardown = run(ctx)\n if (teardown) active.push(teardown)\n }\n if (pendingContentError) {\n const error = pendingContentError\n pendingContentError = undefined\n options.onContentError?.(error)\n }\n },\n deactivate() {\n const active = teardowns\n teardowns = undefined\n for (const teardown of active ?? []) teardown()\n },\n register(editor) {\n const unregister = registerEditor(editor, { ui, preset, ctx, blockActions })\n if (announced !== editor) {\n announced = editor\n options.onReady?.(editor)\n }\n return unregister\n },\n setValue(value) {\n if (value === appliedValue) return\n appliedValue = value\n const editor = getEditor()\n if (collaboration || !value || !editor || editor.isDestroyed || value === lastEmitted) return\n const checked = validated(value)\n if ('error' in checked) {\n options.onContentError?.(checked.error)\n return\n }\n editor.commands.setContent(checked.json, { emitUpdate: false })\n },\n setEditable(editable) {\n if (editable === appliedEditable) return\n appliedEditable = editable\n const editor = getEditor()\n if (!editor || editor.isDestroyed) return\n editor.setEditable(editable, false)\n editor.view.dispatch(editor.state.tr.setMeta('addToHistory', false))\n },\n setMetadata(metadata) {\n if (metadata === appliedMetadata) return\n appliedMetadata = metadata\n registry.replace({ ...metadata })\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"surface-session.js","names":[],"sources":["../../src/core/surface-session.ts"],"sourcesContent":["import { type AnyExtension, type Editor, type EditorOptions, getSchema, type JSONContent } from '@tiptap/core'\nimport type { EditorContext } from './context'\nimport { contributions } from './define-editor'\nimport { type EditorUi, registerEditor } from './editor-ui'\nimport { type CommandDef, toolbarOf } from './commands'\nimport type { AbilitiesOptions, BlockAction } from './extension-points'\nimport type { CollaborationSession, EditorPreset } from './feature'\nimport { blockActionsOf, createFeatureContext } from './feature-context'\nimport { createMetadataRegistry } from './metadata'\nimport type { EditorMetadata, MetadataItem } from './metadata-types'\nimport type { Notifier } from './notify'\nimport { presetExtension } from './pipeline'\nimport { EMPTY_EDITOR_CONTENT } from './schema'\nimport { ROOT_ABILITIES } from './schema/abilities'\nimport { SuggestionPopup, type SuggestionPopupRenderer } from './suggestion-popup'\nimport { transactionGuardExtensions } from './transaction-guards'\n\n/**\n * What a surface hands its session. The framework layer owns everything that lives in its reactivity: the chrome\n * (`ui`), the notice renderer, the suggestion popup renderer and the editor instance (`getEditor`).\n * `preset`, `collaboration`, `documentId`, `abilities`, `extensions`, `blockActions`, `commands`, `value` and\n * `editable` are read once, when the session is created; later changes reach it through `setValue`, `setEditable` and\n * `setMetadata`.\n */\nexport interface SurfaceSessionOptions<C = unknown> {\n app: EditorContext\n /** The features the editor is made of. With `collaboration` it must contain the collaboration feature (else creating the session throws). */\n preset: EditorPreset<C>\n /** The chrome the features reach through `FeatureContext.ui`; registered for the editor by `register`. */\n ui: EditorUi\n /** The surface's notice renderer (`FeatureContext.notify`). */\n notify: Notifier\n /** The framework's popup for `@` / `#` / `/` suggestions. */\n suggestionRenderer: SuggestionPopupRenderer\n /** The mounted editor, `undefined` until the framework layer created it; used by `FeatureContext.getEditor`, `setValue` and `setEditable`. */\n getEditor: () => Editor | undefined\n /** Collaborative mode: the Y.Doc is the document, so there is no `value` and no `onChange`. */\n collaboration?: CollaborationSession | undefined\n /** Standalone initial document (TipTap JSON, unproxied); absent = an empty document. */\n value?: JSONContent | undefined\n /** Default `true`. */\n editable?: boolean | undefined\n /** The host's lists; later ones go through `setMetadata`. */\n metadata?: EditorMetadata | undefined\n documentId?: string | undefined\n abilities?: Partial<AbilitiesOptions> | undefined\n commands?: ((defaults: CommandDef[]) => CommandDef[]) | undefined\n /** Added after the features' extensions. */\n extensions?: readonly AnyExtension[] | undefined\n /** Added after the features' block actions. */\n blockActions?: readonly BlockAction[] | undefined\n /** Standalone: the document after every edit (the JSON the session remembers as its own, see `setValue`). */\n onChange?: ((content: JSONContent) => void) | undefined\n /** A new item was created in a metadata list. */\n onMetadataCreate?: ((list: string, item: MetadataItem) => void) | undefined\n /** A document handed in (initially or through `setValue`) does not fit the schema. */\n onContentError?: ((error: Error) => void) | undefined\n /** A mounted editor was registered for the first time (see `register`). */\n onReady?: ((editor: Editor) => void) | undefined\n}\n\n/**\n * Everything of an `EditorSurface` that does not depend on the UI framework: the preset's extensions and context,\n * the document's validation and echo handling, the features' `setup` lifecycle and the registration of the editor's\n * chrome. The framework layer creates the session once (state of the component instance), mounts a TipTap editor from\n * `editorOptions`, and forwards prop changes and lifecycle moments to it.\n */\nexport interface SurfaceSession<C = unknown> {\n /**\n * Options for the TipTap editor. Standalone mode (no `collaboration`) carries the initial document (`content`, already\n * migrated; an empty document when the given one was rejected) and the `onUpdate` that feeds `onChange`. The framework\n * layer may add its own options (rendering flags) but must not replace these.\n */\n readonly editorOptions: Partial<EditorOptions> & { extensions: AnyExtension[] }\n /** The features' overlay components, in feature order; the framework layer renders them beside the editor with `{ editor }`. */\n readonly overlays: readonly C[]\n /**\n * Starts the features' `setup` contributions, then reports a rejected initial document through `onContentError` (once\n * per session, not per activation). A no-op while active. Pair every activation with `deactivate`: React StrictMode\n * mounts, cleans up and mounts again, which must leave exactly one active setup.\n */\n activate: () => void\n /** Runs the teardowns of the active `setup` contributions, in the order they were started; a no-op while inactive. */\n deactivate: () => void\n /**\n * Registers the chrome of `editor` (the raw instance, see `registerEditor`) and announces it through `onReady` the\n * first time this editor is registered, so a host toolbar that receives it finds the chrome. Returns the unregister\n * function (idempotent). Call it again after the framework layer replaced the editor instance.\n */\n register: (editor: Editor) => () => void\n /**\n * The host's document changed (standalone only; ignored with `collaboration`). Not an edit: no `onChange`, no update\n * event. Ignored when `value` is the very object `onChange` last received (the host handing the surface's own JSON\n * back), is absent, or was already given; otherwise it is migrated and checked against the schema and applied, or\n * reported through `onContentError` with the current document kept. A missing or destroyed editor ignores it.\n */\n setValue: (value: JSONContent | undefined) => void\n /**\n * Switches the editor's editable state; ignored when it equals the last given one, and while no (or a destroyed)\n * editor exists. TipTap's `setEditable(…, false)` changes the flag without any event, so it is followed by an empty\n * `addToHistory: false` transaction: components that follow `isEditable` on `transaction` (block UI, the composite\n * inspector) notice. It has no steps, so nothing syncs and the history is untouched.\n */\n setEditable: (editable: boolean) => void\n /** Replaces the host's metadata lists; ignored when `metadata` is the object that was last given. */\n setMetadata: (metadata: EditorMetadata | undefined) => void\n}\n\n/**\n * Builds the session of one mounted surface: metadata registry, feature context, the extension list, the block actions\n * and the initial document. Throws when `collaboration` is given and the preset has no collaboration feature. Extension\n * order is fixed: the preset extension, the suggestion popup, the features' extensions in feature order, the host's.\n * The document is checked as `preset.pipeline.migrateContent` followed by the schema's `check()`; a failure is an error\n * value, never a throw.\n */\nexport function createSurfaceSession<C = unknown>(options: SurfaceSessionOptions<C>): SurfaceSession<C> {\n const { app, preset, ui, collaboration, getEditor } = options\n if (collaboration && !preset.features.some(feature => feature.id === 'collab')) {\n throw new Error('@ecsplico/editor: `collaboration` needs a preset with the collaboration feature')\n }\n // The surface only consumes a `collaboration` session the host opened (`useEditorDocument(id, { collaboration })`\n // opens it through `backend.collaboration` and checks that itself), so the collaboration capability is never needed\n // here: a host with its own provider mounts the collaboration feature over a backend that offers none.\n for (const feature of preset.features) {\n for (const capability of feature.backend ?? []) {\n if (capability === 'collaboration') continue\n if (!app.backend[capability]) {\n throw new Error(`@ecsplico/editor: feature \"${feature.id}\" needs backend capability \"${capability}\", which \"${app.backend.name}\" lacks`)\n }\n }\n }\n\n const registry = createMetadataRegistry({ onCreate: (list, item) => options.onMetadataCreate?.(list, item) })\n registry.replace({ ...options.metadata })\n\n const ctx = createFeatureContext({\n app,\n preset,\n ui,\n metadata: registry,\n notify: options.notify,\n getEditor,\n ...(options.documentId !== undefined && { documentId: options.documentId }),\n ...(collaboration && { collaboration }),\n ...(options.abilities && { abilities: options.abilities }),\n ...(options.commands && { commands: options.commands }),\n })\n // Fails here, not on the first toolbar render, when a command names a toolbar group no feature declares.\n toolbarOf(preset, ctx)\n // A command whose ability the document root does not grant never shows (its state is permanently disabled): say so once.\n const root = new Set<string>(options.abilities?.root ?? ROOT_ABILITIES)\n for (const cmd of ctx.commands()) {\n if (cmd.ability && !root.has(cmd.ability)) {\n console.warn(`@ecsplico/editor: command \"${cmd.id}\" needs ability \"${cmd.ability}\", which neither ROOT_ABILITIES nor \\`abilities.root\\` grants; it stays unavailable at document root`)\n }\n }\n\n const extensions: AnyExtension[] = [\n presetExtension(preset),\n SuggestionPopup.configure({ renderer: options.suggestionRenderer }),\n ...contributions(preset, 'extensions').flatMap(factory => factory(ctx)),\n ...transactionGuardExtensions(preset, ctx),\n ...(options.extensions ?? []),\n ]\n const schema = getSchema(extensions)\n const overlays = preset.features.flatMap(feature => [...(feature.overlays ?? [])])\n const blockActions = blockActionsOf(preset, ctx, options.blockActions)\n const blockUi = preset.features.find(feature => feature.blockUi)?.blockUi\n\n const validated = (json: JSONContent): { json: JSONContent } | { error: Error } => {\n try {\n const migrated = preset.pipeline.migrateContent(json)\n schema.nodeFromJSON(migrated).check()\n return { json: migrated }\n }\n catch (error) {\n return { error: error instanceof Error ? error : new Error(String(error)) }\n }\n }\n\n // Standalone: the JSON last sent through `onChange`; the host handing it back is not an external change.\n let lastEmitted: JSONContent | undefined\n let pendingContentError: Error | undefined\n const editorOptions: SurfaceSession<C>['editorOptions'] = {\n extensions,\n editable: options.editable ?? true,\n editorProps: { attributes: { class: 'tiptap' } },\n }\n if (!collaboration) {\n const initial = options.value ? validated(options.value) : { json: EMPTY_EDITOR_CONTENT }\n if ('error' in initial) pendingContentError = initial.error\n editorOptions.content = 'json' in initial ? initial.json : EMPTY_EDITOR_CONTENT\n editorOptions.onUpdate = ({ editor }) => {\n lastEmitted = editor.getJSON()\n options.onChange?.(lastEmitted)\n }\n }\n\n // What the setters below already applied; they only react to a change.\n let appliedValue = options.value\n let appliedEditable = options.editable ?? true\n let appliedMetadata = options.metadata\n // Teardowns of the `setup` contributions that are currently active; undefined while inactive.\n let teardowns: (() => void)[] | undefined\n let announced: Editor | undefined\n\n return {\n editorOptions,\n overlays,\n activate() {\n if (teardowns) return\n const active: (() => void)[] = []\n teardowns = active\n for (const run of contributions(preset, 'setup')) {\n const teardown = run(ctx)\n if (teardown) active.push(teardown)\n }\n if (pendingContentError) {\n const error = pendingContentError\n pendingContentError = undefined\n options.onContentError?.(error)\n }\n },\n deactivate() {\n const active = teardowns\n teardowns = undefined\n for (const teardown of active ?? []) teardown()\n },\n register(editor) {\n const unregister = registerEditor(editor, { ui, preset, ctx, blockActions, blockUi })\n if (announced !== editor) {\n announced = editor\n options.onReady?.(editor)\n }\n return unregister\n },\n setValue(value) {\n if (value === appliedValue) return\n appliedValue = value\n const editor = getEditor()\n if (collaboration || !value || !editor || editor.isDestroyed || value === lastEmitted) return\n const checked = validated(value)\n if ('error' in checked) {\n options.onContentError?.(checked.error)\n return\n }\n editor.commands.setContent(checked.json, { emitUpdate: false })\n },\n setEditable(editable) {\n if (editable === appliedEditable) return\n appliedEditable = editable\n const editor = getEditor()\n if (!editor || editor.isDestroyed) return\n editor.setEditable(editable, false)\n editor.view.dispatch(editor.state.tr.setMeta('addToHistory', false))\n },\n setMetadata(metadata) {\n if (metadata === appliedMetadata) return\n appliedMetadata = metadata\n registry.replace({ ...metadata })\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmHA,SAAgB,qBAAkC,SAAsD;CACtG,MAAM,EAAE,KAAK,QAAQ,IAAI,eAAe,cAAc;CACtD,IAAI,iBAAiB,CAAC,OAAO,SAAS,MAAK,YAAW,QAAQ,OAAO,QAAQ,GAC3E,MAAM,IAAI,MAAM,iFAAiF;CAKnG,KAAK,MAAM,WAAW,OAAO,UAC3B,KAAK,MAAM,cAAc,QAAQ,WAAW,CAAC,GAAG;EAC9C,IAAI,eAAe,iBAAiB;EACpC,IAAI,CAAC,IAAI,QAAQ,aACf,MAAM,IAAI,MAAM,8BAA8B,QAAQ,GAAG,8BAA8B,WAAW,YAAY,IAAI,QAAQ,KAAK,QAAQ;CAE3I;CAGF,MAAM,WAAW,uBAAuB,EAAE,WAAW,MAAM,SAAS,QAAQ,mBAAmB,MAAM,IAAI,EAAE,CAAC;CAC5G,SAAS,QAAQ,EAAE,GAAG,QAAQ,SAAS,CAAC;CAExC,MAAM,MAAM,qBAAqB;EAC/B;EACA;EACA;EACA,UAAU;EACV,QAAQ,QAAQ;EAChB;EACA,GAAI,QAAQ,eAAe,KAAA,KAAa,EAAE,YAAY,QAAQ,WAAW;EACzE,GAAI,iBAAiB,EAAE,cAAc;EACrC,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,UAAU;EACxD,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;CACvD,CAAC;CAED,UAAU,QAAQ,GAAG;CAErB,MAAM,OAAO,IAAI,IAAY,QAAQ,WAAW,QAAQ,cAAc;CACtE,KAAK,MAAM,OAAO,IAAI,SAAS,GAC7B,IAAI,IAAI,WAAW,CAAC,KAAK,IAAI,IAAI,OAAO,GACtC,QAAQ,KAAK,8BAA8B,IAAI,GAAG,mBAAmB,IAAI,QAAQ,qGAAqG;CAI1L,MAAM,aAA6B;EACjC,gBAAgB,MAAM;EACtB,gBAAgB,UAAU,EAAE,UAAU,QAAQ,mBAAmB,CAAC;EAClE,GAAG,cAAc,QAAQ,YAAY,CAAC,CAAC,SAAQ,YAAW,QAAQ,GAAG,CAAC;EACtE,GAAG,2BAA2B,QAAQ,GAAG;EACzC,GAAI,QAAQ,cAAc,CAAC;CAC7B;CACA,MAAM,SAAS,UAAU,UAAU;CACnC,MAAM,WAAW,OAAO,SAAS,SAAQ,YAAW,CAAC,GAAI,QAAQ,YAAY,CAAC,CAAE,CAAC;CACjF,MAAM,eAAe,eAAe,QAAQ,KAAK,QAAQ,YAAY;CACrE,MAAM,UAAU,OAAO,SAAS,MAAK,YAAW,QAAQ,OAAO,CAAC,EAAE;CAElE,MAAM,aAAa,SAAgE;EACjF,IAAI;GACF,MAAM,WAAW,OAAO,SAAS,eAAe,IAAI;GACpD,OAAO,aAAa,QAAQ,CAAC,CAAC,MAAM;GACpC,OAAO,EAAE,MAAM,SAAS;EAC1B,SACO,OAAO;GACZ,OAAO,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,EAAE;EAC5E;CACF;CAGA,IAAI;CACJ,IAAI;CACJ,MAAM,gBAAoD;EACxD;EACA,UAAU,QAAQ,YAAY;EAC9B,aAAa,EAAE,YAAY,EAAE,OAAO,SAAS,EAAE;CACjD;CACA,IAAI,CAAC,eAAe;EAClB,MAAM,UAAU,QAAQ,QAAQ,UAAU,QAAQ,KAAK,IAAI,EAAE,MAAM,qBAAqB;EACxF,IAAI,WAAW,SAAS,sBAAsB,QAAQ;EACtD,cAAc,UAAU,UAAU,UAAU,QAAQ,OAAO;EAC3D,cAAc,YAAY,EAAE,aAAa;GACvC,cAAc,OAAO,QAAQ;GAC7B,QAAQ,WAAW,WAAW;EAChC;CACF;CAGA,IAAI,eAAe,QAAQ;CAC3B,IAAI,kBAAkB,QAAQ,YAAY;CAC1C,IAAI,kBAAkB,QAAQ;CAE9B,IAAI;CACJ,IAAI;CAEJ,OAAO;EACL;EACA;EACA,WAAW;GACT,IAAI,WAAW;GACf,MAAM,SAAyB,CAAC;GAChC,YAAY;GACZ,KAAK,MAAM,OAAO,cAAc,QAAQ,OAAO,GAAG;IAChD,MAAM,WAAW,IAAI,GAAG;IACxB,IAAI,UAAU,OAAO,KAAK,QAAQ;GACpC;GACA,IAAI,qBAAqB;IACvB,MAAM,QAAQ;IACd,sBAAsB,KAAA;IACtB,QAAQ,iBAAiB,KAAK;GAChC;EACF;EACA,aAAa;GACX,MAAM,SAAS;GACf,YAAY,KAAA;GACZ,KAAK,MAAM,YAAY,UAAU,CAAC,GAAG,SAAS;EAChD;EACA,SAAS,QAAQ;GACf,MAAM,aAAa,eAAe,QAAQ;IAAE;IAAI;IAAQ;IAAK;IAAc;GAAQ,CAAC;GACpF,IAAI,cAAc,QAAQ;IACxB,YAAY;IACZ,QAAQ,UAAU,MAAM;GAC1B;GACA,OAAO;EACT;EACA,SAAS,OAAO;GACd,IAAI,UAAU,cAAc;GAC5B,eAAe;GACf,MAAM,SAAS,UAAU;GACzB,IAAI,iBAAiB,CAAC,SAAS,CAAC,UAAU,OAAO,eAAe,UAAU,aAAa;GACvF,MAAM,UAAU,UAAU,KAAK;GAC/B,IAAI,WAAW,SAAS;IACtB,QAAQ,iBAAiB,QAAQ,KAAK;IACtC;GACF;GACA,OAAO,SAAS,WAAW,QAAQ,MAAM,EAAE,YAAY,MAAM,CAAC;EAChE;EACA,YAAY,UAAU;GACpB,IAAI,aAAa,iBAAiB;GAClC,kBAAkB;GAClB,MAAM,SAAS,UAAU;GACzB,IAAI,CAAC,UAAU,OAAO,aAAa;GACnC,OAAO,YAAY,UAAU,KAAK;GAClC,OAAO,KAAK,SAAS,OAAO,MAAM,GAAG,QAAQ,gBAAgB,KAAK,CAAC;EACrE;EACA,YAAY,UAAU;GACpB,IAAI,aAAa,iBAAiB;GAClC,kBAAkB;GAClB,SAAS,QAAQ,EAAE,GAAG,SAAS,CAAC;EAClC;CACF;AACF"}
|
|
@@ -112,7 +112,7 @@ function coreCommands(ctx) {
|
|
|
112
112
|
ability: ABILITY.headings,
|
|
113
113
|
command: (c) => c.toggleHeading({ level }),
|
|
114
114
|
active: (e) => e.isActive("heading", { level }),
|
|
115
|
-
visible: headingsShown
|
|
115
|
+
visible: (editor) => headingsShown(editor) && ((editor.extensionManager.extensions.find((extension) => extension.name === "heading")?.options.levels)?.includes(level) ?? true)
|
|
116
116
|
})),
|
|
117
117
|
command({
|
|
118
118
|
id: "bold",
|