@ecsplico/editor 3.5.1 → 4.0.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 +71 -0
- package/dist/adapters/direct-ai.d.ts +4 -4
- package/dist/adapters/direct-ai.js +2 -2
- package/dist/adapters/direct-ai.js.map +1 -1
- package/dist/adapters/document-state.d.ts +1 -1
- package/dist/adapters/document-state.js.map +1 -1
- package/dist/adapters/forge.d.ts +2 -2
- package/dist/adapters/forge.js.map +1 -1
- package/dist/adapters/nitro.d.ts +11 -3
- package/dist/adapters/nitro.js +3 -1
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +6 -6
- package/dist/adapters/shared.js.map +1 -1
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/config/index.d.ts +14 -14
- package/dist/config/index.js +8 -8
- package/dist/config/index.js.map +1 -1
- package/dist/core/backend-types.d.ts +3 -3
- package/dist/core/backend-types.js.map +1 -1
- package/dist/core/backend.d.ts +4 -4
- package/dist/core/backend.js.map +1 -1
- package/dist/core/block-convert.d.ts +1 -1
- package/dist/core/block-convert.js +5 -5
- package/dist/core/block-convert.js.map +1 -1
- package/dist/core/block-transfer.d.ts +8 -4
- package/dist/core/block-transfer.js +23 -14
- package/dist/core/block-transfer.js.map +1 -1
- package/dist/core/capabilities.d.ts +113 -0
- package/dist/core/capabilities.js +155 -0
- package/dist/core/capabilities.js.map +1 -0
- package/dist/core/command-types.d.ts +1 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/convert.d.ts +5 -5
- package/dist/core/convert.js +5 -5
- package/dist/core/convert.js.map +1 -1
- package/dist/core/core-feature.js +1 -1
- package/dist/core/define-editor.d.ts +2 -2
- package/dist/core/define-editor.js +34 -6
- package/dist/core/define-editor.js.map +1 -1
- package/dist/core/derivation.d.ts +119 -0
- package/dist/core/derivation.js +187 -0
- package/dist/core/derivation.js.map +1 -0
- package/dist/core/derive.d.ts +61 -0
- package/dist/core/derive.js +69 -0
- package/dist/core/derive.js.map +1 -0
- package/dist/core/extension-points.d.ts +16 -0
- package/dist/core/feature-context.d.ts +5 -1
- package/dist/core/feature-context.js +10 -3
- package/dist/core/feature-context.js.map +1 -1
- package/dist/core/feature.d.ts +41 -19
- package/dist/core/feature.js.map +1 -1
- package/dist/core/fences.js +1 -1
- package/dist/core/fences.js.map +1 -1
- package/dist/core/index.d.ts +6 -2
- package/dist/core/index.js +5 -1
- package/dist/core/pipeline.d.ts +14 -5
- package/dist/core/pipeline.js +19 -7
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/properties.d.ts +22 -0
- package/dist/core/properties.js +17 -0
- package/dist/core/properties.js.map +1 -0
- package/dist/core/schema/block-ids.js +5 -5
- package/dist/core/schema/block-ids.js.map +1 -1
- package/dist/core/schema/index.d.ts +2 -1
- package/dist/core/schema/index.js +2 -1
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/schema/opaque-fence.d.ts +4 -4
- package/dist/core/schema/opaque-fence.js +7 -7
- package/dist/core/schema/opaque-fence.js.map +1 -1
- package/dist/core/schema/persisted-names.d.ts +58 -0
- package/dist/core/schema/persisted-names.js +96 -0
- package/dist/core/schema/persisted-names.js.map +1 -0
- package/dist/core/toolbar-items.js +1 -1
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/core/util.js +5 -1
- package/dist/core/util.js.map +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/agent-ops.js +1 -1
- package/dist/features/ai/ai-review.js +1 -1
- package/dist/features/ai/assistant.d.ts +4 -4
- package/dist/features/ai/assistant.js +1 -1
- package/dist/features/ai/assistant.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.js +7 -7
- package/dist/features/ai/components/ModelQuickSwitch.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.vue.d.ts +4 -4
- package/dist/features/ai/components/SettingsAiTab.js +8 -8
- package/dist/features/ai/components/SettingsAiTab.js.map +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/document-chat-model.d.ts +2 -2
- package/dist/features/ai/document-chat-model.js +5 -5
- package/dist/features/ai/document-chat-model.js.map +1 -1
- package/dist/features/ai/document-chat.js +2 -2
- package/dist/features/ai/document-chat.js.map +1 -1
- package/dist/features/ai/feature.d.ts +2 -2
- package/dist/features/ai/feature.js +2 -2
- package/dist/features/ai/feature.js.map +1 -1
- package/dist/features/ai/ghost-autocomplete.d.ts +4 -4
- package/dist/features/ai/ghost-autocomplete.js +1 -1
- package/dist/features/ai/ghost-autocomplete.js.map +1 -1
- package/dist/features/ai/index.d.ts +3 -3
- package/dist/features/ai/index.js +2 -2
- package/dist/features/ai/model-profiles.d.ts +9 -9
- package/dist/features/ai/model-profiles.js +13 -13
- package/dist/features/ai/model-profiles.js.map +1 -1
- package/dist/features/ai/protocol/agent-ops.d.ts +2 -2
- package/dist/features/ai/protocol/agent-ops.js +1 -1
- package/dist/features/ai/protocol/agent-ops.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.d.ts +7 -7
- package/dist/features/ai/protocol/block-lines.js +10 -10
- package/dist/features/ai/protocol/block-lines.js.map +1 -1
- package/dist/features/ai/protocol/chat.d.ts +5 -5
- package/dist/features/ai/protocol/chat.js +12 -12
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/generate.d.ts +1 -1
- package/dist/features/ai/protocol/generate.js +1 -1
- package/dist/features/ai/protocol/generate.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -3
- package/dist/features/ai/protocol/index.js +2 -2
- package/dist/features/ai/protocol/models.d.ts +3 -3
- package/dist/features/ai/protocol/models.js +3 -3
- package/dist/features/ai/protocol/models.js.map +1 -1
- package/dist/features/ai/protocol/schemas.d.ts +1 -1
- package/dist/features/ai/protocol/schemas.js +1 -1
- package/dist/features/ai/protocol/schemas.js.map +1 -1
- package/dist/features/ai/protocol/tasks.d.ts +5 -5
- package/dist/features/ai/protocol/tasks.js +12 -12
- package/dist/features/ai/protocol/tasks.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/react/ModelQuickSwitch.d.ts +4 -4
- package/dist/features/ai/react/ModelQuickSwitch.js +6 -6
- package/dist/features/ai/react/ModelQuickSwitch.js.map +1 -1
- package/dist/features/ai/react/SettingsAiTab.d.ts +1 -1
- package/dist/features/ai/react/SettingsAiTab.js +8 -8
- package/dist/features/ai/react/SettingsAiTab.js.map +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.d.ts +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/react/use-ai-assistant.js +1 -1
- package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/react/use-model-profiles.d.ts +5 -5
- package/dist/features/ai/react/use-model-profiles.js +2 -2
- package/dist/features/ai/react/use-model-profiles.js.map +1 -1
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-ai-assistant.js +1 -1
- package/dist/features/ai/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/use-model-profiles.d.ts +4 -4
- package/dist/features/ai/use-model-profiles.js +2 -2
- package/dist/features/ai/use-model-profiles.js.map +1 -1
- package/dist/features/block-ui/block-actions.d.ts +8 -4
- package/dist/features/block-ui/block-actions.js +38 -22
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/block-target.d.ts +29 -0
- package/dist/features/block-ui/block-target.js +63 -0
- package/dist/features/block-ui/block-target.js.map +1 -0
- package/dist/features/block-ui/components/BlockHandles.js +3 -3
- package/dist/features/block-ui/components/BlockHandles.js.map +1 -1
- package/dist/features/block-ui/components/BlockUi.js +11 -23
- package/dist/features/block-ui/components/BlockUi.js.map +1 -1
- package/dist/features/block-ui/feature.d.ts +3 -2
- package/dist/features/block-ui/feature.js +3 -2
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/handle-geometry.js +12 -23
- package/dist/features/block-ui/handle-geometry.js.map +1 -1
- package/dist/features/block-ui/index.d.ts +2 -1
- package/dist/features/block-ui/index.js +2 -1
- package/dist/features/block-ui/react/BlockHandles.d.ts +1 -1
- package/dist/features/block-ui/react/BlockHandles.js +4 -4
- package/dist/features/block-ui/react/BlockHandles.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.js +13 -25
- package/dist/features/block-ui/react/BlockUi.js.map +1 -1
- package/dist/features/comments/model.js +1 -1
- package/dist/features/composites/accepted-children.d.ts +5 -4
- package/dist/features/composites/accepted-children.js +26 -26
- package/dist/features/composites/accepted-children.js.map +1 -1
- package/dist/features/composites/ai-brief.d.ts +4 -4
- package/dist/features/composites/ai-brief.js +14 -12
- package/dist/features/composites/ai-brief.js.map +1 -1
- package/dist/features/composites/anchors.d.ts +1 -1
- package/dist/features/composites/anchors.js.map +1 -1
- package/dist/features/composites/baskets.d.ts +1 -1
- package/dist/features/composites/baskets.js.map +1 -1
- package/dist/features/composites/bibliography-command.d.ts +1 -1
- package/dist/features/composites/bibliography-form.js +1 -1
- package/dist/features/composites/bibliography-form.js.map +1 -1
- package/dist/features/composites/bibliography.js.map +1 -1
- package/dist/features/composites/block-types-json.d.ts +38 -0
- package/dist/features/composites/block-types-json.js +122 -0
- package/dist/features/composites/block-types-json.js.map +1 -0
- package/dist/features/composites/block-types.d.ts +119 -0
- package/dist/features/composites/block-types.js +343 -0
- package/dist/features/composites/block-types.js.map +1 -0
- package/dist/features/composites/capabilities.js +109 -0
- package/dist/features/composites/capabilities.js.map +1 -0
- package/dist/features/composites/components/BibliographyEditor.js +5 -5
- package/dist/features/composites/components/BibliographyEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeBasketMenu.js +2 -2
- package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -1
- package/dist/features/composites/components/CompositeChrome.js +18 -8
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeDetails.js +11 -10
- package/dist/features/composites/components/CompositeDetails.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeFieldEditor.js +7 -7
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeFieldEditor.vue.d.ts +5 -5
- package/dist/features/composites/components/CompositeInspector.js +136 -113
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/components/CompositeInspector.vue.d.ts +3 -3
- package/dist/features/composites/components/CompositeStatusMenu.js +2 -2
- package/dist/features/composites/components/CompositeStatusMenu.js.map +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeTable.js +2 -2
- package/dist/features/composites/components/CompositeTable.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeView.js +19 -17
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +15 -6
- package/dist/features/composites/components/DerivationBadge.js.map +1 -1
- package/dist/features/composites/components/FocusChip.js +1 -1
- package/dist/features/composites/components/FocusChip.js.map +1 -1
- package/dist/features/composites/components/FocusChip.vue.d.ts +1 -1
- package/dist/features/composites/components/ReferenceFieldSelect.js +3 -3
- package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -1
- package/dist/features/composites/components/ReferenceView.js +1 -1
- package/dist/features/composites/components/ReferenceView.js.map +1 -1
- package/dist/features/composites/components/SettingsEditingTab.js +18 -18
- package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/components/SettingsEditingTab.vue.d.ts +3 -3
- package/dist/features/composites/convert.d.ts +4 -4
- package/dist/features/composites/convert.js +27 -28
- package/dist/features/composites/convert.js.map +1 -1
- package/dist/features/composites/derived-chrome.js +34 -28
- package/dist/features/composites/derived-chrome.js.map +1 -1
- package/dist/features/composites/editing-settings-keys.js +6 -6
- package/dist/features/composites/editing-settings-keys.js.map +1 -1
- package/dist/features/composites/feature.d.ts +10 -10
- package/dist/features/composites/feature.js +58 -46
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/fences.d.ts +3 -3
- package/dist/features/composites/fences.js +18 -13
- package/dist/features/composites/fences.js.map +1 -1
- package/dist/features/composites/focus.d.ts +10 -10
- package/dist/features/composites/focus.js +11 -11
- package/dist/features/composites/focus.js.map +1 -1
- package/dist/features/composites/heading-composites.d.ts +5 -5
- package/dist/features/composites/heading-composites.js +13 -16
- package/dist/features/composites/heading-composites.js.map +1 -1
- package/dist/features/composites/index.d.ts +20 -18
- package/dist/features/composites/index.js +9 -7
- package/dist/features/composites/inspector-model.d.ts +45 -22
- package/dist/features/composites/inspector-model.js +53 -14
- package/dist/features/composites/inspector-model.js.map +1 -1
- package/dist/features/composites/legacy.d.ts +5 -4
- package/dist/features/composites/legacy.js +16 -23
- package/dist/features/composites/legacy.js.map +1 -1
- package/dist/features/composites/markdown-export.js +3 -3
- package/dist/features/composites/markdown-export.js.map +1 -1
- package/dist/features/composites/merge.d.ts +8 -7
- package/dist/features/composites/merge.js +39 -12
- package/dist/features/composites/merge.js.map +1 -1
- package/dist/features/composites/meta-display.js +5 -5
- package/dist/features/composites/meta-display.js.map +1 -1
- package/dist/features/composites/meta-keys.d.ts +5 -5
- package/dist/features/composites/meta-keys.js +8 -8
- package/dist/features/composites/meta-keys.js.map +1 -1
- package/dist/features/composites/node-views.d.ts +3 -3
- package/dist/features/composites/node-views.js +2 -2
- package/dist/features/composites/node-views.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +13 -13
- package/dist/features/composites/nodes.js +100 -77
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/outline.d.ts +15 -12
- package/dist/features/composites/outline.js +24 -21
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/properties.d.ts +51 -0
- package/dist/features/composites/properties.js +89 -0
- package/dist/features/composites/properties.js.map +1 -0
- package/dist/features/composites/react/BibliographyEditor.js +5 -5
- package/dist/features/composites/react/BibliographyEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeBasketMenu.js +2 -2
- package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -1
- package/dist/features/composites/react/CompositeChrome.d.ts +1 -1
- package/dist/features/composites/react/CompositeChrome.js +15 -6
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.d.ts +1 -1
- package/dist/features/composites/react/CompositeDetails.js +11 -10
- package/dist/features/composites/react/CompositeDetails.js.map +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.d.ts +5 -5
- package/dist/features/composites/react/CompositeFieldEditor.js +6 -6
- package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeInspector.d.ts +3 -3
- package/dist/features/composites/react/CompositeInspector.js +142 -128
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.js +2 -2
- package/dist/features/composites/react/CompositeStatusMenu.js.map +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeTable.d.ts +1 -1
- package/dist/features/composites/react/CompositeTable.js +3 -3
- package/dist/features/composites/react/CompositeTable.js.map +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeView.js +17 -15
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +13 -4
- package/dist/features/composites/react/DerivationBadge.js.map +1 -1
- package/dist/features/composites/react/FocusChip.d.ts +1 -1
- package/dist/features/composites/react/FocusChip.js +1 -1
- package/dist/features/composites/react/FocusChip.js.map +1 -1
- package/dist/features/composites/react/ReferenceFieldSelect.js +3 -3
- package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -1
- package/dist/features/composites/react/ReferenceView.d.ts +1 -1
- package/dist/features/composites/react/ReferenceView.js +1 -1
- package/dist/features/composites/react/ReferenceView.js.map +1 -1
- package/dist/features/composites/react/SettingsEditingTab.d.ts +4 -4
- package/dist/features/composites/react/SettingsEditingTab.js +20 -20
- package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/react.js +1 -1
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/reference-ai.js +1 -1
- package/dist/features/composites/reference-ai.js.map +1 -1
- package/dist/features/composites/reference-label.d.ts +3 -3
- package/dist/features/composites/reference-label.js +6 -6
- package/dist/features/composites/reference-label.js.map +1 -1
- package/dist/features/composites/reference-suggestion.d.ts +1 -1
- package/dist/features/composites/reference-suggestion.js +3 -3
- package/dist/features/composites/reference-suggestion.js.map +1 -1
- package/dist/features/composites/reference-text.d.ts +2 -2
- package/dist/features/composites/reference-text.js +4 -4
- package/dist/features/composites/reference-text.js.map +1 -1
- package/dist/features/composites/reference.d.ts +3 -3
- package/dist/features/composites/reference.js +1 -1
- package/dist/features/composites/reference.js.map +1 -1
- package/dist/features/composites/reveal-source.d.ts +3 -3
- package/dist/features/composites/reveal-source.js +6 -6
- package/dist/features/composites/reveal-source.js.map +1 -1
- package/dist/features/composites/rows-model.d.ts +2 -2
- package/dist/features/composites/rows-model.js +4 -3
- package/dist/features/composites/rows-model.js.map +1 -1
- package/dist/features/composites/rows.d.ts +16 -28
- package/dist/features/composites/rows.js +20 -54
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/features/composites/schema.d.ts +12 -11
- package/dist/features/composites/schema.js +10 -9
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/source.d.ts +20 -0
- package/dist/features/composites/source.js +40 -0
- package/dist/features/composites/source.js.map +1 -0
- package/dist/features/composites/split.d.ts +11 -11
- package/dist/features/composites/split.js +20 -20
- package/dist/features/composites/split.js.map +1 -1
- package/dist/features/composites/structure-mode.js +3 -3
- package/dist/features/composites/structure-mode.js.map +1 -1
- package/dist/features/composites/table-model.js +2 -2
- package/dist/features/composites/table-model.js.map +1 -1
- package/dist/features/composites/use-composite-inspector.js +3 -1
- package/dist/features/composites/use-composite-inspector.js.map +1 -1
- package/dist/features/composites/use-composite-rows.d.ts +2 -2
- package/dist/features/composites/use-composite-rows.js.map +1 -1
- package/dist/features/composites/vue.js +1 -1
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.js +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +1 -1
- package/dist/features/image-studio/components/ImageStudioView.js.map +1 -1
- package/dist/features/image-studio/feature.d.ts +1 -1
- package/dist/features/image-studio/feature.js +1 -1
- package/dist/features/image-studio/feature.js.map +1 -1
- package/dist/features/image-studio/node-core.d.ts +3 -3
- package/dist/features/image-studio/node-core.js +1 -1
- package/dist/features/image-studio/node-core.js.map +1 -1
- package/dist/features/image-studio/node-store.d.ts +1 -1
- package/dist/features/image-studio/node-store.js +1 -1
- package/dist/features/image-studio/node-store.js.map +1 -1
- package/dist/features/image-studio/react/ImageStudioDialog.d.ts +1 -1
- package/dist/features/image-studio/react/ImageStudioDialog.js +1 -1
- package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/react/ImageStudioView.js +1 -1
- package/dist/features/image-studio/react/ImageStudioView.js.map +1 -1
- package/dist/features/image-studio/react/store.d.ts +3 -3
- package/dist/features/image-studio/react/store.js.map +1 -1
- package/dist/features/image-studio/react.d.ts +1 -1
- package/dist/features/image-studio/react.js +1 -1
- package/dist/features/image-studio/react.js.map +1 -1
- package/dist/features/image-studio/schema.d.ts +1 -1
- package/dist/features/image-studio/schema.js +1 -1
- package/dist/features/image-studio/schema.js.map +1 -1
- package/dist/features/image-studio/state.d.ts +3 -3
- package/dist/features/image-studio/state.js +2 -2
- package/dist/features/image-studio/state.js.map +1 -1
- package/dist/features/image-studio/store.d.ts +3 -3
- package/dist/features/image-studio/vue.d.ts +1 -1
- package/dist/features/image-studio/vue.js +1 -1
- package/dist/features/image-studio/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/mentions/tag.js +2 -1
- package/dist/features/mentions/tag.js.map +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/feature.d.ts +1 -1
- package/dist/features/themes/feature.js.map +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.d.ts +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/theme-runtime.js +1 -1
- package/dist/features/themes/theme-runtime.js.map +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/segment-node.js.map +1 -1
- package/dist/features/transfer/feature.js +1 -1
- package/dist/features/transfer/feature.js.map +1 -1
- package/dist/features/transfer/transforms.d.ts +4 -4
- package/dist/features/transfer/transforms.js +12 -12
- package/dist/features/transfer/transforms.js.map +1 -1
- package/dist/i18n/de/ai.js +4 -4
- package/dist/i18n/de/ai.js.map +1 -1
- package/dist/i18n/de/authors.js +1 -1
- package/dist/i18n/de/authors.js.map +1 -1
- package/dist/i18n/de/comments.js +1 -1
- package/dist/i18n/de/comments.js.map +1 -1
- package/dist/i18n/de/composites.js +62 -59
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/image-studio.js +2 -2
- package/dist/i18n/de/image-studio.js.map +1 -1
- package/dist/i18n/de/integrations.js +20 -20
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/markdown.js +1 -1
- package/dist/i18n/de/markdown.js.map +1 -1
- package/dist/i18n/de/palette.js +1 -1
- package/dist/i18n/de/palette.js.map +1 -1
- package/dist/i18n/de/themes.js +3 -3
- package/dist/i18n/de/themes.js.map +1 -1
- package/dist/i18n/de/transfer.js +2 -2
- package/dist/i18n/de/transfer.js.map +1 -1
- package/dist/i18n/de/tree.js +6 -6
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +101 -98
- package/dist/i18n/en/ai.js +2 -2
- package/dist/i18n/en/ai.js.map +1 -1
- package/dist/i18n/en/composites.js +64 -61
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/integrations.js +9 -9
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/markdown.js +1 -1
- package/dist/i18n/en/markdown.js.map +1 -1
- package/dist/i18n/en/palette.js +1 -1
- package/dist/i18n/en/palette.js.map +1 -1
- package/dist/i18n/en/themes.js +4 -4
- package/dist/i18n/en/themes.js.map +1 -1
- package/dist/i18n/en/tree.js +5 -5
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/integrations/composite-ai/bibliography-fetch.d.ts +6 -6
- package/dist/integrations/composite-ai/bibliography-fetch.js +19 -19
- package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -1
- package/dist/integrations/composite-ai/composite-fill.d.ts +4 -4
- package/dist/integrations/composite-ai/composite-fill.js +5 -5
- package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
- package/dist/integrations/composite-ai/composite-summary.d.ts +2 -2
- package/dist/integrations/composite-ai/composite-summary.js +5 -5
- package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +17 -17
- package/dist/integrations/composite-ai/derive.js +27 -26
- package/dist/integrations/composite-ai/derive.js.map +1 -1
- package/dist/integrations/composite-ai/index.d.ts +14 -15
- package/dist/integrations/composite-ai/index.js +98 -77
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.d.ts +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.js +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -1
- package/dist/integrations/composite-ai/rendition.d.ts +9 -9
- package/dist/integrations/composite-ai/rendition.js +27 -27
- package/dist/integrations/composite-ai/rendition.js.map +1 -1
- package/dist/integrations/composite-ai/split.d.ts +8 -8
- package/dist/integrations/composite-ai/split.js +12 -12
- package/dist/integrations/composite-ai/split.js.map +1 -1
- package/dist/integrations/composite-ai/transfer-transforms.d.ts +2 -2
- package/dist/integrations/composite-ai/transfer-transforms.js +12 -12
- package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -1
- package/dist/integrations/composite-media/index.d.ts +4 -3
- package/dist/integrations/composite-media/index.js +20 -14
- package/dist/integrations/composite-media/index.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
- package/dist/markdown/document.js +1 -1
- package/dist/markdown/document.js.map +1 -1
- package/dist/nuxt/module.d.ts +30 -24
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/server/plugins/editor-server.js +3 -2
- package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
- package/dist/presets/compose.d.ts +6 -6
- package/dist/presets/compose.js +8 -5
- package/dist/presets/compose.js.map +1 -1
- package/dist/react/AdvancedEditor.d.ts +2 -2
- package/dist/react/AdvancedEditor.js +1 -1
- package/dist/react/AdvancedEditor.js.map +1 -1
- package/dist/react/compose.d.ts +1 -1
- package/dist/react/compose.js +2 -2
- package/dist/react/compose.js.map +1 -1
- package/dist/react/composed-surface.d.ts +5 -5
- package/dist/react/composed-surface.js +2 -2
- package/dist/react/composed-surface.js.map +1 -1
- package/dist/server/ai.d.ts +7 -7
- package/dist/server/ai.js +1 -1
- package/dist/server/ai.js.map +1 -1
- package/dist/server/auth.d.ts +1 -1
- package/dist/server/collab-state.d.ts +1 -1
- package/dist/server/collab-state.js.map +1 -1
- package/dist/server/collab.d.ts +4 -4
- package/dist/server/collab.js +22 -17
- package/dist/server/collab.js.map +1 -1
- package/dist/server/handlers/ai.d.ts +9 -6
- package/dist/server/handlers/ai.js +13 -17
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +4 -0
- package/dist/server/handlers/context.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +33 -32
- package/dist/server/handlers/editor.js +13 -8
- package/dist/server/handlers/editor.js.map +1 -1
- package/dist/server/handlers/images.d.ts +1 -1
- package/dist/server/handlers/images.js +1 -1
- package/dist/server/handlers/images.js.map +1 -1
- package/dist/server/host.d.ts +4 -0
- package/dist/server/host.js +10 -2
- package/dist/server/host.js.map +1 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/ports.d.ts +8 -8
- package/dist/server/ports.js.map +1 -1
- package/dist/settings/index.d.ts +20 -19
- package/dist/settings/index.js +25 -24
- package/dist/settings/index.js.map +1 -1
- package/dist/settings/migrate.d.ts +18 -0
- package/dist/settings/migrate.js +46 -0
- package/dist/settings/migrate.js.map +1 -0
- package/dist/settings/values.d.ts +12 -12
- package/dist/settings/values.js +11 -11
- package/dist/settings/values.js.map +1 -1
- package/dist/styles/composites.css +10 -10
- package/dist/tree/DocumentTree.js +12 -5
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/drop-rules.js +5 -5
- package/dist/tree/drop-rules.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +14 -5
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +6 -6
- package/dist/tree/tree-host.js.map +1 -1
- package/dist/tree/tree-items.js +34 -28
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +16 -16
- package/dist/tree/tree-model.js.map +1 -1
- package/dist/tree/tree-rows.js +2 -2
- package/dist/tree/tree-rows.js.map +1 -1
- package/dist/tree/tree-view-model.d.ts +2 -2
- package/dist/tree/tree-view-model.js.map +1 -1
- package/dist/vue/AdvancedEditor.js +2 -2
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +10 -10
- package/dist/vue/SimpleEditor.vue.d.ts +4 -4
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/dist/vue/compose.d.ts +1 -1
- package/dist/vue/compose.js +2 -2
- package/dist/vue/compose.js.map +1 -1
- package/package.json +1 -1
- package/dist/features/composites/derivation.d.ts +0 -62
- package/dist/features/composites/derivation.js +0 -102
- package/dist/features/composites/derivation.js.map +0 -1
- package/dist/features/composites/kinds.d.ts +0 -83
- package/dist/features/composites/kinds.js +0 -285
- package/dist/features/composites/kinds.js.map +0 -1
package/dist/config/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Locale, Messages } from "../i18n/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ModelRole } from "../settings/values.js";
|
|
3
3
|
import "../settings/index.js";
|
|
4
4
|
//#region src/config/index.d.ts
|
|
5
|
-
/** Server-defined
|
|
6
|
-
export interface
|
|
5
|
+
/** Server-defined Model preset (any OpenAI-compatible chat completions endpoint); its key falls back to the shared `apiKey`. The `preset` field of a model reference names one by id. */
|
|
6
|
+
export interface ModelPresetConfig {
|
|
7
7
|
label: string;
|
|
8
8
|
endpoint: string;
|
|
9
9
|
model: string;
|
|
@@ -34,18 +34,18 @@ export interface EditorConfig {
|
|
|
34
34
|
* the preset id in upper snake case, e.g. `flashLite` → `FLASH_LITE`, or `NUXT_TIPTAP_EDITOR_AI_API_KEY`). Preset ids
|
|
35
35
|
* must be camelCase so the env names map onto them. Presets named like a default (`sonnet`, `flashLite`) override its fields.
|
|
36
36
|
*/
|
|
37
|
-
presets: Record<string,
|
|
37
|
+
presets: Record<string, ModelPresetConfig>;
|
|
38
38
|
/** Shared key of presets without their own. Default `''` (so the env variable has a key to override). */
|
|
39
39
|
apiKey: string;
|
|
40
|
-
/** Default preset per
|
|
41
|
-
|
|
40
|
+
/** Default preset per model role; users may override in settings (`ai.roles`). The `complete` model must support assistant prefill. */
|
|
41
|
+
roles: Record<ModelRole, string>;
|
|
42
42
|
/** Per-user limit shared by all AI endpoints: at most `max` requests per sliding `windowMs`. */
|
|
43
43
|
rateLimit: {
|
|
44
44
|
max: number;
|
|
45
45
|
windowMs: number;
|
|
46
46
|
};
|
|
47
47
|
/**
|
|
48
|
-
* Speech-to-text for audio/video
|
|
48
|
+
* Speech-to-text for audio/video Typed blocks: an OpenAI-compatible `<endpoint>/audio/transcriptions` or Deepgram
|
|
49
49
|
* (`provider: 'deepgram'`; OpenRouter presets provide neither). Absent = the action and the endpoint are off.
|
|
50
50
|
* The key comes from env only (Nuxt: `NUXT_TIPTAP_EDITOR_AI_TRANSCRIPTION_API_KEY`).
|
|
51
51
|
*/
|
|
@@ -53,7 +53,7 @@ export interface EditorConfig {
|
|
|
53
53
|
};
|
|
54
54
|
collab: {
|
|
55
55
|
/**
|
|
56
|
-
* Authenticate the `/collab/:id` websocket upgrade itself (`
|
|
56
|
+
* Authenticate the `/collab/:id` websocket upgrade itself (`AccessControl.authenticate` with the upgrade request's
|
|
57
57
|
* headers) and refuse it with HTTP 401 for an unknown user. Default `false`: the upgrade always succeeds, the user
|
|
58
58
|
* is authenticated afterwards from the headers or the provider's `token`, and an unauthenticated socket is closed
|
|
59
59
|
* with code 4401. Enable only when every client authenticates by cookie/headers: a client that sends its identity
|
|
@@ -67,18 +67,18 @@ type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]>; }
|
|
|
67
67
|
/** What a host writes: every key optional, `undefined` (an unset env variable) means the default. */
|
|
68
68
|
export type EditorConfigInput = DeepPartial<EditorConfig>;
|
|
69
69
|
/**
|
|
70
|
-
* Applies every default once: locale `'de'`, the OpenRouter presets `sonnet` and `flashLite` with the
|
|
70
|
+
* Applies every default once: locale `'de'`, the OpenRouter presets `sonnet` and `flashLite` with the model roles that use
|
|
71
71
|
* them, 30 AI requests per user and minute, no upgrade authentication. Every preset and the transcription get an empty
|
|
72
72
|
* `apiKey` unless one is given, so an environment variable has a key to override.
|
|
73
73
|
*/
|
|
74
74
|
export declare function defineEditorConfig(input?: EditorConfigInput): EditorConfig;
|
|
75
|
-
/** What the browser may see: the language and the default model
|
|
75
|
+
/** What the browser may see: the language and the default model roles, never a key. */
|
|
76
76
|
export interface EditorPublicConfig {
|
|
77
77
|
locale: Locale;
|
|
78
78
|
messages?: Partial<Messages>;
|
|
79
|
-
/** Inert AI configuration: default preset per
|
|
79
|
+
/** Inert AI configuration: default preset per model role before the models list has answered, and whether transcription is on. */
|
|
80
80
|
ai: {
|
|
81
|
-
|
|
81
|
+
roles: Record<ModelRole, string>;
|
|
82
82
|
transcription: boolean;
|
|
83
83
|
};
|
|
84
84
|
}
|
|
@@ -86,9 +86,9 @@ export declare function publicConfig(config: EditorConfig): EditorPublicConfig;
|
|
|
86
86
|
/** What the server needs, secrets included: the shape `installEditorServer` takes for `ai`, `rateLimit` and `collab`. */
|
|
87
87
|
export interface EditorServerConfig {
|
|
88
88
|
ai: {
|
|
89
|
-
presets: Record<string,
|
|
89
|
+
presets: Record<string, ModelPresetConfig>;
|
|
90
90
|
apiKey: string;
|
|
91
|
-
|
|
91
|
+
roles: Record<ModelRole, string>;
|
|
92
92
|
transcription?: TranscriptionConfig;
|
|
93
93
|
};
|
|
94
94
|
rateLimit: {
|
package/dist/config/index.js
CHANGED
|
@@ -12,7 +12,7 @@ const DEFAULT_PRESETS = {
|
|
|
12
12
|
model: "google/gemini-2.5-flash-lite"
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
-
const
|
|
15
|
+
const DEFAULT_ROLES = {
|
|
16
16
|
complete: "flashLite",
|
|
17
17
|
rewrite: "sonnet",
|
|
18
18
|
generate: "sonnet"
|
|
@@ -26,7 +26,7 @@ function defined(value) {
|
|
|
26
26
|
return Object.fromEntries(Object.entries(value ?? {}).filter(([, v]) => v !== void 0));
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* Applies every default once: locale `'de'`, the OpenRouter presets `sonnet` and `flashLite` with the
|
|
29
|
+
* Applies every default once: locale `'de'`, the OpenRouter presets `sonnet` and `flashLite` with the model roles that use
|
|
30
30
|
* them, 30 AI requests per user and minute, no upgrade authentication. Every preset and the transcription get an empty
|
|
31
31
|
* `apiKey` unless one is given, so an environment variable has a key to override.
|
|
32
32
|
*/
|
|
@@ -50,9 +50,9 @@ function defineEditorConfig(input = {}) {
|
|
|
50
50
|
ai: {
|
|
51
51
|
presets,
|
|
52
52
|
apiKey: ai.apiKey ?? "",
|
|
53
|
-
|
|
54
|
-
...
|
|
55
|
-
...defined(ai.
|
|
53
|
+
roles: {
|
|
54
|
+
...DEFAULT_ROLES,
|
|
55
|
+
...defined(ai.roles)
|
|
56
56
|
},
|
|
57
57
|
rateLimit: {
|
|
58
58
|
...DEFAULT_RATE_LIMIT,
|
|
@@ -71,18 +71,18 @@ function publicConfig(config) {
|
|
|
71
71
|
locale: config.locale,
|
|
72
72
|
...config.messages && { messages: config.messages },
|
|
73
73
|
ai: {
|
|
74
|
-
|
|
74
|
+
roles: config.ai.roles,
|
|
75
75
|
transcription: Boolean(config.ai.transcription)
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
function serverConfig(config) {
|
|
80
|
-
const { presets, apiKey,
|
|
80
|
+
const { presets, apiKey, roles, transcription, rateLimit } = config.ai;
|
|
81
81
|
return {
|
|
82
82
|
ai: {
|
|
83
83
|
presets,
|
|
84
84
|
apiKey,
|
|
85
|
-
|
|
85
|
+
roles,
|
|
86
86
|
...transcription && { transcription }
|
|
87
87
|
},
|
|
88
88
|
rateLimit,
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/config/index.ts"],"sourcesContent":["import { DEFAULT_LOCALE, type Locale, type Messages } from '../i18n'\nimport type {
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/config/index.ts"],"sourcesContent":["import { DEFAULT_LOCALE, type Locale, type Messages } from '../i18n'\nimport type { ModelRole } from '../settings'\n\n// One configuration of the editor, three views: `defineEditorConfig` applies every default once; `serverConfig` is\n// what the server needs (including the secrets), `publicConfig` what the browser may see. The Nuxt module sets its\n// runtime config from the two views; a plain Nitro host spreads `serverConfig(...)` into `installEditorServer`.\n// Framework-free: imports only the i18n and settings types, so Node, Nuxt build code and the browser can all load it.\n\n/** Server-defined Model preset (any OpenAI-compatible chat completions endpoint); its key falls back to the shared `apiKey`. The `preset` field of a model reference names one by id. */\nexport interface ModelPresetConfig { label: string, endpoint: string, model: string, apiKey?: string }\n\n/**\n * Speech-to-text endpoint. `provider` `'openai'` (default): any OpenAI-compatible `<endpoint>/audio/transcriptions`\n * (e.g. OpenAI `whisper-1`). `'deepgram'`: Deepgram's pre-recorded `<endpoint>/listen` (endpoint\n * `https://api.deepgram.com/v1`, model e.g. `nova-3`). `language` (e.g. `'de'`) fixes the spoken language; without it\n * OpenAI guesses and Deepgram detects it.\n */\nexport interface TranscriptionConfig { provider?: 'openai' | 'deepgram', endpoint: string, model: string, apiKey?: string, language?: string }\n\nexport interface EditorConfig {\n /** Language of the editor's strings: `'de'` (default) or `'en'`. Set once per app; a host that switches languages at run time recreates the editor context. */\n locale: Locale\n /** Overrides of single messages by key (see `MessageKey`), applied on top of the catalog of `locale`; must be JSON-serialisable. */\n messages?: Partial<Messages>\n ai: {\n /**\n * Server-defined model presets (any OpenAI-compatible chat completions endpoint), keyed by preset id. The key is\n * per preset or the shared `apiKey`, both from env only (Nuxt: `NUXT_TIPTAP_EDITOR_AI_PRESETS_<ID>_API_KEY`, `<ID>` =\n * the preset id in upper snake case, e.g. `flashLite` → `FLASH_LITE`, or `NUXT_TIPTAP_EDITOR_AI_API_KEY`). Preset ids\n * must be camelCase so the env names map onto them. Presets named like a default (`sonnet`, `flashLite`) override its fields.\n */\n presets: Record<string, ModelPresetConfig>\n /** Shared key of presets without their own. Default `''` (so the env variable has a key to override). */\n apiKey: string\n /** Default preset per model role; users may override in settings (`ai.roles`). The `complete` model must support assistant prefill. */\n roles: Record<ModelRole, string>\n /** Per-user limit shared by all AI endpoints: at most `max` requests per sliding `windowMs`. */\n rateLimit: { max: number, windowMs: number }\n /**\n * Speech-to-text for audio/video Typed blocks: an OpenAI-compatible `<endpoint>/audio/transcriptions` or Deepgram\n * (`provider: 'deepgram'`; OpenRouter presets provide neither). Absent = the action and the endpoint are off.\n * The key comes from env only (Nuxt: `NUXT_TIPTAP_EDITOR_AI_TRANSCRIPTION_API_KEY`).\n */\n transcription?: TranscriptionConfig\n }\n collab: {\n /**\n * Authenticate the `/collab/:id` websocket upgrade itself (`AccessControl.authenticate` with the upgrade request's\n * headers) and refuse it with HTTP 401 for an unknown user. Default `false`: the upgrade always succeeds, the user\n * is authenticated afterwards from the headers or the provider's `token`, and an unauthenticated socket is closed\n * with code 4401. Enable only when every client authenticates by cookie/headers: a client that sends its identity\n * as `collaboration.token` (no cookies) cannot connect then. Nuxt env override:\n * `NUXT_TIPTAP_EDITOR_COLLAB_AUTHENTICATE_UPGRADE=true`.\n */\n authenticateUpgrade: boolean\n }\n}\n\ntype DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T\n\n/** What a host writes: every key optional, `undefined` (an unset env variable) means the default. */\nexport type EditorConfigInput = DeepPartial<EditorConfig>\n\n// i18n-ignore-start: model product names, the same in every language\nconst DEFAULT_PRESETS: Record<string, ModelPresetConfig> = {\n sonnet: { label: 'Claude Sonnet 4.5 (OpenRouter)', endpoint: 'https://openrouter.ai/api/v1', model: 'anthropic/claude-sonnet-4.5' },\n flashLite: { label: 'Gemini 2.5 Flash Lite (OpenRouter)', endpoint: 'https://openrouter.ai/api/v1', model: 'google/gemini-2.5-flash-lite' },\n}\n// i18n-ignore-end\n\nconst DEFAULT_ROLES: Record<ModelRole, string> = { complete: 'flashLite', rewrite: 'sonnet', generate: 'sonnet' }\n\nconst DEFAULT_RATE_LIMIT = { max: 30, windowMs: 60_000 }\n\n/** Drops `undefined` values so an unset input never overwrites a default. */\nfunction defined<T extends object>(value: T | undefined): Partial<T> {\n return Object.fromEntries(Object.entries(value ?? {}).filter(([, v]) => v !== undefined)) as Partial<T>\n}\n\n/**\n * Applies every default once: locale `'de'`, the OpenRouter presets `sonnet` and `flashLite` with the model roles that use\n * them, 30 AI requests per user and minute, no upgrade authentication. Every preset and the transcription get an empty\n * `apiKey` unless one is given, so an environment variable has a key to override.\n */\nexport function defineEditorConfig(input: EditorConfigInput = {}): EditorConfig {\n const ai = input.ai ?? {}\n const presets: Record<string, ModelPresetConfig> = {}\n for (const id of new Set([...Object.keys(DEFAULT_PRESETS), ...Object.keys(ai.presets ?? {})])) {\n const preset = { ...DEFAULT_PRESETS[id], ...defined(ai.presets?.[id]) } as ModelPresetConfig\n presets[id] = { ...preset, apiKey: preset.apiKey ?? '' }\n }\n const transcription = ai.transcription && ({ ...defined(ai.transcription) } as TranscriptionConfig)\n return {\n locale: input.locale ?? DEFAULT_LOCALE,\n ...(input.messages && { messages: input.messages as Partial<Messages> }),\n ai: {\n presets,\n apiKey: ai.apiKey ?? '',\n roles: { ...DEFAULT_ROLES, ...defined(ai.roles) },\n rateLimit: { ...DEFAULT_RATE_LIMIT, ...defined(ai.rateLimit) },\n ...(transcription && { transcription: { ...transcription, apiKey: transcription.apiKey ?? '' } }),\n },\n collab: { authenticateUpgrade: input.collab?.authenticateUpgrade ?? false },\n }\n}\n\n/** What the browser may see: the language and the default model roles, never a key. */\nexport interface EditorPublicConfig {\n locale: Locale\n messages?: Partial<Messages>\n /** Inert AI configuration: default preset per model role before the models list has answered, and whether transcription is on. */\n ai: { roles: Record<ModelRole, string>, transcription: boolean }\n}\n\nexport function publicConfig(config: EditorConfig): EditorPublicConfig {\n return {\n locale: config.locale,\n ...(config.messages && { messages: config.messages }),\n ai: { roles: config.ai.roles, transcription: Boolean(config.ai.transcription) },\n }\n}\n\n/** What the server needs, secrets included: the shape `installEditorServer` takes for `ai`, `rateLimit` and `collab`. */\nexport interface EditorServerConfig {\n ai: { presets: Record<string, ModelPresetConfig>, apiKey: string, roles: Record<ModelRole, string>, transcription?: TranscriptionConfig }\n rateLimit: { max: number, windowMs: number }\n collab: { authenticateUpgrade: boolean }\n}\n\nexport function serverConfig(config: EditorConfig): EditorServerConfig {\n const { presets, apiKey, roles, transcription, rateLimit } = config.ai\n return {\n ai: { presets, apiKey, roles, ...(transcription && { transcription }) },\n rateLimit,\n collab: config.collab,\n }\n}\n"],"mappings":";;AAgEA,MAAM,kBAAqD;CACzD,QAAQ;EAAE,OAAO;EAAkC,UAAU;EAAgC,OAAO;CAA8B;CAClI,WAAW;EAAE,OAAO;EAAsC,UAAU;EAAgC,OAAO;CAA+B;AAC5I;AAGA,MAAM,gBAA2C;CAAE,UAAU;CAAa,SAAS;CAAU,UAAU;AAAS;AAEhH,MAAM,qBAAqB;CAAE,KAAK;CAAI,UAAU;AAAO;;AAGvD,SAAS,QAA0B,OAAkC;CACnE,OAAO,OAAO,YAAY,OAAO,QAAQ,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,OAAO,MAAM,KAAA,CAAS,CAAC;AAC1F;;;;;;AAOA,SAAgB,mBAAmB,QAA2B,CAAC,GAAiB;CAC9E,MAAM,KAAK,MAAM,MAAM,CAAC;CACxB,MAAM,UAA6C,CAAC;CACpD,KAAK,MAAM,sBAAM,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,eAAe,GAAG,GAAG,OAAO,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG;EAC7F,MAAM,SAAS;GAAE,GAAG,gBAAgB;GAAK,GAAG,QAAQ,GAAG,UAAU,GAAG;EAAE;EACtE,QAAQ,MAAM;GAAE,GAAG;GAAQ,QAAQ,OAAO,UAAU;EAAG;CACzD;CACA,MAAM,gBAAgB,GAAG,iBAAkB,EAAE,GAAG,QAAQ,GAAG,aAAa,EAAE;CAC1E,OAAO;EACL,QAAQ,MAAM,UAAA;EACd,GAAI,MAAM,YAAY,EAAE,UAAU,MAAM,SAA8B;EACtE,IAAI;GACF;GACA,QAAQ,GAAG,UAAU;GACrB,OAAO;IAAE,GAAG;IAAe,GAAG,QAAQ,GAAG,KAAK;GAAE;GAChD,WAAW;IAAE,GAAG;IAAoB,GAAG,QAAQ,GAAG,SAAS;GAAE;GAC7D,GAAI,iBAAiB,EAAE,eAAe;IAAE,GAAG;IAAe,QAAQ,cAAc,UAAU;GAAG,EAAE;EACjG;EACA,QAAQ,EAAE,qBAAqB,MAAM,QAAQ,uBAAuB,MAAM;CAC5E;AACF;AAUA,SAAgB,aAAa,QAA0C;CACrE,OAAO;EACL,QAAQ,OAAO;EACf,GAAI,OAAO,YAAY,EAAE,UAAU,OAAO,SAAS;EACnD,IAAI;GAAE,OAAO,OAAO,GAAG;GAAO,eAAe,QAAQ,OAAO,GAAG,aAAa;EAAE;CAChF;AACF;AASA,SAAgB,aAAa,QAA0C;CACrE,MAAM,EAAE,SAAS,QAAQ,OAAO,eAAe,cAAc,OAAO;CACpE,OAAO;EACL,IAAI;GAAE;GAAS;GAAQ;GAAO,GAAI,iBAAiB,EAAE,cAAc;EAAG;EACtE;EACA,QAAQ,OAAO;CACjB;AACF"}
|
|
@@ -67,7 +67,7 @@ export interface CslDate {
|
|
|
67
67
|
/** The CSL item types the editor offers (select options, server-side schema). */
|
|
68
68
|
export declare const CSL_TYPES: readonly ["article-journal", "book", "chapter", "paper-conference", "thesis", "report", "webpage", "article", "dataset"];
|
|
69
69
|
export type CslType = typeof CSL_TYPES[number];
|
|
70
|
-
/** A CSL-JSON item without `id` and `title`: the
|
|
70
|
+
/** A CSL-JSON item without `id` and `title`: the Typed block's block id and title are authoritative for those and never stored in `meta.bib`. */
|
|
71
71
|
export interface CslItem {
|
|
72
72
|
type: CslType;
|
|
73
73
|
author?: CslName[];
|
|
@@ -249,7 +249,7 @@ export interface ImageStudioImage {
|
|
|
249
249
|
title: string;
|
|
250
250
|
active: boolean;
|
|
251
251
|
}
|
|
252
|
-
/** A managed
|
|
252
|
+
/** A managed prompt block (style, brand, …) of the host. */
|
|
253
253
|
export interface ImageStudioPromptBlock {
|
|
254
254
|
id: string;
|
|
255
255
|
label: string;
|
|
@@ -261,7 +261,7 @@ export interface ImageStudioGenerateBody {
|
|
|
261
261
|
description: string;
|
|
262
262
|
/** This turn's instruction; `''` generates from the description alone. */
|
|
263
263
|
instruction: string;
|
|
264
|
-
/** The active
|
|
264
|
+
/** The active prompt blocks, by the host's ids. */
|
|
265
265
|
promptBlockIds: string[];
|
|
266
266
|
/** The URLs of the active references and generations that go into the generation as reference images. */
|
|
267
267
|
referenceUrls: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend-types.js","names":[],"sources":["../../src/core/backend-types.ts"],"sourcesContent":["import type { ReadonlyStore } from './store'\nimport type { AuthorIdentity } from './feature'\n\n// The wire and model types that `EditorBackend` (`./backend`) names in its methods. They live in core, not in the\n// features they belong to, so the port does not depend on any feature or integration; each feature's public entry\n// (`@ecsplico/editor/mentions/schema`, `…/themes/schema`, `…/ai/protocol`, `…/comments/schema`, …) exports the names\n// it always had. Types only: DOM-free, importable from server code.\n\n/** A user as offered for `@` mentions (`GET /api/editor/users`); never carries credentials. */\nexport interface MentionUser {\n id: string\n username: string\n color: string\n}\n\n/** Profile as the client sees it; presets never expose their key. */\nexport interface ModelProfile { id: string, label: string, endpoint: string, model: string, source: 'preset' | 'user' }\n\n/** Model an AI request runs on: a server preset by id, or an OpenAI-compatible endpoint with its own key. */\nexport type ModelRef = { preset: string } | { endpoint: string, model: string, apiKey: string }\n\n/** A theme in the shared library; `builtin` themes are seeded by the host and cannot be deleted. */\nexport interface ThemeRecord {\n id: string\n name: string\n version?: string\n author?: string\n authorUrl?: string\n builtin: boolean\n}\n\n/** One transcript segment sent for cleanup. */\nexport interface TranscriptCleanupSegment {\n /** Block ID of the segment (`blockIdOf`). */\n id: string\n speaker: string\n /** The segment's inline content as Markdown (mentions as `@name`, tags as `#name`). */\n text: string\n}\n/** Body of the transcript-cleanup route. */\nexport interface TranscriptCleanupRequest { segments: TranscriptCleanupSegment[], model: ModelRef }\n/** Cleaned text (single-line inline Markdown) of the segment with block ID `id`. */\nexport interface TranscriptCleanupResult { id: string, text: string }\n/** `cleaned`: only segments whose text changed. `failed`: segments of chunks whose model answer was unusable (left as they are). */\nexport interface TranscriptCleanupResponse { cleaned: TranscriptCleanupResult[], failed: number }\n\n/** A CSL name: `family`/`given` for a person, `literal` for an institution or an unparsed name. */\nexport interface CslName { family?: string, given?: string, literal?: string }\nexport interface CslDate { 'date-parts': number[][] }\n\n/** The CSL item types the editor offers (select options, server-side schema). */\nexport const CSL_TYPES = ['article-journal', 'book', 'chapter', 'paper-conference', 'thesis', 'report', 'webpage', 'article', 'dataset'] as const\nexport type CslType = typeof CSL_TYPES[number]\n\n/** A CSL-JSON item without `id` and `title`: the Baustein's block id and title are authoritative for those and never stored in `meta.bib`. */\nexport interface CslItem {\n type: CslType\n author?: CslName[]\n editor?: CslName[]\n issued?: CslDate\n 'container-title'?: string\n volume?: string\n issue?: string\n page?: string\n publisher?: string\n 'publisher-place'?: string\n DOI?: string\n ISBN?: string\n URL?: string\n abstract?: string\n language?: string\n}\n\n/** What `ai.extract` makes of a stored asset; `auto` picks by the asset's MIME type. */\nexport type ExtractMethod = 'auto' | 'transcription' | 'pdf-text' | 'ocr' | 'description' | 'bibliography'\n/** Body of the extract route: the stored asset `documentId`/`name` and what to make of it. */\nexport interface ExtractRequest {\n documentId: string\n /** The asset to read; absent only for `bibliography` with a `doi` (the work is looked up by DOI alone). */\n name?: string\n method: ExtractMethod\n /** The model of `ocr`, `description` and model-based bibliography extraction. */\n model: ModelRef\n /** Also extract bibliographic data after `pdf-text`/`ocr`. */\n bibliography?: boolean\n /** DOI to look up for `bibliography`; absent = found in the first pages' text. */\n doi?: string\n /** PDF page cap (default 200). `ocr` reads at most 50 pages whatever this allows (one model call per page; `ocr-truncated` warns). */\n maxPages?: number\n}\nexport interface ExtractResponse {\n method: Exclude<ExtractMethod, 'auto'>\n /** The readable content as Markdown (empty for `bibliography`). */\n markdown: string\n /** Transcription only: the timed segments `markdown` was made of. */\n segments?: { start: number, end: number, text: string }[]\n /** When bibliographic data was requested or the method is `bibliography`; `title` is the work's title when found (never stored in `meta.bib`). */\n bib?: CslItem & { title?: string }\n /** Transcription only: the speech-to-text model that wrote it (`TranscriptionConfig.model`), which the client cannot know. */\n model?: string\n /** SHA-256 (hex) of the asset bytes. */\n inputHash: string\n /** SHA-256 (hex) of the stored raw output (`markdown` as the asset `output` holds it). The client does not compare it: it hashes the body it writes (its re-serialised Markdown) into `Derivation.outputHash` itself. */\n outputHash: string\n /** Asset name (same document) of the stored raw Markdown; `null` for `bibliography`. */\n output: string | null\n warnings: string[]\n}\n\n/** Who wrote a comment or reply: the writing user's identity, copied in when writing. */\nexport type CommentAuthor = AuthorIdentity\n\nexport interface CommentReply {\n id: string\n author: CommentAuthor\n text: string\n /** ISO timestamps; `editedAt` once the author changed the text. */\n createdAt: string\n editedAt?: string\n}\n\n/**\n * Where a thread's text starts, as the JSON form of a Yjs relative position (`Y.relativePositionToJSON`): bound to the\n * first commented character, so it keeps pointing at the place after that text was deleted (and moves with the text\n * around it). Stored in the thread's `Y.Map` under `anchor`, so every collaborator resolves the same place.\n */\nexport interface CommentAnchor {\n type?: { client: number, clock: number }\n tname?: string\n item?: { client: number, clock: number }\n assoc?: number\n}\n\n/**\n * A comment thread: the root comment plus its replies. Stored as a nested `Y.Map` in `ydoc.getMap(COMMENTS_FIELD)`\n * (replies as a nested `Y.Array`), so concurrent replies and edits of different users merge. Its text range is the\n * `comment` mark with the thread's `id`.\n */\nexport interface CommentThread {\n id: string\n author: CommentAuthor\n text: string\n createdAt: string\n editedAt?: string\n /** The commented text when the thread was created, cut to `COMMENT_LIMITS.quote`. */\n quote: string\n /** Resolved threads are kept, without highlight, until reopened. */\n resolved: boolean\n /**\n * Start of the commented text as a Yjs relative position, written when the thread is created (older threads get it\n * once they are seen with their text still in the document). Tells where an orphaned thread's text was. Absent on threads\n * not yet seen with a range, and when the position could not be captured.\n */\n anchor?: CommentAnchor\n replies: CommentReply[]\n}\n\n/** A text range in the editor document. */\nexport type DocRange = { from: number, to: number }\n\n/** What a comments UI renders: replaced (a new object) whenever any part of it changes, so a store subscriber re-renders only then. */\nexport interface CommentsSnapshot {\n /** Comments need a collaborative editor whose `Comments` extension has a user (a host model decides for itself). */\n available: boolean\n /**\n * Whether the user may compose a comment now: the default model says `available` and the editor is editable, a host\n * model may allow readers of a read-only document to comment.\n */\n canComment: boolean\n user: AuthorIdentity | null\n /** Plain snapshots of all threads in the Y.Doc, replaced on every change. */\n threads: readonly CommentThread[]\n /** Current text range per thread id (threads without one are orphans). */\n ranges: ReadonlyMap<string, DocRange>\n /**\n * Where the text of an orphaned open thread was: the document position of the deletion point, resolved from the\n * thread's stored anchor (so it is the same place for every collaborator and follows edits). Absent for orphans\n * without a usable anchor (threads from before anchors, or whose block is gone for good).\n */\n orphanAt: ReadonlyMap<string, number>\n active: string | null\n /** Current range of the draft a new comment is being written for. */\n draft: DocRange | null\n /** The draft's text as the new thread will keep it (`quote`). */\n draftQuote: string\n /** Unresolved threads in document order, orphans last; resolved ones newest first. */\n open: readonly CommentThread[]\n resolved: readonly CommentThread[]\n}\n\n/** The mutations of the comments of one editor; no-ops without collaboration or a user. */\nexport interface CommentsActions {\n /** Creates a thread on the draft range; its id, or null without a (non-empty) draft or text. */\n add: (text: string) => string | null\n reply: (threadId: string, text: string) => void\n /** Changes the text of the user's own thread (`replyId` null) or reply. */\n edit: (threadId: string, replyId: string | null, text: string) => void\n /** Resolves or reopens a thread (anyone may). */\n setResolved: (threadId: string, resolved: boolean) => void\n /** Deletes the user's own thread with its marks / the user's own reply. */\n remove: (threadId: string) => void\n removeReply: (threadId: string, replyId: string) => void\n /** Activates a thread (`null`: none); `reveal` selects and scrolls to its text. */\n activate: (id: string | null, options?: { reveal?: boolean }) => void\n /** Starts a comment on the current selection (the layout shows the composer while `draft` is set). */\n startDraft: () => void\n cancelDraft: () => void\n}\n\n/**\n * The comments of one editor, framework-free: `state` is the observable snapshot, `refresh()` brings it up to date\n * (the framework hook calls it when the editor it watches changes; the model itself listens to that editor's\n * transactions from then on), `dispose()` detaches the editor and Y.Doc listeners and may be followed by `refresh()` again\n * (StrictMode mounts twice).\n */\nexport interface CommentsModel extends CommentsActions {\n readonly state: ReadonlyStore<CommentsSnapshot>\n refresh: () => void\n dispose: () => void\n}\n\n/** A reference or a previous generation of the Bild-Studio (`images.generate` and `images.upload` answer one). `active` = goes into the next generation. */\nexport interface ImageStudioImage {\n /** Host-opaque. */\n id: string\n url: string\n title: string\n active: boolean\n}\n\n/** A managed Prompt-Baustein (style, brand, …) of the host. */\nexport interface ImageStudioPromptBlock {\n id: string\n label: string\n text: string\n}\n\n/** Body of the image-generation route (`images.generate`): what the Bild-Studio sends for one chat turn. */\nexport interface ImageStudioGenerateBody {\n /** The block's description of the image. */\n description: string\n /** This turn's instruction; `''` generates from the description alone. */\n instruction: string\n /** The active Prompt-Bausteine, by the host's ids. */\n promptBlockIds: string[]\n /** The URLs of the active references and generations that go into the generation as reference images. */\n referenceUrls: string[]\n}\n"],"mappings":";;AAmDA,MAAa,YAAY;CAAC;CAAmB;CAAQ;CAAW;CAAoB;CAAU;CAAU;CAAW;CAAW;AAAS"}
|
|
1
|
+
{"version":3,"file":"backend-types.js","names":[],"sources":["../../src/core/backend-types.ts"],"sourcesContent":["import type { ReadonlyStore } from './store'\nimport type { AuthorIdentity } from './feature'\n\n// The wire and model types that `EditorBackend` (`./backend`) names in its methods. They live in core, not in the\n// features they belong to, so the port does not depend on any feature or integration; each feature's public entry\n// (`@ecsplico/editor/mentions/schema`, `…/themes/schema`, `…/ai/protocol`, `…/comments/schema`, …) exports the names\n// it always had. Types only: DOM-free, importable from server code.\n\n/** A user as offered for `@` mentions (`GET /api/editor/users`); never carries credentials. */\nexport interface MentionUser {\n id: string\n username: string\n color: string\n}\n\n/** Profile as the client sees it; presets never expose their key. */\nexport interface ModelProfile { id: string, label: string, endpoint: string, model: string, source: 'preset' | 'user' }\n\n/** Model an AI request runs on: a server preset by id, or an OpenAI-compatible endpoint with its own key. */\nexport type ModelRef = { preset: string } | { endpoint: string, model: string, apiKey: string }\n\n/** A theme in the shared library; `builtin` themes are seeded by the host and cannot be deleted. */\nexport interface ThemeRecord {\n id: string\n name: string\n version?: string\n author?: string\n authorUrl?: string\n builtin: boolean\n}\n\n/** One transcript segment sent for cleanup. */\nexport interface TranscriptCleanupSegment {\n /** Block ID of the segment (`blockIdOf`). */\n id: string\n speaker: string\n /** The segment's inline content as Markdown (mentions as `@name`, tags as `#name`). */\n text: string\n}\n/** Body of the transcript-cleanup route. */\nexport interface TranscriptCleanupRequest { segments: TranscriptCleanupSegment[], model: ModelRef }\n/** Cleaned text (single-line inline Markdown) of the segment with block ID `id`. */\nexport interface TranscriptCleanupResult { id: string, text: string }\n/** `cleaned`: only segments whose text changed. `failed`: segments of chunks whose model answer was unusable (left as they are). */\nexport interface TranscriptCleanupResponse { cleaned: TranscriptCleanupResult[], failed: number }\n\n/** A CSL name: `family`/`given` for a person, `literal` for an institution or an unparsed name. */\nexport interface CslName { family?: string, given?: string, literal?: string }\nexport interface CslDate { 'date-parts': number[][] }\n\n/** The CSL item types the editor offers (select options, server-side schema). */\nexport const CSL_TYPES = ['article-journal', 'book', 'chapter', 'paper-conference', 'thesis', 'report', 'webpage', 'article', 'dataset'] as const\nexport type CslType = typeof CSL_TYPES[number]\n\n/** A CSL-JSON item without `id` and `title`: the Typed block's block id and title are authoritative for those and never stored in `meta.bib`. */\nexport interface CslItem {\n type: CslType\n author?: CslName[]\n editor?: CslName[]\n issued?: CslDate\n 'container-title'?: string\n volume?: string\n issue?: string\n page?: string\n publisher?: string\n 'publisher-place'?: string\n DOI?: string\n ISBN?: string\n URL?: string\n abstract?: string\n language?: string\n}\n\n/** What `ai.extract` makes of a stored asset; `auto` picks by the asset's MIME type. */\nexport type ExtractMethod = 'auto' | 'transcription' | 'pdf-text' | 'ocr' | 'description' | 'bibliography'\n/** Body of the extract route: the stored asset `documentId`/`name` and what to make of it. */\nexport interface ExtractRequest {\n documentId: string\n /** The asset to read; absent only for `bibliography` with a `doi` (the work is looked up by DOI alone). */\n name?: string\n method: ExtractMethod\n /** The model of `ocr`, `description` and model-based bibliography extraction. */\n model: ModelRef\n /** Also extract bibliographic data after `pdf-text`/`ocr`. */\n bibliography?: boolean\n /** DOI to look up for `bibliography`; absent = found in the first pages' text. */\n doi?: string\n /** PDF page cap (default 200). `ocr` reads at most 50 pages whatever this allows (one model call per page; `ocr-truncated` warns). */\n maxPages?: number\n}\nexport interface ExtractResponse {\n method: Exclude<ExtractMethod, 'auto'>\n /** The readable content as Markdown (empty for `bibliography`). */\n markdown: string\n /** Transcription only: the timed segments `markdown` was made of. */\n segments?: { start: number, end: number, text: string }[]\n /** When bibliographic data was requested or the method is `bibliography`; `title` is the work's title when found (never stored in `meta.bib`). */\n bib?: CslItem & { title?: string }\n /** Transcription only: the speech-to-text model that wrote it (`TranscriptionConfig.model`), which the client cannot know. */\n model?: string\n /** SHA-256 (hex) of the asset bytes. */\n inputHash: string\n /** SHA-256 (hex) of the stored raw output (`markdown` as the asset `output` holds it). The client does not compare it: it hashes the body it writes (its re-serialised Markdown) into `Derivation.outputHash` itself. */\n outputHash: string\n /** Asset name (same document) of the stored raw Markdown; `null` for `bibliography`. */\n output: string | null\n warnings: string[]\n}\n\n/** Who wrote a comment or reply: the writing user's identity, copied in when writing. */\nexport type CommentAuthor = AuthorIdentity\n\nexport interface CommentReply {\n id: string\n author: CommentAuthor\n text: string\n /** ISO timestamps; `editedAt` once the author changed the text. */\n createdAt: string\n editedAt?: string\n}\n\n/**\n * Where a thread's text starts, as the JSON form of a Yjs relative position (`Y.relativePositionToJSON`): bound to the\n * first commented character, so it keeps pointing at the place after that text was deleted (and moves with the text\n * around it). Stored in the thread's `Y.Map` under `anchor`, so every collaborator resolves the same place.\n */\nexport interface CommentAnchor {\n type?: { client: number, clock: number }\n tname?: string\n item?: { client: number, clock: number }\n assoc?: number\n}\n\n/**\n * A comment thread: the root comment plus its replies. Stored as a nested `Y.Map` in `ydoc.getMap(COMMENTS_FIELD)`\n * (replies as a nested `Y.Array`), so concurrent replies and edits of different users merge. Its text range is the\n * `comment` mark with the thread's `id`.\n */\nexport interface CommentThread {\n id: string\n author: CommentAuthor\n text: string\n createdAt: string\n editedAt?: string\n /** The commented text when the thread was created, cut to `COMMENT_LIMITS.quote`. */\n quote: string\n /** Resolved threads are kept, without highlight, until reopened. */\n resolved: boolean\n /**\n * Start of the commented text as a Yjs relative position, written when the thread is created (older threads get it\n * once they are seen with their text still in the document). Tells where an orphaned thread's text was. Absent on threads\n * not yet seen with a range, and when the position could not be captured.\n */\n anchor?: CommentAnchor\n replies: CommentReply[]\n}\n\n/** A text range in the editor document. */\nexport type DocRange = { from: number, to: number }\n\n/** What a comments UI renders: replaced (a new object) whenever any part of it changes, so a store subscriber re-renders only then. */\nexport interface CommentsSnapshot {\n /** Comments need a collaborative editor whose `Comments` extension has a user (a host model decides for itself). */\n available: boolean\n /**\n * Whether the user may compose a comment now: the default model says `available` and the editor is editable, a host\n * model may allow readers of a read-only document to comment.\n */\n canComment: boolean\n user: AuthorIdentity | null\n /** Plain snapshots of all threads in the Y.Doc, replaced on every change. */\n threads: readonly CommentThread[]\n /** Current text range per thread id (threads without one are orphans). */\n ranges: ReadonlyMap<string, DocRange>\n /**\n * Where the text of an orphaned open thread was: the document position of the deletion point, resolved from the\n * thread's stored anchor (so it is the same place for every collaborator and follows edits). Absent for orphans\n * without a usable anchor (threads from before anchors, or whose block is gone for good).\n */\n orphanAt: ReadonlyMap<string, number>\n active: string | null\n /** Current range of the draft a new comment is being written for. */\n draft: DocRange | null\n /** The draft's text as the new thread will keep it (`quote`). */\n draftQuote: string\n /** Unresolved threads in document order, orphans last; resolved ones newest first. */\n open: readonly CommentThread[]\n resolved: readonly CommentThread[]\n}\n\n/** The mutations of the comments of one editor; no-ops without collaboration or a user. */\nexport interface CommentsActions {\n /** Creates a thread on the draft range; its id, or null without a (non-empty) draft or text. */\n add: (text: string) => string | null\n reply: (threadId: string, text: string) => void\n /** Changes the text of the user's own thread (`replyId` null) or reply. */\n edit: (threadId: string, replyId: string | null, text: string) => void\n /** Resolves or reopens a thread (anyone may). */\n setResolved: (threadId: string, resolved: boolean) => void\n /** Deletes the user's own thread with its marks / the user's own reply. */\n remove: (threadId: string) => void\n removeReply: (threadId: string, replyId: string) => void\n /** Activates a thread (`null`: none); `reveal` selects and scrolls to its text. */\n activate: (id: string | null, options?: { reveal?: boolean }) => void\n /** Starts a comment on the current selection (the layout shows the composer while `draft` is set). */\n startDraft: () => void\n cancelDraft: () => void\n}\n\n/**\n * The comments of one editor, framework-free: `state` is the observable snapshot, `refresh()` brings it up to date\n * (the framework hook calls it when the editor it watches changes; the model itself listens to that editor's\n * transactions from then on), `dispose()` detaches the editor and Y.Doc listeners and may be followed by `refresh()` again\n * (StrictMode mounts twice).\n */\nexport interface CommentsModel extends CommentsActions {\n readonly state: ReadonlyStore<CommentsSnapshot>\n refresh: () => void\n dispose: () => void\n}\n\n/** A reference or a previous generation of the Bild-Studio (`images.generate` and `images.upload` answer one). `active` = goes into the next generation. */\nexport interface ImageStudioImage {\n /** Host-opaque. */\n id: string\n url: string\n title: string\n active: boolean\n}\n\n/** A managed prompt block (style, brand, …) of the host. */\nexport interface ImageStudioPromptBlock {\n id: string\n label: string\n text: string\n}\n\n/** Body of the image-generation route (`images.generate`): what the Bild-Studio sends for one chat turn. */\nexport interface ImageStudioGenerateBody {\n /** The block's description of the image. */\n description: string\n /** This turn's instruction; `''` generates from the description alone. */\n instruction: string\n /** The active prompt blocks, by the host's ids. */\n promptBlockIds: string[]\n /** The URLs of the active references and generations that go into the generation as reference images. */\n referenceUrls: string[]\n}\n"],"mappings":";;AAmDA,MAAa,YAAY;CAAC;CAAmB;CAAQ;CAAW;CAAoB;CAAU;CAAU;CAAW;CAAW;AAAS"}
|
package/dist/core/backend.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ModelRole } from "../settings/values.js";
|
|
2
2
|
import { EditorSettings } from "../settings/index.js";
|
|
3
3
|
import { BackendCapability } from "./backend-capability.js";
|
|
4
4
|
import { AuthorIdentity } from "./feature.js";
|
|
@@ -85,7 +85,7 @@ export interface EditorBackend {
|
|
|
85
85
|
ai?: {
|
|
86
86
|
models: () => Promise<{
|
|
87
87
|
presets: ModelProfile[];
|
|
88
|
-
|
|
88
|
+
roles: Record<ModelRole, string>;
|
|
89
89
|
}>;
|
|
90
90
|
complete: (body: {
|
|
91
91
|
before: string;
|
|
@@ -101,9 +101,9 @@ export interface EditorBackend {
|
|
|
101
101
|
extract: (body: ExtractRequest, signal: AbortSignal) => Promise<ExtractResponse>;
|
|
102
102
|
};
|
|
103
103
|
images?: {
|
|
104
|
-
/** The
|
|
104
|
+
/** The prompt blocks (style, brand, …) the host manages, in the order it lists them. */
|
|
105
105
|
promptBlocks: () => Promise<ImageStudioPromptBlock[]>;
|
|
106
|
-
/** Generates one image from the description, the instruction, the active
|
|
106
|
+
/** Generates one image from the description, the instruction, the active prompt blocks and the reference images; resolves with the stored image (`active: false`). */
|
|
107
107
|
generate: (body: ImageStudioGenerateBody, signal?: AbortSignal) => Promise<ImageStudioImage>;
|
|
108
108
|
/** Stores `file` as a reference image for document `documentId`; resolves with the stored image (`active: true`). */
|
|
109
109
|
upload: (documentId: string, file: File) => Promise<ImageStudioImage>;
|
package/dist/core/backend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.js","names":[],"sources":["../../src/core/backend.ts"],"sourcesContent":["import type { HocuspocusProvider } from '@hocuspocus/provider'\nimport type { Editor, JSONContent } from '@tiptap/core'\nimport type * as Y from 'yjs'\nimport type { EditorSettings,
|
|
1
|
+
{"version":3,"file":"backend.js","names":[],"sources":["../../src/core/backend.ts"],"sourcesContent":["import type { HocuspocusProvider } from '@hocuspocus/provider'\nimport type { Editor, JSONContent } from '@tiptap/core'\nimport type * as Y from 'yjs'\nimport type { EditorSettings, ModelRole } from '../settings'\nimport type { BackendCapability } from './backend-capability'\nimport type { CommentsModel, ExtractRequest, ExtractResponse, ImageStudioGenerateBody, ImageStudioImage, ImageStudioPromptBlock, MentionUser, ModelProfile, ModelRef, ThemeRecord, TranscriptCleanupRequest, TranscriptCleanupResponse } from './backend-types'\nimport type { AuthorIdentity } from './feature'\n\n// The client side of the data layer: everything the editor and its features ask of the host's backend (documents,\n// uploads, mention lookups, settings, themes, AI, collaboration, comments) is one method of `EditorBackend`. Two\n// adapters implement it: HTTP (`@ecsplico/editor/adapters/http`, the `/api/editor` and `/api/ai` routes the Nuxt module\n// and `installEditorServer` serve) and the host's own. A capability the backend does not offer is `undefined`.\n\n/** What a backend can do; the server's `EditorPorts` use the same names. */\nexport type { BackendCapability }\n\n/** One server-sent event of an AI stream (`delta` `{ text }`, `ops` the proposed edit operations, `done`, `error` `{ message }`). `data` is the parsed JSON payload (`{}` without one). */\nexport interface AiStreamEvent {\n event: 'delta' | 'ops' | 'done' | 'error'\n data: unknown\n}\n\n/** The token a collaboration server's `authenticate` receives: a string, or a function (also async) that returns it on each connect. */\nexport type CollaborationToken = string | (() => string) | (() => Promise<string>)\n\n/** A refused or failed backend request; `status` is the HTTP status (or its equivalent) and `message` the server's, when it sent one. */\nexport class BackendError extends Error {\n constructor(message: string, readonly status: number, options?: ErrorOptions) {\n super(message, options)\n this.name = 'BackendError'\n }\n}\n\n/** What a backend needs from the host to give a comments model its anchors and its author. */\nexport interface BackendCommentsOptions {\n /** Who writes: the author of new comments; `null` while nobody is signed in. */\n user: () => AuthorIdentity | null\n /** Whether the user may compose and change comments now. */\n canComment: () => boolean\n /** The editor the threads belong to; may return no editor yet or a destroyed one. */\n getEditor: () => Editor | undefined\n /** Called with every failed request. */\n onError?: (error: Error) => void\n}\n\nexport interface EditorBackend {\n /** Names the backend in error messages (`'http'`, `'memory'`, …). */\n readonly name: string\n /**\n * Settles once the backend knows which capabilities it really has (the HTTP adapter's `probe`, which drops what the\n * server lacks). `createEditorContext` awaits it before it reports `ready`, so a composition that prunes by capability\n * (`composeEditor(…, { backend })`) should run after it too. Never rejects; absent: usable at once.\n */\n readonly ready?: Promise<void>\n documents?: {\n /** The stored content; `null` when the document does not exist (yet). */\n load: (id: string) => Promise<JSONContent | null>\n save: (id: string, content: JSONContent) => Promise<void>\n }\n collaboration?: {\n /** Opens the live session of document `id`; `dispose()` closes the provider and the Y.Doc. */\n connect: (id: string, token?: CollaborationToken) => { ydoc: Y.Doc, provider: HocuspocusProvider, dispose: () => void }\n }\n assets?: {\n /** Stores `file` for document `documentId`; resolves with the URL it is addressed by and, when the backend hashes uploads, the SHA-256 (hex) of its bytes. */\n upload: (documentId: string, file: File) => Promise<{ url: string, sha256?: string }>\n }\n users?: {\n search: (query: string, signal?: AbortSignal) => Promise<MentionUser[]>\n }\n tags?: {\n search: (query: string, signal?: AbortSignal) => Promise<string[]>\n }\n settings?: {\n /** The account's settings; `null` when it has none stored yet. */\n load: () => Promise<EditorSettings | null>\n save: (settings: EditorSettings) => Promise<void>\n }\n themes?: {\n list: () => Promise<ThemeRecord[]>\n /** The stylesheet of theme `id` (unscoped). */\n css: (id: string) => Promise<string>\n /** Adds an Obsidian theme to the shared library: the stylesheet and, optionally, its `manifest.json`; the backend derives name and id. */\n store: (files: { css: File, manifest?: File }) => Promise<ThemeRecord>\n /** Deletes an uploaded theme; rejects for builtins and unknown ids. */\n remove: (id: string) => Promise<boolean>\n }\n ai?: {\n models: () => Promise<{ presets: ModelProfile[], roles: Record<ModelRole, string> }>\n complete: (body: { before: string, model: ModelRef }, signal: AbortSignal) => Promise<{ text: string }>\n /** Streams the SSE events of `generate` or `chat`; throws `BackendError` (with `status`) on a non-2xx answer before the stream starts. */\n stream: (path: 'generate' | 'chat', body: object, signal: AbortSignal) => AsyncIterable<AiStreamEvent>\n /** Speaker-segment cleanup of a transcript (one JSON answer, not a stream). */\n transcriptCleanup: (body: TranscriptCleanupRequest, signal: AbortSignal) => Promise<TranscriptCleanupResponse>\n /** Turns a stored asset into readable content: transcription (with segment timestamps), PDF text or OCR, an image description, bibliographic data. */\n extract: (body: ExtractRequest, signal: AbortSignal) => Promise<ExtractResponse>\n }\n images?: {\n /** The prompt blocks (style, brand, …) the host manages, in the order it lists them. */\n promptBlocks: () => Promise<ImageStudioPromptBlock[]>\n /** Generates one image from the description, the instruction, the active prompt blocks and the reference images; resolves with the stored image (`active: false`). */\n generate: (body: ImageStudioGenerateBody, signal?: AbortSignal) => Promise<ImageStudioImage>\n /** Stores `file` as a reference image for document `documentId`; resolves with the stored image (`active: true`). */\n upload: (documentId: string, file: File) => Promise<ImageStudioImage>\n }\n /** The comments model of document `documentId`. */\n comments?: (documentId: string, options: BackendCommentsOptions) => CommentsModel\n}\n"],"mappings":";;AA0BA,IAAa,eAAb,cAAkC,MAAM;CACA;CAAtC,YAAY,SAAiB,QAAyB,SAAwB;EAC5E,MAAM,SAAS,OAAO;EADc,KAAA,SAAA;EAEpC,KAAK,OAAO;CACd;AACF"}
|
|
@@ -48,7 +48,7 @@ export declare function blockConversion(providers: readonly ConversionProvider[]
|
|
|
48
48
|
/**
|
|
49
49
|
* Client-only, no schema: the `convertBlockTo` command over the preset's conversion providers — the „Umwandeln in …“
|
|
50
50
|
* block action and the toolbar's conversion groups. One transaction, so one undo step. The target must pass the same
|
|
51
|
-
* insert guards as creating that node there (`guardsAccept`): a
|
|
51
|
+
* insert guards as creating that node there (`guardsAccept`): a Block type its parent does not accept is refused.
|
|
52
52
|
*/
|
|
53
53
|
export declare const BlockConvert: Extension<any, any>;
|
|
54
54
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TYPE_ABILITIES } from "./schema/abilities.js";
|
|
2
2
|
import { abilitiesAt } from "./abilities.js";
|
|
3
|
+
import { isBlockContainer, isChromeNode } from "./schema/node-flags.js";
|
|
3
4
|
import { conversionsOf } from "./pipeline.js";
|
|
4
5
|
import { guardsAccept } from "./commands.js";
|
|
5
|
-
import { isBlockContainer, isChromeNode } from "./schema/node-flags.js";
|
|
6
6
|
import { convertCoreBlocks, convertTargetId, coreConversionTargets, isCoreTarget, parseCoreTarget } from "./convert.js";
|
|
7
7
|
import { Extension } from "@tiptap/core";
|
|
8
8
|
import { Selection } from "@tiptap/pm/state";
|
|
@@ -128,9 +128,9 @@ function createdBy(target) {
|
|
|
128
128
|
type: "heading",
|
|
129
129
|
attrs: { level: target.level }
|
|
130
130
|
};
|
|
131
|
-
case "
|
|
132
|
-
type: "
|
|
133
|
-
attrs: {
|
|
131
|
+
case "typedBlock": return {
|
|
132
|
+
type: "typedBlock",
|
|
133
|
+
attrs: { blockType: target.blockType }
|
|
134
134
|
};
|
|
135
135
|
case "custom": return {
|
|
136
136
|
type: target.id,
|
|
@@ -142,7 +142,7 @@ function createdBy(target) {
|
|
|
142
142
|
/**
|
|
143
143
|
* Client-only, no schema: the `convertBlockTo` command over the preset's conversion providers — the „Umwandeln in …“
|
|
144
144
|
* block action and the toolbar's conversion groups. One transaction, so one undo step. The target must pass the same
|
|
145
|
-
* insert guards as creating that node there (`guardsAccept`): a
|
|
145
|
+
* insert guards as creating that node there (`guardsAccept`): a Block type its parent does not accept is refused.
|
|
146
146
|
*/
|
|
147
147
|
const BlockConvert = Extension.create({
|
|
148
148
|
name: "blockConvert",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-convert.js","names":[],"sources":["../../src/core/block-convert.ts"],"sourcesContent":["import { type Editor, Extension } from '@tiptap/core'\nimport { Fragment, type Node as PMNode, type ResolvedPos } from '@tiptap/pm/model'\nimport { type EditorState, Selection } from '@tiptap/pm/state'\nimport { abilitiesAt } from './abilities'\nimport type { Creates, MenuItem } from './command-types'\nimport { guardsAccept } from './commands'\nimport { type ConvertTarget, convertCoreBlocks, convertTargetId, coreConversionTargets, isCoreTarget, parseCoreTarget } from './convert'\nimport type { BlockTarget } from './extension-points'\nimport type { ConversionProvider } from './feature'\nimport type { MessageKey, Translate } from '../i18n'\nimport { conversionsOf } from './pipeline'\nimport { isBlockContainer, isChromeNode } from './schema/node-flags'\nimport { TYPE_ABILITIES } from './schema/abilities'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n blockConvert: {\n /**\n * Convert the block at `pos` (default: the caret's block in the document or another block container; a chrome\n * node stands for its parent) into `target`. False when the conversion changes nothing or is impossible there.\n */\n convertBlockTo: (target: ConvertTarget, pos?: number) => ReturnType\n }\n }\n}\n\nconst CORE_LABELS: Record<string, { key: MessageKey, icon: string }> = {\n paragraph: { key: 'block.paragraph', icon: 'i-lucide-pilcrow' },\n bulletList: { key: 'block.bulletList', icon: 'i-lucide-list' },\n orderedList: { key: 'block.orderedList', icon: 'i-lucide-list-ordered' },\n blockquote: { key: 'block.blockquote', icon: 'i-lucide-text-quote' },\n codeBlock: { key: 'block.codeBlock', icon: 'i-lucide-square-code' },\n}\n\n/**\n * The standard conversions: paragraphs, headings, lists, quotes and code blocks into each other. Declines every other\n * target, so features that add targets (speaker segments, composites) own them in their own providers.\n */\nexport const coreConversions: ConversionProvider = {\n targets: (_editor, node) => coreConversionTargets(node),\n parseTarget: parseCoreTarget,\n label: (target, t) => {\n if (target.type === 'heading') return { label: t('block.heading', { level: target.level }), icon: `i-lucide-heading-${target.level}` }\n const core = isCoreTarget(target) ? CORE_LABELS[target.type] : undefined\n return core ? { label: t(core.key), icon: core.icon } : null\n },\n convert: (state, $start, target) => {\n const node = $start.nodeAfter\n if (!node) return null\n const blocks = convertCoreBlocks(state.schema, [node], target)\n return blocks ? { from: $start.pos, to: $start.pos + node.nodeSize, blocks } : null\n },\n}\n\n/** The provider that owns `target`: the first, in feature order, that parses its id. */\nexport function targetOwner(providers: readonly ConversionProvider[], target: ConvertTarget): ConversionProvider | undefined {\n const id = convertTargetId(target)\n return providers.find(provider => provider.parseTarget(id) !== null)\n}\n\n/** The target `id` names, by its owner; null when no provider knows it. */\nexport function parseConversionTarget(providers: readonly ConversionProvider[], id: string): ConvertTarget | null {\n for (const provider of providers) {\n const target = provider.parseTarget(id)\n if (target) return target\n }\n return null\n}\n\n/** Label (in the language of `t`) and icon of `target`, from its owner. */\nexport function conversionLabel(providers: readonly ConversionProvider[], target: ConvertTarget, t: Translate): { label: string, icon: string } {\n const label = targetOwner(providers, target)?.label(target, t)\n if (!label) throw new Error(`@ecsplico/editor: no conversion provider owns the target \"${convertTargetId(target)}\"`)\n return label\n}\n\n/** The targets offered for `node` per provider, in feature order; a target id offered twice is an error. */\nexport function conversionTargetGroups(providers: readonly ConversionProvider[], editor: Editor, node: PMNode): ConvertTarget[][] {\n const offered = new Set<string>()\n return providers.map(provider => provider.targets(editor, node).filter((target) => {\n const id = convertTargetId(target)\n if (offered.has(id)) throw new Error(`@ecsplico/editor: the conversion target \"${id}\" is advertised by more than one provider`)\n offered.add(id)\n return true\n }))\n}\n\n/** The `$pos` right in front of the block to convert; a chrome node stands for its parent block. */\nfunction blockStart(state: EditorState, pos: number | undefined): ResolvedPos | null {\n let $start: ResolvedPos\n if (pos === undefined) {\n // The caret's block whose parent is a block container (the document, a composite).\n const { $head } = state.selection\n let depth = $head.depth\n while (depth > 0 && !isBlockContainer($head.node(depth - 1))) depth--\n if (depth === 0) return null\n $start = state.doc.resolve($head.before(depth))\n }\n else {\n if (pos < 0 || pos >= state.doc.content.size) return null\n $start = state.doc.resolve(pos)\n }\n const node = $start.nodeAfter\n if (!node) return null\n return isChromeNode(node) ? state.doc.resolve($start.before()) : $start\n}\n\n/**\n * The conversion of the block right after `$start` into `target`: the range it replaces and the converted blocks.\n * A provider claiming the source block (`handlesSource`) executes; otherwise the target's owner does. Null when the\n * conversion changes nothing or the result does not fit there (`parent.canReplace`). Shared by `convertBlockTo` and\n * the chat's `convert` op.\n */\nexport function blockConversion(providers: readonly ConversionProvider[], state: EditorState, $start: ResolvedPos, target: ConvertTarget): { from: number, to: number, blocks: PMNode[] } | null {\n const node = $start.nodeAfter\n if (!node) return null\n const provider = providers.find(candidate => candidate.handlesSource?.(node)) ?? targetOwner(providers, target)\n const conversion = provider?.convert(state, $start, target)\n if (!conversion) return null\n const parent = $start.parent\n const $end = state.doc.resolve(conversion.to)\n if ($end.parent !== parent || !parent.canReplace($start.index(), $end.index(), Fragment.from(conversion.blocks))) return null\n return conversion\n}\n\n/** The node a conversion into `target` creates, as the insert guards see it (`Creates`). */\nfunction createdBy(target: ConvertTarget): Creates {\n switch (target.type) {\n case 'heading': return { type: 'heading', attrs: { level: target.level } }\n case 'composite': return { type: 'composite', attrs: { kind: target.kind } }\n case 'custom': return { type: target.id, ...(target.attrs && { attrs: target.attrs }) }\n default: return { type: target.type }\n }\n}\n\n/**\n * Client-only, no schema: the `convertBlockTo` command over the preset's conversion providers — the „Umwandeln in …“\n * block action and the toolbar's conversion groups. One transaction, so one undo step. The target must pass the same\n * insert guards as creating that node there (`guardsAccept`): a Baustein kind its parent does not accept is refused.\n */\nexport const BlockConvert = Extension.create({\n name: 'blockConvert',\n\n addCommands() {\n return {\n convertBlockTo: (target, pos) => ({ editor, state, tr, dispatch }) => {\n const $start = blockStart(state, pos)\n if (!$start) return false\n const conversion = blockConversion(conversionsOf(editor), state, $start, target)\n if (!conversion || !guardsAccept(editor, state.doc.resolve($start.start()), createdBy(target))) return false\n if (dispatch) {\n tr.replaceWith(conversion.from, conversion.to, conversion.blocks)\n const $first = tr.doc.resolve(conversion.from)\n tr.setSelection(Selection.findFrom($first, 1, true) ?? Selection.near($first)).scrollIntoView()\n }\n return true\n },\n }\n },\n})\n\n/**\n * „Umwandeln in …“ entries for the block `target`: the targets the providers offer whose type the block's container\n * allows (`TYPE_ABILITIES`), one separated group per provider in feature order; targets the block cannot be converted\n * to right now are disabled. A list item stands for its list.\n */\nexport function convertMenuItems(editor: Editor, target: BlockTarget, t: Translate): MenuItem[] {\n let { node, pos } = target\n if (node.type.name === 'listItem') {\n const $item = editor.state.doc.resolve(pos)\n node = $item.parent\n pos = $item.before()\n }\n const providers = conversionsOf(editor)\n const abilities = abilitiesAt(editor, pos)\n const groups = conversionTargetGroups(providers, editor, node)\n .map(targets => targets\n .filter(to => !TYPE_ABILITIES[to.type] || abilities.has(TYPE_ABILITIES[to.type]!))\n .map((to): MenuItem => ({\n ...conversionLabel(providers, to, t),\n disabled: !editor.can().convertBlockTo(to, pos),\n onSelect: () => editor.chain().focus().convertBlockTo(to, pos).run(),\n })))\n .filter(items => items.length > 0)\n return groups.flatMap((items, index) => index ? [{ type: 'separator' } as const, ...items] : items)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"block-convert.js","names":[],"sources":["../../src/core/block-convert.ts"],"sourcesContent":["import { type Editor, Extension } from '@tiptap/core'\nimport { Fragment, type Node as PMNode, type ResolvedPos } from '@tiptap/pm/model'\nimport { type EditorState, Selection } from '@tiptap/pm/state'\nimport { abilitiesAt } from './abilities'\nimport type { Creates, MenuItem } from './command-types'\nimport { guardsAccept } from './commands'\nimport { type ConvertTarget, convertCoreBlocks, convertTargetId, coreConversionTargets, isCoreTarget, parseCoreTarget } from './convert'\nimport type { BlockTarget } from './extension-points'\nimport type { ConversionProvider } from './feature'\nimport type { MessageKey, Translate } from '../i18n'\nimport { conversionsOf } from './pipeline'\nimport { isBlockContainer, isChromeNode } from './schema/node-flags'\nimport { TYPE_ABILITIES } from './schema/abilities'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n blockConvert: {\n /**\n * Convert the block at `pos` (default: the caret's block in the document or another block container; a chrome\n * node stands for its parent) into `target`. False when the conversion changes nothing or is impossible there.\n */\n convertBlockTo: (target: ConvertTarget, pos?: number) => ReturnType\n }\n }\n}\n\nconst CORE_LABELS: Record<string, { key: MessageKey, icon: string }> = {\n paragraph: { key: 'block.paragraph', icon: 'i-lucide-pilcrow' },\n bulletList: { key: 'block.bulletList', icon: 'i-lucide-list' },\n orderedList: { key: 'block.orderedList', icon: 'i-lucide-list-ordered' },\n blockquote: { key: 'block.blockquote', icon: 'i-lucide-text-quote' },\n codeBlock: { key: 'block.codeBlock', icon: 'i-lucide-square-code' },\n}\n\n/**\n * The standard conversions: paragraphs, headings, lists, quotes and code blocks into each other. Declines every other\n * target, so features that add targets (speaker segments, composites) own them in their own providers.\n */\nexport const coreConversions: ConversionProvider = {\n targets: (_editor, node) => coreConversionTargets(node),\n parseTarget: parseCoreTarget,\n label: (target, t) => {\n if (target.type === 'heading') return { label: t('block.heading', { level: target.level }), icon: `i-lucide-heading-${target.level}` }\n const core = isCoreTarget(target) ? CORE_LABELS[target.type] : undefined\n return core ? { label: t(core.key), icon: core.icon } : null\n },\n convert: (state, $start, target) => {\n const node = $start.nodeAfter\n if (!node) return null\n const blocks = convertCoreBlocks(state.schema, [node], target)\n return blocks ? { from: $start.pos, to: $start.pos + node.nodeSize, blocks } : null\n },\n}\n\n/** The provider that owns `target`: the first, in feature order, that parses its id. */\nexport function targetOwner(providers: readonly ConversionProvider[], target: ConvertTarget): ConversionProvider | undefined {\n const id = convertTargetId(target)\n return providers.find(provider => provider.parseTarget(id) !== null)\n}\n\n/** The target `id` names, by its owner; null when no provider knows it. */\nexport function parseConversionTarget(providers: readonly ConversionProvider[], id: string): ConvertTarget | null {\n for (const provider of providers) {\n const target = provider.parseTarget(id)\n if (target) return target\n }\n return null\n}\n\n/** Label (in the language of `t`) and icon of `target`, from its owner. */\nexport function conversionLabel(providers: readonly ConversionProvider[], target: ConvertTarget, t: Translate): { label: string, icon: string } {\n const label = targetOwner(providers, target)?.label(target, t)\n if (!label) throw new Error(`@ecsplico/editor: no conversion provider owns the target \"${convertTargetId(target)}\"`)\n return label\n}\n\n/** The targets offered for `node` per provider, in feature order; a target id offered twice is an error. */\nexport function conversionTargetGroups(providers: readonly ConversionProvider[], editor: Editor, node: PMNode): ConvertTarget[][] {\n const offered = new Set<string>()\n return providers.map(provider => provider.targets(editor, node).filter((target) => {\n const id = convertTargetId(target)\n if (offered.has(id)) throw new Error(`@ecsplico/editor: the conversion target \"${id}\" is advertised by more than one provider`)\n offered.add(id)\n return true\n }))\n}\n\n/** The `$pos` right in front of the block to convert; a chrome node stands for its parent block. */\nfunction blockStart(state: EditorState, pos: number | undefined): ResolvedPos | null {\n let $start: ResolvedPos\n if (pos === undefined) {\n // The caret's block whose parent is a block container (the document, a composite).\n const { $head } = state.selection\n let depth = $head.depth\n while (depth > 0 && !isBlockContainer($head.node(depth - 1))) depth--\n if (depth === 0) return null\n $start = state.doc.resolve($head.before(depth))\n }\n else {\n if (pos < 0 || pos >= state.doc.content.size) return null\n $start = state.doc.resolve(pos)\n }\n const node = $start.nodeAfter\n if (!node) return null\n return isChromeNode(node) ? state.doc.resolve($start.before()) : $start\n}\n\n/**\n * The conversion of the block right after `$start` into `target`: the range it replaces and the converted blocks.\n * A provider claiming the source block (`handlesSource`) executes; otherwise the target's owner does. Null when the\n * conversion changes nothing or the result does not fit there (`parent.canReplace`). Shared by `convertBlockTo` and\n * the chat's `convert` op.\n */\nexport function blockConversion(providers: readonly ConversionProvider[], state: EditorState, $start: ResolvedPos, target: ConvertTarget): { from: number, to: number, blocks: PMNode[] } | null {\n const node = $start.nodeAfter\n if (!node) return null\n const provider = providers.find(candidate => candidate.handlesSource?.(node)) ?? targetOwner(providers, target)\n const conversion = provider?.convert(state, $start, target)\n if (!conversion) return null\n const parent = $start.parent\n const $end = state.doc.resolve(conversion.to)\n if ($end.parent !== parent || !parent.canReplace($start.index(), $end.index(), Fragment.from(conversion.blocks))) return null\n return conversion\n}\n\n/** The node a conversion into `target` creates, as the insert guards see it (`Creates`). */\nfunction createdBy(target: ConvertTarget): Creates {\n switch (target.type) {\n case 'heading': return { type: 'heading', attrs: { level: target.level } }\n case 'typedBlock': return { type: 'typedBlock', attrs: { blockType: target.blockType } }\n case 'custom': return { type: target.id, ...(target.attrs && { attrs: target.attrs }) }\n default: return { type: target.type }\n }\n}\n\n/**\n * Client-only, no schema: the `convertBlockTo` command over the preset's conversion providers — the „Umwandeln in …“\n * block action and the toolbar's conversion groups. One transaction, so one undo step. The target must pass the same\n * insert guards as creating that node there (`guardsAccept`): a Block type its parent does not accept is refused.\n */\nexport const BlockConvert = Extension.create({\n name: 'blockConvert',\n\n addCommands() {\n return {\n convertBlockTo: (target, pos) => ({ editor, state, tr, dispatch }) => {\n const $start = blockStart(state, pos)\n if (!$start) return false\n const conversion = blockConversion(conversionsOf(editor), state, $start, target)\n if (!conversion || !guardsAccept(editor, state.doc.resolve($start.start()), createdBy(target))) return false\n if (dispatch) {\n tr.replaceWith(conversion.from, conversion.to, conversion.blocks)\n const $first = tr.doc.resolve(conversion.from)\n tr.setSelection(Selection.findFrom($first, 1, true) ?? Selection.near($first)).scrollIntoView()\n }\n return true\n },\n }\n },\n})\n\n/**\n * „Umwandeln in …“ entries for the block `target`: the targets the providers offer whose type the block's container\n * allows (`TYPE_ABILITIES`), one separated group per provider in feature order; targets the block cannot be converted\n * to right now are disabled. A list item stands for its list.\n */\nexport function convertMenuItems(editor: Editor, target: BlockTarget, t: Translate): MenuItem[] {\n let { node, pos } = target\n if (node.type.name === 'listItem') {\n const $item = editor.state.doc.resolve(pos)\n node = $item.parent\n pos = $item.before()\n }\n const providers = conversionsOf(editor)\n const abilities = abilitiesAt(editor, pos)\n const groups = conversionTargetGroups(providers, editor, node)\n .map(targets => targets\n .filter(to => !TYPE_ABILITIES[to.type] || abilities.has(TYPE_ABILITIES[to.type]!))\n .map((to): MenuItem => ({\n ...conversionLabel(providers, to, t),\n disabled: !editor.can().convertBlockTo(to, pos),\n onSelect: () => editor.chain().focus().convertBlockTo(to, pos).run(),\n })))\n .filter(items => items.length > 0)\n return groups.flatMap((items, index) => index ? [{ type: 'separator' } as const, ...items] : items)\n}\n"],"mappings":";;;;;;;;;;AA0BA,MAAM,cAAiE;CACrE,WAAW;EAAE,KAAK;EAAmB,MAAM;CAAmB;CAC9D,YAAY;EAAE,KAAK;EAAoB,MAAM;CAAgB;CAC7D,aAAa;EAAE,KAAK;EAAqB,MAAM;CAAwB;CACvE,YAAY;EAAE,KAAK;EAAoB,MAAM;CAAsB;CACnE,WAAW;EAAE,KAAK;EAAmB,MAAM;CAAuB;AACpE;;;;;AAMA,MAAa,kBAAsC;CACjD,UAAU,SAAS,SAAS,sBAAsB,IAAI;CACtD,aAAa;CACb,QAAQ,QAAQ,MAAM;EACpB,IAAI,OAAO,SAAS,WAAW,OAAO;GAAE,OAAO,EAAE,iBAAiB,EAAE,OAAO,OAAO,MAAM,CAAC;GAAG,MAAM,oBAAoB,OAAO;EAAQ;EACrI,MAAM,OAAO,aAAa,MAAM,IAAI,YAAY,OAAO,QAAQ,KAAA;EAC/D,OAAO,OAAO;GAAE,OAAO,EAAE,KAAK,GAAG;GAAG,MAAM,KAAK;EAAK,IAAI;CAC1D;CACA,UAAU,OAAO,QAAQ,WAAW;EAClC,MAAM,OAAO,OAAO;EACpB,IAAI,CAAC,MAAM,OAAO;EAClB,MAAM,SAAS,kBAAkB,MAAM,QAAQ,CAAC,IAAI,GAAG,MAAM;EAC7D,OAAO,SAAS;GAAE,MAAM,OAAO;GAAK,IAAI,OAAO,MAAM,KAAK;GAAU;EAAO,IAAI;CACjF;AACF;;AAGA,SAAgB,YAAY,WAA0C,QAAuD;CAC3H,MAAM,KAAK,gBAAgB,MAAM;CACjC,OAAO,UAAU,MAAK,aAAY,SAAS,YAAY,EAAE,MAAM,IAAI;AACrE;;AAGA,SAAgB,sBAAsB,WAA0C,IAAkC;CAChH,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,SAAS,SAAS,YAAY,EAAE;EACtC,IAAI,QAAQ,OAAO;CACrB;CACA,OAAO;AACT;;AAGA,SAAgB,gBAAgB,WAA0C,QAAuB,GAA+C;CAC9I,MAAM,QAAQ,YAAY,WAAW,MAAM,CAAC,EAAE,MAAM,QAAQ,CAAC;CAC7D,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,6DAA6D,gBAAgB,MAAM,EAAE,EAAE;CACnH,OAAO;AACT;;AAGA,SAAgB,uBAAuB,WAA0C,QAAgB,MAAiC;CAChI,MAAM,0BAAU,IAAI,IAAY;CAChC,OAAO,UAAU,KAAI,aAAY,SAAS,QAAQ,QAAQ,IAAI,CAAC,CAAC,QAAQ,WAAW;EACjF,MAAM,KAAK,gBAAgB,MAAM;EACjC,IAAI,QAAQ,IAAI,EAAE,GAAG,MAAM,IAAI,MAAM,4CAA4C,GAAG,0CAA0C;EAC9H,QAAQ,IAAI,EAAE;EACd,OAAO;CACT,CAAC,CAAC;AACJ;;AAGA,SAAS,WAAW,OAAoB,KAA6C;CACnF,IAAI;CACJ,IAAI,QAAQ,KAAA,GAAW;EAErB,MAAM,EAAE,UAAU,MAAM;EACxB,IAAI,QAAQ,MAAM;EAClB,OAAO,QAAQ,KAAK,CAAC,iBAAiB,MAAM,KAAK,QAAQ,CAAC,CAAC,GAAG;EAC9D,IAAI,UAAU,GAAG,OAAO;EACxB,SAAS,MAAM,IAAI,QAAQ,MAAM,OAAO,KAAK,CAAC;CAChD,OACK;EACH,IAAI,MAAM,KAAK,OAAO,MAAM,IAAI,QAAQ,MAAM,OAAO;EACrD,SAAS,MAAM,IAAI,QAAQ,GAAG;CAChC;CACA,MAAM,OAAO,OAAO;CACpB,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO,aAAa,IAAI,IAAI,MAAM,IAAI,QAAQ,OAAO,OAAO,CAAC,IAAI;AACnE;;;;;;;AAQA,SAAgB,gBAAgB,WAA0C,OAAoB,QAAqB,QAA8E;CAC/L,MAAM,OAAO,OAAO;CACpB,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,cADW,UAAU,MAAK,cAAa,UAAU,gBAAgB,IAAI,CAAC,KAAK,YAAY,WAAW,MAAM,EAAA,EACjF,QAAQ,OAAO,QAAQ,MAAM;CAC1D,IAAI,CAAC,YAAY,OAAO;CACxB,MAAM,SAAS,OAAO;CACtB,MAAM,OAAO,MAAM,IAAI,QAAQ,WAAW,EAAE;CAC5C,IAAI,KAAK,WAAW,UAAU,CAAC,OAAO,WAAW,OAAO,MAAM,GAAG,KAAK,MAAM,GAAG,SAAS,KAAK,WAAW,MAAM,CAAC,GAAG,OAAO;CACzH,OAAO;AACT;;AAGA,SAAS,UAAU,QAAgC;CACjD,QAAQ,OAAO,MAAf;EACE,KAAK,WAAW,OAAO;GAAE,MAAM;GAAW,OAAO,EAAE,OAAO,OAAO,MAAM;EAAE;EACzE,KAAK,cAAc,OAAO;GAAE,MAAM;GAAc,OAAO,EAAE,WAAW,OAAO,UAAU;EAAE;EACvF,KAAK,UAAU,OAAO;GAAE,MAAM,OAAO;GAAI,GAAI,OAAO,SAAS,EAAE,OAAO,OAAO,MAAM;EAAG;EACtF,SAAS,OAAO,EAAE,MAAM,OAAO,KAAK;CACtC;AACF;;;;;;AAOA,MAAa,eAAe,UAAU,OAAO;CAC3C,MAAM;CAEN,cAAc;EACZ,OAAO,EACL,iBAAiB,QAAQ,SAAS,EAAE,QAAQ,OAAO,IAAI,eAAe;GACpE,MAAM,SAAS,WAAW,OAAO,GAAG;GACpC,IAAI,CAAC,QAAQ,OAAO;GACpB,MAAM,aAAa,gBAAgB,cAAc,MAAM,GAAG,OAAO,QAAQ,MAAM;GAC/E,IAAI,CAAC,cAAc,CAAC,aAAa,QAAQ,MAAM,IAAI,QAAQ,OAAO,MAAM,CAAC,GAAG,UAAU,MAAM,CAAC,GAAG,OAAO;GACvG,IAAI,UAAU;IACZ,GAAG,YAAY,WAAW,MAAM,WAAW,IAAI,WAAW,MAAM;IAChE,MAAM,SAAS,GAAG,IAAI,QAAQ,WAAW,IAAI;IAC7C,GAAG,aAAa,UAAU,SAAS,QAAQ,GAAG,IAAI,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe;GAChG;GACA,OAAO;EACT,EACF;CACF;AACF,CAAC;;;;;;AAOD,SAAgB,iBAAiB,QAAgB,QAAqB,GAA0B;CAC9F,IAAI,EAAE,MAAM,QAAQ;CACpB,IAAI,KAAK,KAAK,SAAS,YAAY;EACjC,MAAM,QAAQ,OAAO,MAAM,IAAI,QAAQ,GAAG;EAC1C,OAAO,MAAM;EACb,MAAM,MAAM,OAAO;CACrB;CACA,MAAM,YAAY,cAAc,MAAM;CACtC,MAAM,YAAY,YAAY,QAAQ,GAAG;CAUzC,OATe,uBAAuB,WAAW,QAAQ,IAAI,CAAC,CAC3D,KAAI,YAAW,QACb,QAAO,OAAM,CAAC,eAAe,GAAG,SAAS,UAAU,IAAI,eAAe,GAAG,KAAM,CAAC,CAAC,CACjF,KAAK,QAAkB;EACtB,GAAG,gBAAgB,WAAW,IAAI,CAAC;EACnC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,eAAe,IAAI,GAAG;EAC9C,gBAAgB,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,eAAe,IAAI,GAAG,CAAC,CAAC,IAAI;CACrE,EAAE,CAAC,CAAC,CACL,QAAO,UAAS,MAAM,SAAS,CACtB,CAAC,CAAC,SAAS,OAAO,UAAU,QAAQ,CAAC,EAAE,MAAM,YAAY,GAAY,GAAG,KAAK,IAAI,KAAK;AACpG"}
|
|
@@ -7,9 +7,9 @@ export interface BlockTransfer {
|
|
|
7
7
|
/** Document the block lives in; `null` when its editor has none. */
|
|
8
8
|
documentId: string | null;
|
|
9
9
|
blockId: string | null;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
/**
|
|
10
|
+
/** Block type id of a Typed block; `null` for every other block. */
|
|
11
|
+
blockType: string | null;
|
|
12
|
+
/** Typed block: its title text; other blocks: the first 80 characters of their text. */
|
|
13
13
|
title: string;
|
|
14
14
|
json: JSONContent;
|
|
15
15
|
/** The block as Markdown; empty when the editor has no Markdown serializer. */
|
|
@@ -19,7 +19,11 @@ export interface BlockTransfer {
|
|
|
19
19
|
type TransferData = Pick<DataTransfer, 'setData' | 'getData' | 'types'>;
|
|
20
20
|
/** Writes `payload` under `BLOCK_MIME`. */
|
|
21
21
|
export declare function writeBlockTransfer(dataTransfer: Pick<DataTransfer, 'setData'>, payload: BlockTransfer): void;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* The block payload of a drag, or `null` when it carries none or one that does not parse or has the wrong shape. A payload
|
|
24
|
+
* written before the rename of Typed blocks (Block type id in `kind`, `json` with old node and attribute names) is read
|
|
25
|
+
* as the current shape: `json` goes through `migratePersistedNames`, the Block type id is taken from `blockType` or `kind`.
|
|
26
|
+
*/
|
|
23
27
|
export declare function readBlockTransfer(dataTransfer: Partial<TransferData>): BlockTransfer | null;
|
|
24
28
|
/**
|
|
25
29
|
* The payload of the block whose own position is `pos`; `null` when `pos` holds no block. The Markdown is empty when
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { blockIdOf } from "./schema/block-ids.js";
|
|
2
|
+
import { nodeAtPos } from "./util.js";
|
|
3
|
+
import { TYPED_BLOCK, migratePersistedNames } from "./schema/persisted-names.js";
|
|
2
4
|
import { Extension } from "@tiptap/core";
|
|
3
5
|
//#region src/core/block-transfer.ts
|
|
4
6
|
/** MIME type of the block payload a drag carries next to its plain text; another editor surface or document tree reads it on drop. */
|
|
@@ -9,7 +11,12 @@ function writeBlockTransfer(dataTransfer, payload) {
|
|
|
9
11
|
dataTransfer.setData(BLOCK_MIME, JSON.stringify(payload));
|
|
10
12
|
}
|
|
11
13
|
const isStringOrNull = (value) => value === null || typeof value === "string";
|
|
12
|
-
|
|
14
|
+
const isJSONContent = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && "type" in value && typeof value.type === "string";
|
|
15
|
+
/**
|
|
16
|
+
* The block payload of a drag, or `null` when it carries none or one that does not parse or has the wrong shape. A payload
|
|
17
|
+
* written before the rename of Typed blocks (Block type id in `kind`, `json` with old node and attribute names) is read
|
|
18
|
+
* as the current shape: `json` goes through `migratePersistedNames`, the Block type id is taken from `blockType` or `kind`.
|
|
19
|
+
*/
|
|
13
20
|
function readBlockTransfer(dataTransfer) {
|
|
14
21
|
if (!dataTransfer.types || !Array.from(dataTransfer.types).includes("application/x-tip-block")) return null;
|
|
15
22
|
let value;
|
|
@@ -19,15 +26,16 @@ function readBlockTransfer(dataTransfer) {
|
|
|
19
26
|
return null;
|
|
20
27
|
}
|
|
21
28
|
if (typeof value !== "object" || value === null) return null;
|
|
22
|
-
const { documentId, blockId, kind, title, json, markdown } = value;
|
|
23
|
-
|
|
24
|
-
if (
|
|
29
|
+
const { documentId, blockId, blockType: current, kind: old, title, json, markdown } = value;
|
|
30
|
+
const blockType = current === void 0 ? old : current;
|
|
31
|
+
if (!isStringOrNull(documentId) || !isStringOrNull(blockId) || !isStringOrNull(blockType) || typeof title !== "string" || typeof markdown !== "string") return null;
|
|
32
|
+
if (!isJSONContent(json)) return null;
|
|
25
33
|
return {
|
|
26
34
|
documentId,
|
|
27
35
|
blockId,
|
|
28
|
-
|
|
36
|
+
blockType,
|
|
29
37
|
title,
|
|
30
|
-
json,
|
|
38
|
+
json: migratePersistedNames(json),
|
|
31
39
|
markdown
|
|
32
40
|
};
|
|
33
41
|
}
|
|
@@ -36,17 +44,16 @@ function readBlockTransfer(dataTransfer) {
|
|
|
36
44
|
* `editor.markdown` is unavailable (a headless editor without `DocumentMarkdown`).
|
|
37
45
|
*/
|
|
38
46
|
function blockTransferOf(editor, pos) {
|
|
39
|
-
|
|
40
|
-
const node = editor.state.doc.nodeAt(pos);
|
|
47
|
+
const node = nodeAtPos(editor.state.doc, pos);
|
|
41
48
|
if (!node || !node.isBlock) return null;
|
|
42
49
|
const json = node.toJSON();
|
|
43
|
-
const
|
|
44
|
-
const
|
|
50
|
+
const typedBlock = node.type.name === TYPED_BLOCK;
|
|
51
|
+
const blockType = typedBlock ? node.attrs.blockType : null;
|
|
45
52
|
return {
|
|
46
|
-
documentId: editor
|
|
53
|
+
documentId: documentIdOf(editor),
|
|
47
54
|
blockId: blockIdOf(node),
|
|
48
|
-
|
|
49
|
-
title:
|
|
55
|
+
blockType: typeof blockType === "string" ? blockType : null,
|
|
56
|
+
title: typedBlock ? node.firstChild?.textContent ?? "" : node.textContent.slice(0, TITLE_LENGTH),
|
|
50
57
|
json,
|
|
51
58
|
markdown: editor.markdown?.serialize({
|
|
52
59
|
type: "doc",
|
|
@@ -54,6 +61,8 @@ function blockTransferOf(editor, pos) {
|
|
|
54
61
|
}) ?? ""
|
|
55
62
|
};
|
|
56
63
|
}
|
|
64
|
+
/** The id of `editor`'s document as the `blockTransfer` storage knows it; null when the editor has none. */
|
|
65
|
+
const documentIdOf = (editor) => editor.storage.blockTransfer?.documentId ?? null;
|
|
57
66
|
/** Carries the editor's document id so `blockTransferOf` can name where a block comes from (`editor.storage.blockTransfer`). */
|
|
58
67
|
const BlockTransferStorage = Extension.create({
|
|
59
68
|
name: "blockTransfer",
|
|
@@ -63,6 +72,6 @@ const BlockTransferStorage = Extension.create({
|
|
|
63
72
|
}
|
|
64
73
|
});
|
|
65
74
|
//#endregion
|
|
66
|
-
export { BLOCK_MIME, BlockTransferStorage, blockTransferOf, readBlockTransfer, writeBlockTransfer };
|
|
75
|
+
export { BLOCK_MIME, BlockTransferStorage, blockTransferOf, documentIdOf, readBlockTransfer, writeBlockTransfer };
|
|
67
76
|
|
|
68
77
|
//# sourceMappingURL=block-transfer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-transfer.js","names":[],"sources":["../../src/core/block-transfer.ts"],"sourcesContent":["import { type Editor, Extension, type JSONContent } from '@tiptap/core'\nimport { blockIdOf } from './schema/block-ids'\n\n/** MIME type of the block payload a drag carries next to its plain text; another editor surface or document tree reads it on drop. */\nexport const BLOCK_MIME = 'application/x-tip-block'\n\nconst TITLE_LENGTH = 80\n\n/** A block as it travels between editor surfaces and document trees. */\nexport interface BlockTransfer {\n /** Document the block lives in; `null` when its editor has none. */\n documentId: string | null\n blockId: string | null\n /**
|
|
1
|
+
{"version":3,"file":"block-transfer.js","names":[],"sources":["../../src/core/block-transfer.ts"],"sourcesContent":["import { type Editor, Extension, type JSONContent } from '@tiptap/core'\nimport { blockIdOf } from './schema/block-ids'\nimport { migratePersistedNames, TYPED_BLOCK } from './schema/persisted-names'\nimport { nodeAtPos } from './util'\n\n/** MIME type of the block payload a drag carries next to its plain text; another editor surface or document tree reads it on drop. */\nexport const BLOCK_MIME = 'application/x-tip-block'\n\nconst TITLE_LENGTH = 80\n\n/** A block as it travels between editor surfaces and document trees. */\nexport interface BlockTransfer {\n /** Document the block lives in; `null` when its editor has none. */\n documentId: string | null\n blockId: string | null\n /** Block type id of a Typed block; `null` for every other block. */\n blockType: string | null\n /** Typed block: its title text; other blocks: the first 80 characters of their text. */\n title: string\n json: JSONContent\n /** The block as Markdown; empty when the editor has no Markdown serializer. */\n markdown: string\n}\n\n/** The minimal slice of `DataTransfer` the payload needs (a plain object in tests). */\ntype TransferData = Pick<DataTransfer, 'setData' | 'getData' | 'types'>\n\n/** Writes `payload` under `BLOCK_MIME`. */\nexport function writeBlockTransfer(dataTransfer: Pick<DataTransfer, 'setData'>, payload: BlockTransfer): void {\n dataTransfer.setData(BLOCK_MIME, JSON.stringify(payload))\n}\n\nconst isStringOrNull = (value: unknown): value is string | null => value === null || typeof value === 'string'\n\nconst isJSONContent = (value: unknown): value is JSONContent => typeof value === 'object' && value !== null && !Array.isArray(value) && 'type' in value && typeof value.type === 'string'\n\n/**\n * The block payload of a drag, or `null` when it carries none or one that does not parse or has the wrong shape. A payload\n * written before the rename of Typed blocks (Block type id in `kind`, `json` with old node and attribute names) is read\n * as the current shape: `json` goes through `migratePersistedNames`, the Block type id is taken from `blockType` or `kind`.\n */\nexport function readBlockTransfer(dataTransfer: Partial<TransferData>): BlockTransfer | null {\n if (!dataTransfer.types || !Array.from(dataTransfer.types).includes(BLOCK_MIME)) return null\n let value: unknown\n try {\n value = JSON.parse(dataTransfer.getData?.(BLOCK_MIME) ?? '')\n }\n catch {\n return null\n }\n if (typeof value !== 'object' || value === null) return null\n const { documentId, blockId, blockType: current, kind: old, title, json, markdown } = value as Record<string, unknown>\n const blockType = current === undefined ? old : current\n if (!isStringOrNull(documentId) || !isStringOrNull(blockId) || !isStringOrNull(blockType) || typeof title !== 'string' || typeof markdown !== 'string') return null\n if (!isJSONContent(json)) return null\n return { documentId, blockId, blockType, title, json: migratePersistedNames(json), markdown }\n}\n\n/**\n * The payload of the block whose own position is `pos`; `null` when `pos` holds no block. The Markdown is empty when\n * `editor.markdown` is unavailable (a headless editor without `DocumentMarkdown`).\n */\nexport function blockTransferOf(editor: Editor, pos: number): BlockTransfer | null {\n const node = nodeAtPos(editor.state.doc, pos)\n if (!node || !node.isBlock) return null\n const json = node.toJSON() as JSONContent\n const typedBlock = node.type.name === TYPED_BLOCK\n const blockType: unknown = typedBlock ? node.attrs.blockType : null\n return {\n documentId: documentIdOf(editor),\n blockId: blockIdOf(node),\n blockType: typeof blockType === 'string' ? blockType : null,\n title: typedBlock ? (node.firstChild?.textContent ?? '') : node.textContent.slice(0, TITLE_LENGTH),\n json,\n markdown: editor.markdown?.serialize({ type: 'doc', content: [json] }) ?? '',\n }\n}\n\nexport interface BlockTransferStorageData {\n documentId: string | null\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n /** Absent in an editor without the `BlockTransferStorage` extension. */\n blockTransfer?: BlockTransferStorageData\n }\n}\n\n/** The id of `editor`'s document as the `blockTransfer` storage knows it; null when the editor has none. */\nexport const documentIdOf = (editor: Editor): string | null => editor.storage.blockTransfer?.documentId ?? null\n\n/** Carries the editor's document id so `blockTransferOf` can name where a block comes from (`editor.storage.blockTransfer`). */\nexport const BlockTransferStorage = Extension.create<{ documentId: string | null }, BlockTransferStorageData>({\n name: 'blockTransfer',\n addOptions: () => ({ documentId: null }),\n addStorage() {\n return { documentId: this.options.documentId }\n },\n})\n"],"mappings":";;;;;;AAMA,MAAa,aAAa;AAE1B,MAAM,eAAe;;AAoBrB,SAAgB,mBAAmB,cAA6C,SAA8B;CAC5G,aAAa,QAAQ,YAAY,KAAK,UAAU,OAAO,CAAC;AAC1D;AAEA,MAAM,kBAAkB,UAA2C,UAAU,QAAQ,OAAO,UAAU;AAEtG,MAAM,iBAAiB,UAAyC,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,KAAK,UAAU,SAAS,OAAO,MAAM,SAAS;;;;;;AAOjL,SAAgB,kBAAkB,cAA2D;CAC3F,IAAI,CAAC,aAAa,SAAS,CAAC,MAAM,KAAK,aAAa,KAAK,CAAC,CAAC,SAAA,yBAAmB,GAAG,OAAO;CACxF,IAAI;CACJ,IAAI;EACF,QAAQ,KAAK,MAAM,aAAa,UAAA,yBAAoB,KAAK,EAAE;CAC7D,QACM;EACJ,OAAO;CACT;CACA,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,EAAE,YAAY,SAAS,WAAW,SAAS,MAAM,KAAK,OAAO,MAAM,aAAa;CACtF,MAAM,YAAY,YAAY,KAAA,IAAY,MAAM;CAChD,IAAI,CAAC,eAAe,UAAU,KAAK,CAAC,eAAe,OAAO,KAAK,CAAC,eAAe,SAAS,KAAK,OAAO,UAAU,YAAY,OAAO,aAAa,UAAU,OAAO;CAC/J,IAAI,CAAC,cAAc,IAAI,GAAG,OAAO;CACjC,OAAO;EAAE;EAAY;EAAS;EAAW;EAAO,MAAM,sBAAsB,IAAI;EAAG;CAAS;AAC9F;;;;;AAMA,SAAgB,gBAAgB,QAAgB,KAAmC;CACjF,MAAM,OAAO,UAAU,OAAO,MAAM,KAAK,GAAG;CAC5C,IAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,OAAO;CACnC,MAAM,OAAO,KAAK,OAAO;CACzB,MAAM,aAAa,KAAK,KAAK,SAAS;CACtC,MAAM,YAAqB,aAAa,KAAK,MAAM,YAAY;CAC/D,OAAO;EACL,YAAY,aAAa,MAAM;EAC/B,SAAS,UAAU,IAAI;EACvB,WAAW,OAAO,cAAc,WAAW,YAAY;EACvD,OAAO,aAAc,KAAK,YAAY,eAAe,KAAM,KAAK,YAAY,MAAM,GAAG,YAAY;EACjG;EACA,UAAU,OAAO,UAAU,UAAU;GAAE,MAAM;GAAO,SAAS,CAAC,IAAI;EAAE,CAAC,KAAK;CAC5E;AACF;;AAcA,MAAa,gBAAgB,WAAkC,OAAO,QAAQ,eAAe,cAAc;;AAG3G,MAAa,uBAAuB,UAAU,OAAgE;CAC5G,MAAM;CACN,mBAAmB,EAAE,YAAY,KAAK;CACtC,aAAa;EACX,OAAO,EAAE,YAAY,KAAK,QAAQ,WAAW;CAC/C;AACF,CAAC"}
|