@ecsplico/editor 3.6.0 → 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 +65 -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/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/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 -8
- package/dist/features/composites/merge.js +23 -22
- 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/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 +7 -4
- 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 +6 -6
- 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 -89
- package/dist/features/composites/kinds.js +0 -285
- package/dist/features/composites/kinds.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdvancedEditor.js","names":[],"sources":["../../src/react/AdvancedEditor.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport { ComposedSurface, type ComposedEditorHandle, type ComposedEditorProps } from './composed-surface'\n\nexport type { ComposedEditorHandle as AdvancedEditorHandle }\n\n/**\n * Props of the advanced editor: the surface's own (collaboration session or standalone `value`/`onChange`, metadata,\n * abilities, extensions, block actions, commands, `documentId`, `editable`, `settingsUi`, the open state of the sidebar\n * and comments margin, `onReady` and the other callbacks) except the preset, plus the composition: `content`,\n * `interaction`, `ai`, `add`, `remove`, `backend` and the
|
|
1
|
+
{"version":3,"file":"AdvancedEditor.js","names":[],"sources":["../../src/react/AdvancedEditor.tsx"],"sourcesContent":["import { forwardRef } from 'react'\nimport { ComposedSurface, type ComposedEditorHandle, type ComposedEditorProps } from './composed-surface'\n\nexport type { ComposedEditorHandle as AdvancedEditorHandle }\n\n/**\n * Props of the advanced editor: the surface's own (collaboration session or standalone `value`/`onChange`, metadata,\n * abilities, extensions, block actions, commands, `documentId`, `editable`, `settingsUi`, the open state of the sidebar\n * and comments margin, `onReady` and the other callbacks) except the preset, plus the composition: `content`,\n * `interaction`, `ai`, `add`, `remove`, `backend` and the Block types. The defaults are the advanced editor's:\n * `content: 'rich'`, `interaction: 'full'`, `ai: true`.\n */\nexport type AdvancedEditorProps = ComposedEditorProps\n\n/**\n * The advanced editor on the React surface: `composeEditor({ content: 'rich', interaction: 'full', ai: true })` mounted\n * through `EditorSurface`; every dimension is overridable by its prop, and the backend (default: the editor context's)\n * prunes what it cannot serve. The ref's `preset` is the composed preset (`omitted` lists the dropped features). The\n * composition is fixed at mount like the Block types it is built from; hosts remount (`key`) to change it. Place\n * `EditorToolbar` and, with `settingsUi: 'external'`, `EditorSettingsModal`/`EditorSettingsPanel` beside it and give\n * them the editor from `onReady`; `useEditorUi(editor)` reaches the chrome of a ready editor. Collaboration is active\n * only when a `collaboration` session is passed; mount `Toaster` once for notices.\n */\nexport const AdvancedEditor = forwardRef<ComposedEditorHandle, AdvancedEditorProps>(function AdvancedEditor({ content = 'rich', interaction = 'full', ai = true, ...rest }, ref) {\n return <ComposedSurface ref={ref} content={content} interaction={interaction} ai={ai} {...rest} />\n})\n"],"mappings":";;;;;;;;;;;;;AAuBA,MAAa,iBAAiB,WAAsD,SAAS,eAAe,EAAE,UAAU,QAAQ,cAAc,QAAQ,KAAK,MAAM,GAAG,QAAQ,KAAK;CAC/K,OAAO,oBAAC,iBAAD;EAAsB;EAAc;EAAsB;EAAiB;EAAI,GAAI;CAAO,CAAA;AACnG,CAAC"}
|
package/dist/react/compose.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ type ReactComponent = ComponentType<{
|
|
|
12
12
|
export declare const composeEditor: (options: ComposeOptions<ReactComponent>) => ComposedEditor<ReactComponent>;
|
|
13
13
|
/**
|
|
14
14
|
* The advanced editor on the React surface: `{ content: 'rich', interaction: 'full', ai: true }`, every dimension option
|
|
15
|
-
* overridable (`
|
|
15
|
+
* overridable (`blockTypes`, `backend`, `add`, `remove`, ...), so the schema is the one of `advancedSchemaExtensions()`.
|
|
16
16
|
*/
|
|
17
17
|
export declare const advancedEditor: (options?: Partial<ComposeOptions<ReactComponent>>) => ComposedEditor<ReactComponent>;
|
|
18
18
|
//#endregion
|
package/dist/react/compose.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { coreUiFeature } from "../core/core-feature.js";
|
|
2
|
-
import { composeEditor as composeEditor$1 } from "../presets/compose.js";
|
|
3
2
|
import { EditorCommandPalette } from "./EditorCommandPalette.js";
|
|
4
3
|
import { aiFeature } from "../features/ai/react.js";
|
|
5
4
|
import { authorsFeature } from "../features/authors/react.js";
|
|
@@ -14,6 +13,7 @@ import { transcriptFeature } from "../features/transcript/react.js";
|
|
|
14
13
|
import { bulletsTransform, summaryTransform } from "../integrations/composite-ai/transfer-transforms.js";
|
|
15
14
|
import { compositeAiFeature } from "../integrations/composite-ai/index.js";
|
|
16
15
|
import { transcriptAiFeature } from "../integrations/transcript-ai/index.js";
|
|
16
|
+
import { composeEditor as composeEditor$1 } from "../presets/compose.js";
|
|
17
17
|
//#region src/react/compose.ts
|
|
18
18
|
/** The React bindings of every UI-bound catalog feature. */
|
|
19
19
|
const REACT_BINDINGS = {
|
|
@@ -39,7 +39,7 @@ const REACT_BINDINGS = {
|
|
|
39
39
|
const composeEditor = (options) => composeEditor$1(REACT_BINDINGS, options);
|
|
40
40
|
/**
|
|
41
41
|
* The advanced editor on the React surface: `{ content: 'rich', interaction: 'full', ai: true }`, every dimension option
|
|
42
|
-
* overridable (`
|
|
42
|
+
* overridable (`blockTypes`, `backend`, `add`, `remove`, ...), so the schema is the one of `advancedSchemaExtensions()`.
|
|
43
43
|
*/
|
|
44
44
|
const advancedEditor = (options = {}) => composeEditor({
|
|
45
45
|
content: "rich",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.js","names":["compose"],"sources":["../../src/react/compose.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { ComponentType } from 'react'\nimport { coreUiFeature } from '../core/core-feature'\nimport { EditorCommandPalette } from './EditorCommandPalette'\nimport { aiFeature } from '../features/ai/react'\nimport { authorsFeature } from '../features/authors/react'\nimport { blockUiFeature } from '../features/block-ui/react'\nimport { collabFeature } from '../features/collab/react'\nimport { commentsFeature } from '../features/comments/react'\nimport { compositeFeature } from '../features/composites/react'\nimport { mediaFeature } from '../features/media/react'\nimport { themesFeature } from '../features/themes/react'\nimport { transferFeature } from '../features/transfer/react'\nimport { transcriptFeature } from '../features/transcript/react'\nimport { bulletsTransform, compositeAiFeature, summaryTransform } from '../integrations/composite-ai'\nimport { transcriptAiFeature } from '../integrations/transcript-ai'\nimport { type ComposedEditor, type ComposeOptions, composeEditor as compose, type UiBindings } from '../presets/compose'\n\ntype ReactComponent = ComponentType<{ editor: Editor }>\n\n/** The React bindings of every UI-bound catalog feature. */\nconst REACT_BINDINGS = {\n 'core-ui': () => coreUiFeature<ReactComponent>({ palette: EditorCommandPalette }),\n 'media': mediaFeature,\n 'transcript': transcriptFeature,\n 'composites': compositeFeature,\n 'authors': authorsFeature,\n 'comments': commentsFeature,\n 'ai': aiFeature,\n 'composite-ai': compositeAiFeature,\n 'transcript-ai': transcriptAiFeature,\n 'collab': collabFeature,\n 'block-ui': blockUiFeature,\n 'themes': themesFeature,\n 'transfer': transferFeature,\n 'transfer-ai': () => [summaryTransform, bulletsTransform],\n} satisfies UiBindings<ReactComponent>\n\n/**\n * `composeEditor` of `presets/compose` over the React bindings: the editor of the three dimensions (`content`,\n * `interaction`, `ai`), pruned by the capabilities of `options.backend`.\n */\nexport const composeEditor = (options: ComposeOptions<ReactComponent>): ComposedEditor<ReactComponent> => compose(REACT_BINDINGS, options)\n\n/**\n * The advanced editor on the React surface: `{ content: 'rich', interaction: 'full', ai: true }`, every dimension option\n * overridable (`
|
|
1
|
+
{"version":3,"file":"compose.js","names":["compose"],"sources":["../../src/react/compose.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { ComponentType } from 'react'\nimport { coreUiFeature } from '../core/core-feature'\nimport { EditorCommandPalette } from './EditorCommandPalette'\nimport { aiFeature } from '../features/ai/react'\nimport { authorsFeature } from '../features/authors/react'\nimport { blockUiFeature } from '../features/block-ui/react'\nimport { collabFeature } from '../features/collab/react'\nimport { commentsFeature } from '../features/comments/react'\nimport { compositeFeature } from '../features/composites/react'\nimport { mediaFeature } from '../features/media/react'\nimport { themesFeature } from '../features/themes/react'\nimport { transferFeature } from '../features/transfer/react'\nimport { transcriptFeature } from '../features/transcript/react'\nimport { bulletsTransform, compositeAiFeature, summaryTransform } from '../integrations/composite-ai'\nimport { transcriptAiFeature } from '../integrations/transcript-ai'\nimport { type ComposedEditor, type ComposeOptions, composeEditor as compose, type UiBindings } from '../presets/compose'\n\ntype ReactComponent = ComponentType<{ editor: Editor }>\n\n/** The React bindings of every UI-bound catalog feature. */\nconst REACT_BINDINGS = {\n 'core-ui': () => coreUiFeature<ReactComponent>({ palette: EditorCommandPalette }),\n 'media': mediaFeature,\n 'transcript': transcriptFeature,\n 'composites': compositeFeature,\n 'authors': authorsFeature,\n 'comments': commentsFeature,\n 'ai': aiFeature,\n 'composite-ai': compositeAiFeature,\n 'transcript-ai': transcriptAiFeature,\n 'collab': collabFeature,\n 'block-ui': blockUiFeature,\n 'themes': themesFeature,\n 'transfer': transferFeature,\n 'transfer-ai': () => [summaryTransform, bulletsTransform],\n} satisfies UiBindings<ReactComponent>\n\n/**\n * `composeEditor` of `presets/compose` over the React bindings: the editor of the three dimensions (`content`,\n * `interaction`, `ai`), pruned by the capabilities of `options.backend`.\n */\nexport const composeEditor = (options: ComposeOptions<ReactComponent>): ComposedEditor<ReactComponent> => compose(REACT_BINDINGS, options)\n\n/**\n * The advanced editor on the React surface: `{ content: 'rich', interaction: 'full', ai: true }`, every dimension option\n * overridable (`blockTypes`, `backend`, `add`, `remove`, ...), so the schema is the one of `advancedSchemaExtensions()`.\n */\nexport const advancedEditor = (options: Partial<ComposeOptions<ReactComponent>> = {}): ComposedEditor<ReactComponent> => composeEditor({ content: 'rich', interaction: 'full', ai: true, ...options })\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,MAAM,iBAAiB;CACrB,iBAAiB,cAA8B,EAAE,SAAS,qBAAqB,CAAC;CAChF,SAAS;CACT,cAAc;CACd,cAAc;CACd,WAAW;CACX,YAAY;CACZ,MAAM;CACN,gBAAgB;CAChB,iBAAiB;CACjB,UAAU;CACV,YAAY;CACZ,UAAU;CACV,YAAY;CACZ,qBAAqB,CAAC,kBAAkB,gBAAgB;AAC1D;;;;;AAMA,MAAa,iBAAiB,YAA4EA,gBAAQ,gBAAgB,OAAO;;;;;AAMzI,MAAa,kBAAkB,UAAmD,CAAC,MAAsC,cAAc;CAAE,SAAS;CAAQ,aAAa;CAAQ,IAAI;CAAM,GAAG;AAAQ,CAAC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { EditorBackend } from "../core/backend.js";
|
|
2
2
|
import { BlockUiOptions } from "../core/extension-points.js";
|
|
3
|
+
import { Basket } from "../features/composites/baskets.js";
|
|
4
|
+
import { BlockType } from "../features/composites/block-types.js";
|
|
3
5
|
import { CoreContentOptions } from "../core/schema/index.js";
|
|
4
6
|
import { EditorSurfaceHandle, EditorSurfaceProps, ReactFeature } from "./EditorSurface.js";
|
|
5
|
-
import { CompositeKind } from "../features/composites/kinds.js";
|
|
6
|
-
import { Basket } from "../features/composites/baskets.js";
|
|
7
7
|
import { ComposedEditor, ContentLevel, FeatureId, InteractionLevel } from "../presets/compose.js";
|
|
8
8
|
import { Editor } from "@tiptap/core";
|
|
9
9
|
import { ComponentType } from "react";
|
|
@@ -35,9 +35,9 @@ export interface ComposedEditorProps extends Omit<EditorSurfaceProps, 'preset'>
|
|
|
35
35
|
remove?: readonly FeatureId[];
|
|
36
36
|
/** The backend the composition is pruned by (default: the editor context's backend). */
|
|
37
37
|
backend?: EditorBackend;
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
/** Colour baskets a
|
|
38
|
+
/** Block types when `content` includes composites (default: `defaultBlockTypes(t)`). */
|
|
39
|
+
blockTypes?: readonly BlockType[];
|
|
40
|
+
/** Colour baskets a Typed block can sit in when `content` includes composites (default: `defaultBaskets(t)`). */
|
|
41
41
|
compositeBaskets?: readonly Basket[];
|
|
42
42
|
/** Which surfaces render block actions: the hover bar (default), the selected-block toolbar, `'both'` or `'none'` (`BlockUiOptions`). */
|
|
43
43
|
blockUi?: BlockUiOptions;
|
|
@@ -26,7 +26,7 @@ const ReadySurface = forwardRef(function ReadySurface({ preset, ...surface }, re
|
|
|
26
26
|
* then nothing renders. Passing the host's own `collaboration` session keeps `collab` and `comments` whatever the
|
|
27
27
|
* backend offers.
|
|
28
28
|
*/
|
|
29
|
-
const ComposedSurface = forwardRef(function ComposedSurface({ content, interaction, ai, add, remove, backend,
|
|
29
|
+
const ComposedSurface = forwardRef(function ComposedSurface({ content, interaction, ai, add, remove, backend, blockTypes, compositeBaskets, blockUi, coreContent, ...surface }, ref) {
|
|
30
30
|
const app = useEditorContext();
|
|
31
31
|
const [source] = useState(() => backend ?? app.backend);
|
|
32
32
|
const [compose] = useState(() => {
|
|
@@ -35,7 +35,7 @@ const ComposedSurface = forwardRef(function ComposedSurface({ content, interacti
|
|
|
35
35
|
interaction,
|
|
36
36
|
ai,
|
|
37
37
|
collaboration: surface.collaboration !== void 0,
|
|
38
|
-
...
|
|
38
|
+
...blockTypes && { blockTypes },
|
|
39
39
|
...compositeBaskets && { compositeBaskets },
|
|
40
40
|
...blockUi && { blockUi },
|
|
41
41
|
...coreContent && { coreContent },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composed-surface.js","names":[],"sources":["../../src/react/composed-surface.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type ComponentType, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'\nimport type { EditorBackend } from '../core/backend'\nimport type { BlockUiOptions } from '../core/extension-points'\nimport type { Basket } from '../features/composites/baskets'\nimport type {
|
|
1
|
+
{"version":3,"file":"composed-surface.js","names":[],"sources":["../../src/react/composed-surface.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type ComponentType, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'\nimport type { EditorBackend } from '../core/backend'\nimport type { BlockUiOptions } from '../core/extension-points'\nimport type { Basket } from '../features/composites/baskets'\nimport type { BlockType } from '../features/composites/block-types'\nimport type { CoreContentOptions } from '../core/schema'\nimport type { ComposedEditor, ContentLevel, FeatureId, InteractionLevel } from '../presets/compose'\nimport { composeEditor } from './compose'\nimport { useEditorContext } from './context'\nimport { EditorSurface, type EditorSurfaceHandle, type EditorSurfaceProps, type ReactFeature } from './EditorSurface'\n\n/** The preset a composed editor mounts: the three dimensions' features, with `omitted` listing what the backend or `remove` dropped. */\nexport type ReactComposedPreset = ComposedEditor<ComponentType<{ editor: Editor }>>\n\n/** The handle of a composed editor: the surface's (`editor`, `openSettings`) plus the preset it composed. */\nexport interface ComposedEditorHandle extends EditorSurfaceHandle {\n readonly preset: ReactComposedPreset\n}\n\n/**\n * The surface's own props (collaboration session or standalone `value`/`onChange`, metadata, abilities, extensions,\n * block actions, commands, `documentId`, `editable`, `settingsUi`, the open state of the sidebar and comments margin,\n * `onReady` and the other callbacks) except the preset, plus the composition. Every composition prop is read once at\n * mount; hosts remount (`key`) to change one.\n */\nexport interface ComposedEditorProps extends Omit<EditorSurfaceProps, 'preset'> {\n /** Document model: a level or the content feature ids to install. */\n content?: ContentLevel | readonly FeatureId[]\n /** Editing UX: a level or the interaction feature ids; `[]` = no interaction (a viewer). */\n interaction?: InteractionLevel | readonly FeatureId[]\n /** Installs the AI feature and the integrations whose content is present. */\n ai?: boolean\n /** Host features, appended after the catalog. */\n add?: readonly ReactFeature[]\n /** Features (catalog or host) left out, with the features that require them. */\n remove?: readonly FeatureId[]\n /** The backend the composition is pruned by (default: the editor context's backend). */\n backend?: EditorBackend\n /** Block types when `content` includes composites (default: `defaultBlockTypes(t)`). */\n blockTypes?: readonly BlockType[]\n /** Colour baskets a Typed block can sit in when `content` includes composites (default: `defaultBaskets(t)`). */\n compositeBaskets?: readonly Basket[]\n /** Which surfaces render block actions: the hover bar (default), the selected-block toolbar, `'both'` or `'none'` (`BlockUiOptions`). */\n blockUi?: BlockUiOptions\n /** Optional structures the content base drops from the schema: headings or heading levels, lists, blockquote, code block, rule (`CoreContentOptions`). */\n coreContent?: CoreContentOptions\n}\n\n/** `EditorSurface` over an already composed preset; hands out the preset through the ref. */\nconst ReadySurface = forwardRef<ComposedEditorHandle, Omit<EditorSurfaceProps, 'preset'> & { preset: ReactComposedPreset }>(function ReadySurface({ preset, ...surface }, ref) {\n const surfaceRef = useRef<EditorSurfaceHandle>(null)\n useImperativeHandle(ref, () => ({\n get editor() { return surfaceRef.current?.editor ?? null },\n openSettings: tab => surfaceRef.current?.openSettings(tab),\n preset,\n }), [preset])\n return <EditorSurface ref={surfaceRef} preset={preset} {...surface} />\n})\n\n/**\n * `EditorSurface` over the preset `composeEditor` builds from the composition props, at mount. A backend that probes\n * its capabilities (`backend.ready`) is awaited first, so the composition is pruned by what it really offers; until\n * then nothing renders. Passing the host's own `collaboration` session keeps `collab` and `comments` whatever the\n * backend offers.\n */\nexport const ComposedSurface = forwardRef<ComposedEditorHandle, ComposedEditorProps & Required<Pick<ComposedEditorProps, 'content' | 'interaction' | 'ai'>>>(function ComposedSurface(\n { content, interaction, ai, add, remove, backend, blockTypes, compositeBaskets, blockUi, coreContent, ...surface },\n ref,\n) {\n const app = useEditorContext()\n // Read once at mount, like every composition prop, although the preset may only be built after `backend.ready`.\n const [source] = useState(() => backend ?? app.backend)\n const [compose] = useState(() => {\n const options = {\n content,\n interaction,\n ai,\n collaboration: surface.collaboration !== undefined,\n ...(blockTypes && { blockTypes }),\n ...(compositeBaskets && { compositeBaskets }),\n ...(blockUi && { blockUi }),\n ...(coreContent && { coreContent }),\n ...(add && { add }),\n ...(remove && { remove }),\n }\n return () => composeEditor({ ...options, backend: source })\n })\n const [preset, setPreset] = useState<ReactComposedPreset | undefined>(() => source.ready ? undefined : compose())\n useEffect(() => {\n if (preset || !source.ready) return\n let live = true\n void source.ready.then(() => { if (live) setPreset(compose()) })\n return () => { live = false }\n }, [preset, source, compose])\n return preset ? <ReadySurface ref={ref} preset={preset} {...surface} /> : null\n})\n"],"mappings":";;;;;;;AAkDA,MAAM,eAAe,WAAuG,SAAS,aAAa,EAAE,QAAQ,GAAG,WAAW,KAAK;CAC7K,MAAM,aAAa,OAA4B,IAAI;CACnD,oBAAoB,YAAY;EAC9B,IAAI,SAAS;GAAE,OAAO,WAAW,SAAS,UAAU;EAAK;EACzD,eAAc,QAAO,WAAW,SAAS,aAAa,GAAG;EACzD;CACF,IAAI,CAAC,MAAM,CAAC;CACZ,OAAO,oBAAC,eAAD;EAAe,KAAK;EAAoB;EAAQ,GAAI;CAAU,CAAA;AACvE,CAAC;;;;;;;AAQD,MAAa,kBAAkB,WAA8H,SAAS,gBACpK,EAAE,SAAS,aAAa,IAAI,KAAK,QAAQ,SAAS,YAAY,kBAAkB,SAAS,aAAa,GAAG,WACzG,KACA;CACA,MAAM,MAAM,iBAAiB;CAE7B,MAAM,CAAC,UAAU,eAAe,WAAW,IAAI,OAAO;CACtD,MAAM,CAAC,WAAW,eAAe;EAC/B,MAAM,UAAU;GACd;GACA;GACA;GACA,eAAe,QAAQ,kBAAkB,KAAA;GACzC,GAAI,cAAc,EAAE,WAAW;GAC/B,GAAI,oBAAoB,EAAE,iBAAiB;GAC3C,GAAI,WAAW,EAAE,QAAQ;GACzB,GAAI,eAAe,EAAE,YAAY;GACjC,GAAI,OAAO,EAAE,IAAI;GACjB,GAAI,UAAU,EAAE,OAAO;EACzB;EACA,aAAa,cAAc;GAAE,GAAG;GAAS,SAAS;EAAO,CAAC;CAC5D,CAAC;CACD,MAAM,CAAC,QAAQ,aAAa,eAAgD,OAAO,QAAQ,KAAA,IAAY,QAAQ,CAAC;CAChH,gBAAgB;EACd,IAAI,UAAU,CAAC,OAAO,OAAO;EAC7B,IAAI,OAAO;EACX,OAAY,MAAM,WAAW;GAAE,IAAI,MAAM,UAAU,QAAQ,CAAC;EAAE,CAAC;EAC/D,aAAa;GAAE,OAAO;EAAM;CAC9B,GAAG;EAAC;EAAQ;EAAQ;CAAO,CAAC;CAC5B,OAAO,SAAS,oBAAC,cAAD;EAAmB;EAAa;EAAQ,GAAI;CAAU,CAAA,IAAI;AAC5E,CAAC"}
|
package/dist/server/ai.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ModelRole } from "../settings/values.js";
|
|
2
2
|
import "../settings/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ModelPresetConfig, TranscriptionConfig } from "../config/index.js";
|
|
4
4
|
import { ModelProfile, ModelRef } from "../core/backend-types.js";
|
|
5
5
|
import { ChatMessage } from "../features/ai/protocol/generate.js";
|
|
6
6
|
import "../features/ai/protocol/index.js";
|
|
@@ -21,11 +21,11 @@ export type ChatTurnPart = {
|
|
|
21
21
|
/** Everything the AI endpoints need, passed explicitly: no runtime config, no globals. */
|
|
22
22
|
export interface AiModelsConfig {
|
|
23
23
|
/** Server-defined model presets, keyed by preset id. */
|
|
24
|
-
presets: Record<string,
|
|
24
|
+
presets: Record<string, ModelPresetConfig>;
|
|
25
25
|
/** Shared key of presets without their own. */
|
|
26
26
|
apiKey?: string;
|
|
27
|
-
/** Default preset per model
|
|
28
|
-
|
|
27
|
+
/** Default preset per model role, answered by `GET /api/ai/models`. */
|
|
28
|
+
roles?: Record<ModelRole, string>;
|
|
29
29
|
/** Speech-to-text for audio/video composites; absent = the transcription endpoint answers 501. */
|
|
30
30
|
transcription?: TranscriptionConfig;
|
|
31
31
|
/**
|
|
@@ -52,7 +52,7 @@ export interface AiModels {
|
|
|
52
52
|
* returned; the `listModels` hook supplies the list when it returns one (replacing the presets).
|
|
53
53
|
*/
|
|
54
54
|
listModels(user: object | null): Promise<ModelProfile[]>;
|
|
55
|
-
readonly
|
|
55
|
+
readonly roles: Record<ModelRole, string> | undefined;
|
|
56
56
|
readonly transcription: TranscriptionConfig | undefined;
|
|
57
57
|
streamChat: typeof streamChat;
|
|
58
58
|
streamChatTurn: typeof streamChatTurn;
|
|
@@ -83,5 +83,5 @@ export declare function streamChatTurn(opts: {
|
|
|
83
83
|
toolChoice?: ToolChoice<ToolSet>;
|
|
84
84
|
}): AsyncGenerator<ChatTurnPart>;
|
|
85
85
|
//#endregion
|
|
86
|
-
export type { ChatMessage,
|
|
86
|
+
export type { ChatMessage, ModelPresetConfig, TranscriptionConfig };
|
|
87
87
|
//# sourceMappingURL=ai.d.ts.map
|
package/dist/server/ai.js
CHANGED
package/dist/server/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.js","names":[],"sources":["../../src/server/ai.ts"],"sourcesContent":["import { createOpenAICompatible } from '@ai-sdk/openai-compatible'\nimport { APICallError, type LanguageModel, type ModelMessage, streamText, type ToolChoice, type ToolSet } from 'ai'\nimport type { ChatMessage, ModelProfile, ModelRef } from '../features/ai/protocol'\nimport type {
|
|
1
|
+
{"version":3,"file":"ai.js","names":[],"sources":["../../src/server/ai.ts"],"sourcesContent":["import { createOpenAICompatible } from '@ai-sdk/openai-compatible'\nimport { APICallError, type LanguageModel, type ModelMessage, streamText, type ToolChoice, type ToolSet } from 'ai'\nimport type { ChatMessage, ModelProfile, ModelRef } from '../features/ai/protocol'\nimport type { ModelRole } from '../settings'\nimport type { ModelPresetConfig, TranscriptionConfig } from '../config'\nimport { HttpError } from './errors'\n\nexport type { ChatMessage }\n\n/** A streamed text delta, or a finished tool call (`invalid`: unparsable input or unknown tool). */\nexport type ChatTurnPart =\n | { type: 'text', text: string }\n | { type: 'tool-call', toolCallId: string, toolName: string, input: unknown, invalid: boolean }\n\nexport type { ModelPresetConfig, TranscriptionConfig }\n\n/** Everything the AI endpoints need, passed explicitly: no runtime config, no globals. */\nexport interface AiModelsConfig {\n /** Server-defined model presets, keyed by preset id. */\n presets: Record<string, ModelPresetConfig>\n /** Shared key of presets without their own. */\n apiKey?: string\n /** Default preset per model role, answered by `GET /api/ai/models`. */\n roles?: Record<ModelRole, string>\n /** Speech-to-text for audio/video composites; absent = the transcription endpoint answers 501. */\n transcription?: TranscriptionConfig\n /**\n * The host's own language model for `ref` (`ModelProvider.languageModel`), consulted before the presets for every\n * model reference; `user` is what `authenticate` returned. `null` or nothing falls through to the presets.\n */\n languageModel?: (ref: ModelRef, user: object | null) => Promise<LanguageModel | null | undefined>\n /** The models listed for `user` instead of the presets (`ModelProvider.listModels`); `null` or nothing lists the presets. */\n listModels?: (user: object | null) => Promise<ModelProfile[] | null | undefined>\n /** Appended to the error of a preset without key, e.g. the environment variable names that set it. */\n missingKeyHint?: (presetId: string) => string\n}\n\nexport interface AiModels {\n /**\n * AI SDK language model for `ref` (an OpenAI-compatible chat completions endpoint), for direct `ai` calls. The\n * host's `languageModel(ref, user)` is asked first (`user`: what `authenticate` returned); a model it returns is\n * used as is, `null` (or no such hook) falls through to the configured presets. A preset uses its own key or the\n * shared `apiKey`; a user ref brings its own. Throws `HttpError` 400 for an unknown preset and 500 for a preset\n * without key, so routes can call it before streaming starts.\n */\n resolveModel(ref: ModelRef, user: object | null): Promise<LanguageModel>\n /**\n * The configured presets as the client may see them: without their keys. `user` is what the host's `authenticate`\n * returned; the `listModels` hook supplies the list when it returns one (replacing the presets).\n */\n listModels(user: object | null): Promise<ModelProfile[]>\n readonly roles: Record<ModelRole, string> | undefined\n readonly transcription: TranscriptionConfig | undefined\n streamChat: typeof streamChat\n streamChatTurn: typeof streamChatTurn\n}\n\n/** The AI model utilities over an explicit configuration. */\nexport function createAiModels(config: AiModelsConfig): AiModels {\n return {\n async resolveModel(ref, user) {\n const hosted = await config.languageModel?.(ref, user)\n if (hosted) return hosted\n if (!('preset' in ref)) return createOpenAICompatible({ name: 'tiptap-editor', baseURL: ref.endpoint, apiKey: ref.apiKey }).chatModel(ref.model)\n const preset = config.presets[ref.preset]\n if (!preset) throw new HttpError(400, 'Unbekanntes Modell-Preset')\n const apiKey = preset.apiKey || config.apiKey\n if (!apiKey) throw new HttpError(500, `API-Key für Preset „${ref.preset}“ fehlt${config.missingKeyHint?.(ref.preset) ?? ''}`)\n return createOpenAICompatible({ name: 'tiptap-editor', baseURL: preset.endpoint, apiKey }).chatModel(preset.model)\n },\n async listModels(user) {\n const listed = await config.listModels?.(user)\n if (listed) return listed\n return Object.entries(config.presets).map(([id, { label, endpoint, model }]) => ({ id, label, endpoint, model, source: 'preset' }))\n },\n roles: config.roles,\n transcription: config.transcription,\n streamChat,\n streamChatTurn,\n }\n}\n\nexport function upstreamError(error: unknown) {\n let detail: string\n if (APICallError.isInstance(error)) detail = [error.statusCode, error.responseBody || error.message].filter(Boolean).join(' ')\n else if (error instanceof Error) detail = error.message\n else detail = (error as { message?: string } | null)?.message ?? JSON.stringify(error)\n return new HttpError(502, `KI-Endpunkt: ${detail}`.slice(0, 500))\n}\n\n/**\n * Streams the text deltas of a chat completion (AI SDK `streamText`) on the resolved language model `model` (see\n * `resolveModel`). System messages may appear in `messages`; a trailing assistant message is a prefill the model\n * continues. Upstream failures throw a 502 `HttpError`; aborting `signal` cancels the upstream request and makes the\n * generator throw.\n */\nexport async function* streamChat(opts: { model: LanguageModel, messages: ChatMessage[], signal: AbortSignal }): AsyncGenerator<string> {\n for await (const part of streamChatTurn(opts)) {\n if (part.type === 'text') yield part.text\n }\n}\n\n/**\n * Like `streamChat`, with `tools` the model may call (tools without `execute`: the turn ends with the calls, which are\n * yielded for the caller to handle) and structured `messages` (tool calls and results in the history).\n */\nexport async function* streamChatTurn(opts: {\n model: LanguageModel\n messages: ModelMessage[]\n signal: AbortSignal\n tools?: ToolSet\n toolChoice?: ToolChoice<ToolSet>\n}): AsyncGenerator<ChatTurnPart> {\n const { model } = opts\n // Also cancels the upstream request when the consumer stops iterating early (break / return).\n const upstream = new AbortController()\n const onAbort = () => upstream.abort(opts.signal.reason)\n if (opts.signal.aborted) onAbort()\n else opts.signal.addEventListener('abort', onAbort, { once: true })\n try {\n const result = streamText({\n model,\n messages: opts.messages,\n tools: opts.tools,\n toolChoice: opts.toolChoice,\n allowSystemInMessages: true,\n abortSignal: upstream.signal,\n maxRetries: 0,\n // Errors surface as `error` parts below and are rethrown; the default handler would only log them.\n onError: () => {},\n })\n for await (const part of result.stream) {\n if (part.type === 'text-delta') {\n if (part.text) yield { type: 'text', text: part.text }\n }\n else if (part.type === 'tool-call') {\n yield { type: 'tool-call', toolCallId: part.toolCallId, toolName: part.toolName, input: part.input, invalid: part.invalid === true }\n }\n else if (part.type === 'error') {\n if (opts.signal.aborted) break\n throw upstreamError(part.error)\n }\n else if (part.type === 'abort') {\n break\n }\n }\n opts.signal.throwIfAborted()\n }\n finally {\n opts.signal.removeEventListener('abort', onAbort)\n upstream.abort()\n }\n}\n"],"mappings":";;;;;AA0DA,SAAgB,eAAe,QAAkC;CAC/D,OAAO;EACL,MAAM,aAAa,KAAK,MAAM;GAC5B,MAAM,SAAS,MAAM,OAAO,gBAAgB,KAAK,IAAI;GACrD,IAAI,QAAQ,OAAO;GACnB,IAAI,EAAE,YAAY,MAAM,OAAO,uBAAuB;IAAE,MAAM;IAAiB,SAAS,IAAI;IAAU,QAAQ,IAAI;GAAO,CAAC,CAAC,CAAC,UAAU,IAAI,KAAK;GAC/I,MAAM,SAAS,OAAO,QAAQ,IAAI;GAClC,IAAI,CAAC,QAAQ,MAAM,IAAI,UAAU,KAAK,2BAA2B;GACjE,MAAM,SAAS,OAAO,UAAU,OAAO;GACvC,IAAI,CAAC,QAAQ,MAAM,IAAI,UAAU,KAAK,uBAAuB,IAAI,OAAO,SAAS,OAAO,iBAAiB,IAAI,MAAM,KAAK,IAAI;GAC5H,OAAO,uBAAuB;IAAE,MAAM;IAAiB,SAAS,OAAO;IAAU;GAAO,CAAC,CAAC,CAAC,UAAU,OAAO,KAAK;EACnH;EACA,MAAM,WAAW,MAAM;GACrB,MAAM,SAAS,MAAM,OAAO,aAAa,IAAI;GAC7C,IAAI,QAAQ,OAAO;GACnB,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,UAAU,cAAc;IAAE;IAAI;IAAO;IAAU;IAAO,QAAQ;GAAS,EAAE;EACpI;EACA,OAAO,OAAO;EACd,eAAe,OAAO;EACtB;EACA;CACF;AACF;AAEA,SAAgB,cAAc,OAAgB;CAC5C,IAAI;CACJ,IAAI,aAAa,WAAW,KAAK,GAAG,SAAS,CAAC,MAAM,YAAY,MAAM,gBAAgB,MAAM,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;MACxH,IAAI,iBAAiB,OAAO,SAAS,MAAM;MAC3C,SAAU,OAAuC,WAAW,KAAK,UAAU,KAAK;CACrF,OAAO,IAAI,UAAU,KAAK,gBAAgB,SAAS,MAAM,GAAG,GAAG,CAAC;AAClE;;;;;;;AAQA,gBAAuB,WAAW,MAAsG;CACtI,WAAW,MAAM,QAAQ,eAAe,IAAI,GAC1C,IAAI,KAAK,SAAS,QAAQ,MAAM,KAAK;AAEzC;;;;;AAMA,gBAAuB,eAAe,MAML;CAC/B,MAAM,EAAE,UAAU;CAElB,MAAM,WAAW,IAAI,gBAAgB;CACrC,MAAM,gBAAgB,SAAS,MAAM,KAAK,OAAO,MAAM;CACvD,IAAI,KAAK,OAAO,SAAS,QAAQ;MAC5B,KAAK,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAClE,IAAI;EACF,MAAM,SAAS,WAAW;GACxB;GACA,UAAU,KAAK;GACf,OAAO,KAAK;GACZ,YAAY,KAAK;GACjB,uBAAuB;GACvB,aAAa,SAAS;GACtB,YAAY;GAEZ,eAAe,CAAC;EAClB,CAAC;EACD,WAAW,MAAM,QAAQ,OAAO,QAC9B,IAAI,KAAK,SAAS,cACZ;OAAA,KAAK,MAAM,MAAM;IAAE,MAAM;IAAQ,MAAM,KAAK;GAAK;EAAA,OAElD,IAAI,KAAK,SAAS,aACrB,MAAM;GAAE,MAAM;GAAa,YAAY,KAAK;GAAY,UAAU,KAAK;GAAU,OAAO,KAAK;GAAO,SAAS,KAAK,YAAY;EAAK;OAEhI,IAAI,KAAK,SAAS,SAAS;GAC9B,IAAI,KAAK,OAAO,SAAS;GACzB,MAAM,cAAc,KAAK,KAAK;EAChC,OACK,IAAI,KAAK,SAAS,SACrB;EAGJ,KAAK,OAAO,eAAe;CAC7B,UACQ;EACN,KAAK,OAAO,oBAAoB,SAAS,OAAO;EAChD,SAAS,MAAM;CACjB;AACF"}
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/server/auth.d.ts
|
|
2
|
-
/** What `
|
|
2
|
+
/** What `AccessControl.authenticate` gets: the headers of the request (the websocket upgrade for collaboration) and the connection's `token`. */
|
|
3
3
|
export interface AuthenticateRequest {
|
|
4
4
|
headers: Headers;
|
|
5
5
|
/** The collaboration client's token (`HocuspocusProvider` `token`, sent right after the socket opened); absent for plain HTTP requests and token-less clients. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/server/collab-state.d.ts
|
|
2
2
|
export interface CollabState {
|
|
3
|
-
/** `
|
|
3
|
+
/** `DocumentContentStore.contentVersion` of the content the update was stored with; `null` = none known (also: legacy payload). */
|
|
4
4
|
version: string | null;
|
|
5
5
|
update: Uint8Array;
|
|
6
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collab-state.js","names":[],"sources":["../../src/server/collab-state.ts"],"sourcesContent":["// Stored Yjs state payload and the re-seed decision of the collab server. Pure: no Hocuspocus, no adapter, no Yjs.\n//\n// The adapter stores opaque bytes (`loadState`/`storeState` keep their signature); the server prefixes the Yjs update\n// with the content version it was written against:\n//\n// [0xFF, 0x76] marker (cannot start a legacy payload: a Yjs update opens with the varuint number of client groups,\n// and two continuation bytes would mean 16384 or more clients)\n// utf8 version empty = `null`\n// 0x00 terminator, so a version never contains NUL\n// update bytes `Y.encodeStateAsUpdate`\n//\n// Payloads without the marker are legacy raw updates and decode as `version: null`.\n\nconst MARKER = [0xFF, 0x76] as const\nconst TERMINATOR = 0\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nexport interface CollabState {\n /** `
|
|
1
|
+
{"version":3,"file":"collab-state.js","names":[],"sources":["../../src/server/collab-state.ts"],"sourcesContent":["// Stored Yjs state payload and the re-seed decision of the collab server. Pure: no Hocuspocus, no adapter, no Yjs.\n//\n// The adapter stores opaque bytes (`loadState`/`storeState` keep their signature); the server prefixes the Yjs update\n// with the content version it was written against:\n//\n// [0xFF, 0x76] marker (cannot start a legacy payload: a Yjs update opens with the varuint number of client groups,\n// and two continuation bytes would mean 16384 or more clients)\n// utf8 version empty = `null`\n// 0x00 terminator, so a version never contains NUL\n// update bytes `Y.encodeStateAsUpdate`\n//\n// Payloads without the marker are legacy raw updates and decode as `version: null`.\n\nconst MARKER = [0xFF, 0x76] as const\nconst TERMINATOR = 0\n\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder()\n\nexport interface CollabState {\n /** `DocumentContentStore.contentVersion` of the content the update was stored with; `null` = none known (also: legacy payload). */\n version: string | null\n update: Uint8Array\n}\n\nexport function encodeCollabState({ version, update }: CollabState): Uint8Array {\n const label = encoder.encode(version ?? '')\n if (label.includes(TERMINATOR)) throw new Error('tiptap-editor: contentVersion darf kein NUL-Zeichen enthalten')\n const out = new Uint8Array(MARKER.length + label.length + 1 + update.length)\n out.set(MARKER, 0)\n out.set(label, MARKER.length)\n out[MARKER.length + label.length] = TERMINATOR\n out.set(update, MARKER.length + label.length + 1)\n return out\n}\n\nexport function decodeCollabState(payload: Uint8Array): CollabState {\n if (payload[0] !== MARKER[0] || payload[1] !== MARKER[1]) return { version: null, update: payload }\n const end = payload.indexOf(TERMINATOR, MARKER.length)\n // A marker without terminator is no payload this module wrote: treat it as an opaque legacy update.\n if (end === -1) return { version: null, update: payload }\n const label = decoder.decode(payload.subarray(MARKER.length, end))\n return { version: label === '' ? null : label, update: payload.subarray(end + 1) }\n}\n\n/**\n * Whether stored collaborative state is stale against the content behind it. Only hosts that implement\n * `contentVersion` opt in (`live === undefined`: the adapter has no such method, state is never discarded). A legacy\n * payload (`stored: null`) against a known live version counts as stale: it cannot prove it matches the content.\n */\nexport function isStateStale(stored: string | null, live: string | null | undefined): boolean {\n return live !== undefined && stored !== live\n}\n"],"mappings":";AAaA,MAAM,SAAS,CAAC,KAAM,GAAI;AAC1B,MAAM,aAAa;AAEnB,MAAM,UAAU,IAAI,YAAY;AAChC,MAAM,UAAU,IAAI,YAAY;AAQhC,SAAgB,kBAAkB,EAAE,SAAS,UAAmC;CAC9E,MAAM,QAAQ,QAAQ,OAAO,WAAW,EAAE;CAC1C,IAAI,MAAM,SAAS,UAAU,GAAG,MAAM,IAAI,MAAM,+DAA+D;CAC/G,MAAM,MAAM,IAAI,WAAW,OAAO,SAAS,MAAM,SAAS,IAAI,OAAO,MAAM;CAC3E,IAAI,IAAI,QAAQ,CAAC;CACjB,IAAI,IAAI,OAAO,OAAO,MAAM;CAC5B,IAAI,OAAO,SAAS,MAAM,UAAU;CACpC,IAAI,IAAI,QAAQ,OAAO,SAAS,MAAM,SAAS,CAAC;CAChD,OAAO;AACT;AAEA,SAAgB,kBAAkB,SAAkC;CAClE,IAAI,QAAQ,OAAO,OAAO,MAAM,QAAQ,OAAO,OAAO,IAAI,OAAO;EAAE,SAAS;EAAM,QAAQ;CAAQ;CAClG,MAAM,MAAM,QAAQ,QAAQ,YAAY,OAAO,MAAM;CAErD,IAAI,QAAQ,IAAI,OAAO;EAAE,SAAS;EAAM,QAAQ;CAAQ;CACxD,MAAM,QAAQ,QAAQ,OAAO,QAAQ,SAAS,OAAO,QAAQ,GAAG,CAAC;CACjE,OAAO;EAAE,SAAS,UAAU,KAAK,OAAO;EAAO,QAAQ,QAAQ,SAAS,MAAM,CAAC;CAAE;AACnF;;;;;;AAOA,SAAgB,aAAa,QAAuB,MAA0C;CAC5F,OAAO,SAAS,KAAA,KAAa,WAAW;AAC1C"}
|
package/dist/server/collab.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AccessControl, DocumentContentStore, EditorPorts } from "./ports.js";
|
|
2
2
|
import { AnyExtension, JSONContent } from "@tiptap/core";
|
|
3
3
|
import { Schema } from "@tiptap/pm/model";
|
|
4
4
|
import { Configuration, Hocuspocus, WebSocketLike } from "@hocuspocus/server";
|
|
@@ -6,14 +6,14 @@ import { Configuration, Hocuspocus, WebSocketLike } from "@hocuspocus/server";
|
|
|
6
6
|
/** Y.XmlFragment the TipTap Collaboration extension binds to (its default `field`). */
|
|
7
7
|
export declare const COLLAB_FIELD = "default";
|
|
8
8
|
export interface CollabServerOptions {
|
|
9
|
-
/**
|
|
9
|
+
/** Access control, documents and Yjs state of the editor backend; `collabState` is required. */
|
|
10
10
|
ports: EditorPorts;
|
|
11
11
|
/** TipTap extensions of the collaboration schema; default `advancedSchemaExtensions()`. Host-defined nodes and marks: `() => [...advancedSchemaExtensions(), HostNode]` (Vue-free node files only, no node views). */
|
|
12
12
|
schema?: () => AnyExtension[];
|
|
13
13
|
/** Per-document access and content versioning; each defaults to the port's method of the same name. */
|
|
14
14
|
hooks?: {
|
|
15
|
-
authorize?: NonNullable<
|
|
16
|
-
contentVersion?: NonNullable<
|
|
15
|
+
authorize?: NonNullable<AccessControl['authorize']>;
|
|
16
|
+
contentVersion?: NonNullable<DocumentContentStore['contentVersion']>;
|
|
17
17
|
};
|
|
18
18
|
/** Further Hocuspocus configuration (`debounce`, `timeout`, `quiet`, …); the editor's hooks win over equally named ones. */
|
|
19
19
|
configuration?: Partial<Configuration>;
|
package/dist/server/collab.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assignBlockIds } from "../core/schema/block-ids.js";
|
|
2
|
+
import { isOldPersistedNode } from "../core/schema/persisted-names.js";
|
|
2
3
|
import { EMPTY_EDITOR_CONTENT } from "../core/schema/index.js";
|
|
3
|
-
import { migrateLegacyContent } from "../features/composites/legacy.js";
|
|
4
|
+
import { hasFlatTranscript, migrateLegacyContent } from "../features/composites/legacy.js";
|
|
4
5
|
import { advancedSchemaExtensions } from "../schema/advanced.js";
|
|
5
6
|
import { decodeCollabState, encodeCollabState, isStateStale } from "./collab-state.js";
|
|
6
7
|
import { getSchema } from "@tiptap/core";
|
|
@@ -17,19 +18,19 @@ const UNAUTHORIZED = {
|
|
|
17
18
|
};
|
|
18
19
|
/** Context key under which `handleConnection` hands the connection's socket closer to the `onAuthenticate` hook. */
|
|
19
20
|
const REJECT = Symbol("collab.reject");
|
|
20
|
-
/** ProseMirror document of stored JSON content: legacy nodes migrated, empty content replaced by the empty document, block IDs assigned. */
|
|
21
|
+
/** ProseMirror document of stored JSON content: old persisted names and legacy nodes migrated, empty content replaced by the empty document, block IDs assigned. */
|
|
21
22
|
function contentNode(schema, loaded) {
|
|
22
23
|
const content = migrateLegacyContent(loaded);
|
|
23
24
|
const node = schema.nodeFromJSON(content.type === "doc" && content.content?.length ? content : EMPTY_EDITOR_CONTENT);
|
|
24
25
|
return assignBlockIds(node) ?? node;
|
|
25
26
|
}
|
|
26
|
-
/** Whether
|
|
27
|
-
function
|
|
28
|
-
return !fragment.createTreeWalker((node) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
/** Whether the stored Yjs content still uses an old persisted name (the names listed in `persisted-names.ts`); the current schema does not know those elements. */
|
|
28
|
+
function hasOldPersistedNames(fragment) {
|
|
29
|
+
return !fragment.createTreeWalker((node) => node instanceof Y.XmlElement && isOldPersistedNode(node.nodeName, node.getAttributes(), { earliest: true })).next().done;
|
|
30
|
+
}
|
|
31
|
+
/** Whether a Typed block of the stored Yjs content still carries the flat `meta.transcript` that `migrateLegacyContent` turns into a `rendition` child; its Block type does not matter. */
|
|
32
|
+
function hasFlatTranscriptBlock(fragment) {
|
|
33
|
+
return !fragment.createTreeWalker((node) => node instanceof Y.XmlElement && node.nodeName === "typedBlock" && hasFlatTranscript(node.getAttribute("meta"))).next().done;
|
|
33
34
|
}
|
|
34
35
|
/** Hocuspocus answers a thrown error's `reason` as permission-denied. */
|
|
35
36
|
const denied = (message, reason) => Object.assign(new Error(message), { reason });
|
|
@@ -110,16 +111,20 @@ function createCollabServer(options) {
|
|
|
110
111
|
const doc = new Y.Doc();
|
|
111
112
|
Y.applyUpdate(doc, stored);
|
|
112
113
|
const fragment = doc.getXmlFragment(COLLAB_FIELD);
|
|
113
|
-
if (
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (withIds) {
|
|
118
|
-
prosemirrorToYXmlFragment(withIds, fragment);
|
|
119
|
-
await persist(doc);
|
|
120
|
-
}
|
|
114
|
+
if (hasOldPersistedNames(fragment)) {
|
|
115
|
+
const migrated = contentNode(currentSchema, yDocToProsemirrorJSON(doc, COLLAB_FIELD));
|
|
116
|
+
prosemirrorToYXmlFragment(migrated, fragment);
|
|
117
|
+
await persist(doc);
|
|
121
118
|
return doc;
|
|
122
119
|
}
|
|
120
|
+
const current = yXmlFragmentToProsemirror(currentSchema, fragment);
|
|
121
|
+
const migrated = hasFlatTranscriptBlock(fragment) ? currentSchema.nodeFromJSON(migrateLegacyContent(current.toJSON())) : current;
|
|
122
|
+
const withIds = assignBlockIds(migrated) ?? (migrated === current ? null : migrated);
|
|
123
|
+
if (withIds) {
|
|
124
|
+
prosemirrorToYXmlFragment(withIds, fragment);
|
|
125
|
+
await persist(doc);
|
|
126
|
+
}
|
|
127
|
+
return doc;
|
|
123
128
|
}
|
|
124
129
|
const loaded = await documents.loadContent(documentName);
|
|
125
130
|
if (!loaded) throw new Error("Dokument nicht gefunden");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collab.js","names":[],"sources":["../../src/server/collab.ts"],"sourcesContent":["import { type Configuration, Hocuspocus, type WebSocketLike } from '@hocuspocus/server'\nimport { type AnyExtension, getSchema, type JSONContent } from '@tiptap/core'\nimport type { Schema } from '@tiptap/pm/model'\nimport { prosemirrorToYDoc, prosemirrorToYXmlFragment, yDocToProsemirrorJSON, yXmlFragmentToProsemirror } from '@tiptap/y-tiptap'\nimport * as Y from 'yjs'\nimport { assignBlockIds, EMPTY_EDITOR_CONTENT } from '../core/schema'\nimport { LEGACY_COMPOSITE_NODE, migrateLegacyContent } from '../features/composites/schema'\nimport { advancedSchemaExtensions } from '../schema/advanced'\nimport { decodeCollabState, encodeCollabState, isStateStale } from './collab-state'\nimport type { CollabStateStore, DocumentStore, EditorPorts, Identity } from './ports'\n\n/** Y.XmlFragment the TipTap Collaboration extension binds to (its default `field`). */\nexport const COLLAB_FIELD = 'default'\n\n/** Close code of a connection whose client could not be authenticated (Hocuspocus' `Unauthorized`, HTTP 401 in websocket terms). */\nconst UNAUTHORIZED = { code: 4401, reason: 'Unauthorized' }\n\n/** Context key under which `handleConnection` hands the connection's socket closer to the `onAuthenticate` hook. */\nconst REJECT = Symbol('collab.reject')\n\nexport interface CollabServerOptions {\n /** Identity, documents and Yjs state of the editor backend; `collabState` is required. */\n ports: EditorPorts\n /** TipTap extensions of the collaboration schema; default `advancedSchemaExtensions()`. Host-defined nodes and marks: `() => [...advancedSchemaExtensions(), HostNode]` (Vue-free node files only, no node views). */\n schema?: () => AnyExtension[]\n /** Per-document access and content versioning; each defaults to the port's method of the same name. */\n hooks?: {\n authorize?: NonNullable<Identity['authorize']>\n contentVersion?: NonNullable<DocumentStore['contentVersion']>\n }\n /** Further Hocuspocus configuration (`debounce`, `timeout`, `quiet`, …); the editor's hooks win over equally named ones. */\n configuration?: Partial<Configuration>\n}\n\n/** One served websocket connection: feed it the socket's messages and its close event. */\nexport interface CollabConnection {\n handleMessage(data: Uint8Array): void\n handleClose(event?: CloseEvent): void\n}\n\nexport interface CollabServer {\n /**\n * The Hocuspocus instance of this server. The Yjs state (via the `collabState` port's `loadState`/`storeState`) is the\n * source of truth for content, the host's JSON content (via the `documents` port) is a mirror kept up to date on every store.\n */\n hocuspocus: Hocuspocus\n /** Schema of the collab server: `options.schema()` (default `advancedSchemaExtensions()`), built on first use. */\n schema(): Schema\n /**\n * Serves one websocket connection (any transport: crossws, `ws`, Bun, Deno). The user is authenticated in Hocuspocus'\n * `onAuthenticate` from the upgrade request's headers and the client's token, unless the transport already knows it\n * and passes it as `context.user`. Wire `handleMessage` and `handleClose` of the returned connection to the socket's events.\n */\n handleConnection(socket: WebSocketLike, request: Request, context?: { user?: object }): CollabConnection\n /** Tell connected clients the document is gone, disconnect them and drop the Yjs state. Call before removing the JSON. */\n forgetDocument(id: string): Promise<void>\n /** Current content of a document that is open in Hocuspocus (unsaved edits included), `null` when nobody has it open. */\n liveContent(id: string): JSONContent | null\n /**\n * The host rewrote the content behind the editor's back (an agent wrote the file): merge it into the open document now,\n * as a diff-update connected clients see as an ordinary edit. Without this the merge waits for the document's next store.\n * No-op when nobody has the document open or the content version is unchanged; needs the documents port's `contentVersion`.\n */\n contentChanged(id: string): Promise<void>\n /** Open collaboration connections and loaded documents of this server. */\n stats(): { connections: number, documents: number }\n /** Disconnect every client, write pending stores and wait until all documents are unloaded. The server accepts no connection afterwards. */\n close(): Promise<void>\n}\n\n/** ProseMirror document of stored JSON content: legacy nodes migrated, empty content replaced by the empty document, block IDs assigned. */\nfunction contentNode(schema: Schema, loaded: JSONContent) {\n const content = migrateLegacyContent(loaded)\n const node = schema.nodeFromJSON(content.type === 'doc' && content.content?.length ? content : EMPTY_EDITOR_CONTENT)\n return assignBlockIds(node) ?? node\n}\n\n/** Whether a `document` Baustein of the stored Yjs content still carries the flat `meta.transcript` that `migrateLegacyContent` turns into a `rendition` child. */\nfunction hasFlatTranscript(fragment: Y.XmlFragment): boolean {\n return !fragment.createTreeWalker((node) => {\n if (!(node instanceof Y.XmlElement) || node.nodeName !== 'composite' || node.getAttribute('kind') !== 'document') return false\n const meta: unknown = node.getAttribute('meta')\n return typeof meta === 'object' && meta !== null && 'transcript' in meta && typeof meta.transcript === 'string'\n }).next().done\n}\n\n/** Hocuspocus answers a thrown error's `reason` as permission-denied. */\nconst denied = (message: string, reason: string) => Object.assign(new Error(message), { reason })\n\n/** The `collabState` port; the collaboration server cannot run without it. */\nfunction collabStateOf(ports: EditorPorts): CollabStateStore {\n if (!ports.collabState) throw new Error('@ecsplico/editor: collaboration needs the collabState port')\n return ports.collabState\n}\n\n/**\n * The editor's collaboration server, independent of any web framework: Hocuspocus with the editor's hooks (seeding from\n * the stored JSON, versioned state, block-ID backfill, debounced `storeContent`/`storeState`) over `options.ports`.\n * `/nuxt` mounts one in Nitro; any Node/Bun/Deno process can serve `handleConnection` on its own websocket transport.\n */\nexport function createCollabServer(options: CollabServerOptions): CollabServer {\n const { identity, documents } = options.ports\n const state = collabStateOf(options.ports)\n const authorize = (user: object, id: string) => options.hooks?.authorize ? options.hooks.authorize(user, id) : identity.authorize?.(user, id)\n const contentVersion = (id: string) => options.hooks?.contentVersion ? options.hooks.contentVersion(id) : documents.contentVersion?.(id)\n\n let schema: Schema | undefined\n const collabSchema = () => schema ??= getSchema((options.schema ?? advancedSchemaExtensions)())\n\n /** Loaded documents deleted via `forgetDocument`; a store still pending for them must not write their Yjs state back. */\n const forgotten = new Set<string>()\n /** `contentVersion` the loaded document's content mirror had when it was last read or written; only tracked for documents ports with `contentVersion`. */\n const loadedVersions = new Map<string, string | null>()\n /** Resolvers of `close()` waiting for the last document to unload. */\n const idle: (() => void)[] = []\n let closed = false\n\n /** Merges content rewritten behind the editor's back into the open document and stores its state against `version`. */\n async function mergeRewrite(id: string, document: Y.Doc, version: string | null): Promise<void> {\n const rewritten = await documents.loadContent(id)\n if (!rewritten) return // removed meanwhile: nothing to merge, nothing to mirror\n // A diff-update: unchanged blocks keep their Yjs items, so connected clients see an ordinary edit and need no reload.\n prosemirrorToYXmlFragment(contentNode(collabSchema(), rewritten), document.getXmlFragment(COLLAB_FIELD))\n loadedVersions.set(id, version)\n await state.storeState(id, encodeCollabState({ version, update: Y.encodeStateAsUpdate(document) }))\n }\n\n /** Stored state of a document, `null` when there is none usable: it was written against another content version and is removed. */\n async function loadCurrentState(id: string, liveVersion: string | null | undefined): Promise<Uint8Array | null> {\n const payload = await state.loadState(id)\n if (!payload) return null\n const { version, update } = decodeCollabState(payload)\n if (isStateStale(version, liveVersion)) {\n await state.removeState(id)\n return null\n }\n return update\n }\n\n const hocuspocus = new Hocuspocus({\n ...options.configuration,\n async onAuthenticate({ context, token, requestHeaders, documentName, connectionConfig }) {\n // The transport may know the user already; otherwise it comes from the upgrade request's headers or the client's token.\n let user = context.user as object | null | undefined\n if (!user) user = await identity.authenticate({ headers: requestHeaders, ...(token && { token }) })\n if (!user) {\n // After the permission-denied answer Hocuspocus sends: close the socket, so a client without credentials does not linger.\n const socketCloser = context[REJECT] as (() => void) | undefined\n socketCloser?.()\n throw denied('Unauthorized', 'unauthorized')\n }\n const access = await authorize(user, documentName)\n // `undefined`: no authorize hook, every authenticated user may edit. `null`: rejected; the thrown error's `reason` is answered as permission-denied.\n if (access === null) throw denied('Forbidden', 'forbidden')\n if (access?.readOnly) connectionConfig.readOnly = true\n return { user }\n },\n async onLoadDocument({ documentName }) {\n const currentSchema = collabSchema()\n // Read before the content: a change in between leaves an older version on the state, which only costs a re-seed.\n const version = await contentVersion(documentName)\n const stored = await loadCurrentState(documentName, version)\n if (version !== undefined) loadedVersions.set(documentName, version)\n const persist = (doc: Y.Doc) => state.storeState(documentName, encodeCollabState({ version: version ?? null, update: Y.encodeStateAsUpdate(doc) }))\n if (stored) {\n // Decoded into a scratch doc: Hocuspocus only gets it once it is known to be current.\n const doc = new Y.Doc()\n Y.applyUpdate(doc, stored)\n const fragment = doc.getXmlFragment(COLLAB_FIELD)\n // State from before the composite family holds node names the schema no longer knows (any depth): drop it and\n // reseed below from the migrated JSON mirror, which also rewrites the stored state.\n const legacy = !fragment.createTreeWalker(node => node instanceof Y.XmlElement && node.nodeName === LEGACY_COMPOSITE_NODE).next().done\n if (!legacy) {\n // Blocks without an ID (state from before block IDs) and a `document` Baustein's flat `meta.transcript` (it\n // became a `rendition` child): migrate here, before any client syncs, so clients never backfill concurrently.\n // The diff-update keeps the Yjs items of everything that did not change.\n const current = yXmlFragmentToProsemirror(currentSchema, fragment)\n const migrated = hasFlatTranscript(fragment) ? currentSchema.nodeFromJSON(migrateLegacyContent(current.toJSON())) : current\n const withIds = assignBlockIds(migrated) ?? (migrated === current ? null : migrated)\n if (withIds) {\n prosemirrorToYXmlFragment(withIds, fragment)\n // Persist right away: a backfill after a restart would assign other IDs than clients already hold.\n await persist(doc)\n }\n return doc\n }\n }\n const loaded = await documents.loadContent(documentName)\n if (!loaded) throw new Error('Dokument nicht gefunden') // Hocuspocus closes the connection\n // No usable Yjs state (new, legacy, stale or restored document): seed it from the stored JSON, with block IDs.\n const seeded = prosemirrorToYDoc(contentNode(currentSchema, loaded), COLLAB_FIELD)\n // Persist right away: re-seeding later would create new Yjs items that duplicate what clients still hold.\n await persist(seeded)\n return seeded\n },\n async onStoreDocument({ documentName, document }) {\n if (forgotten.has(documentName)) return\n const tracked = loadedVersions.get(documentName)\n const current = tracked === undefined ? undefined : await contentVersion(documentName)\n if (current !== undefined && current !== tracked) {\n // The content was rewritten behind the editor's back: writing the Yjs content back would clobber the rewrite.\n // Merge the rewrite into the open document instead and store the state against the new version.\n await mergeRewrite(documentName, document, current)\n return\n }\n await documents.storeContent(documentName, yDocToProsemirrorJSON(document, COLLAB_FIELD))\n // After the content: the write itself moves the version, and the state must carry the one it matches.\n const version = await contentVersion(documentName)\n if (version !== undefined) loadedVersions.set(documentName, version)\n await state.storeState(documentName, encodeCollabState({ version: version ?? null, update: Y.encodeStateAsUpdate(document) }))\n },\n async afterUnloadDocument({ documentName }) {\n forgotten.delete(documentName)\n loadedVersions.delete(documentName)\n if (hocuspocus.getDocumentsCount() === 0) idle.splice(0).forEach(resolve => resolve())\n },\n })\n\n return {\n hocuspocus,\n schema: collabSchema,\n handleConnection(socket, request, context = {}) {\n if (closed) throw new Error('tiptap-editor: der Collab-Server ist geschlossen')\n // Deferred: Hocuspocus answers permission-denied first, then the socket closes.\n const reject = () => void setTimeout(() => socket.close(UNAUTHORIZED.code, UNAUTHORIZED.reason), 0)\n return hocuspocus.handleConnection(socket, request, { ...context, [REJECT]: reject })\n },\n async forgetDocument(id) {\n const doc = hocuspocus.documents.get(id)\n if (doc) {\n forgotten.add(id)\n doc.broadcastStateless('deleted')\n hocuspocus.closeConnections(id)\n }\n await state.removeState(id)\n },\n liveContent(id) {\n const doc = hocuspocus.documents.get(id)\n return doc ? yDocToProsemirrorJSON(doc, COLLAB_FIELD) as JSONContent : null\n },\n async contentChanged(id) {\n const doc = hocuspocus.documents.get(id)\n if (!doc || forgotten.has(id)) return\n const tracked = loadedVersions.get(id)\n if (tracked === undefined) return // no `contentVersion`: a rewrite cannot be told from the server's own writes\n const current = await contentVersion(id)\n if (current === undefined || current === tracked) return\n await mergeRewrite(id, doc, current)\n },\n stats: () => ({ connections: hocuspocus.getConnectionsCount(), documents: hocuspocus.getDocumentsCount() }),\n async close() {\n closed = true\n if (hocuspocus.getDocumentsCount() === 0) return\n await new Promise<void>((resolve) => {\n idle.push(resolve)\n hocuspocus.closeConnections()\n hocuspocus.flushPendingStores()\n })\n },\n }\n}\n\nexport type { WebSocketLike }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collab.js","names":[],"sources":["../../src/server/collab.ts"],"sourcesContent":["import { type Configuration, Hocuspocus, type WebSocketLike } from '@hocuspocus/server'\nimport { type AnyExtension, getSchema, type JSONContent } from '@tiptap/core'\nimport type { Schema } from '@tiptap/pm/model'\nimport { prosemirrorToYDoc, prosemirrorToYXmlFragment, yDocToProsemirrorJSON, yXmlFragmentToProsemirror } from '@tiptap/y-tiptap'\nimport * as Y from 'yjs'\nimport { assignBlockIds, EMPTY_EDITOR_CONTENT, isOldPersistedNode, TYPED_BLOCK } from '../core/schema'\nimport { hasFlatTranscript, migrateLegacyContent } from '../features/composites/schema'\nimport { advancedSchemaExtensions } from '../schema/advanced'\nimport { decodeCollabState, encodeCollabState, isStateStale } from './collab-state'\nimport type { AccessControl, CollabStateStore, DocumentContentStore, EditorPorts } from './ports'\n\n/** Y.XmlFragment the TipTap Collaboration extension binds to (its default `field`). */\nexport const COLLAB_FIELD = 'default'\n\n/** Close code of a connection whose client could not be authenticated (Hocuspocus' `Unauthorized`, HTTP 401 in websocket terms). */\nconst UNAUTHORIZED = { code: 4401, reason: 'Unauthorized' }\n\n/** Context key under which `handleConnection` hands the connection's socket closer to the `onAuthenticate` hook. */\nconst REJECT = Symbol('collab.reject')\n\nexport interface CollabServerOptions {\n /** Access control, documents and Yjs state of the editor backend; `collabState` is required. */\n ports: EditorPorts\n /** TipTap extensions of the collaboration schema; default `advancedSchemaExtensions()`. Host-defined nodes and marks: `() => [...advancedSchemaExtensions(), HostNode]` (Vue-free node files only, no node views). */\n schema?: () => AnyExtension[]\n /** Per-document access and content versioning; each defaults to the port's method of the same name. */\n hooks?: {\n authorize?: NonNullable<AccessControl['authorize']>\n contentVersion?: NonNullable<DocumentContentStore['contentVersion']>\n }\n /** Further Hocuspocus configuration (`debounce`, `timeout`, `quiet`, …); the editor's hooks win over equally named ones. */\n configuration?: Partial<Configuration>\n}\n\n/** One served websocket connection: feed it the socket's messages and its close event. */\nexport interface CollabConnection {\n handleMessage(data: Uint8Array): void\n handleClose(event?: CloseEvent): void\n}\n\nexport interface CollabServer {\n /**\n * The Hocuspocus instance of this server. The Yjs state (via the `collabState` port's `loadState`/`storeState`) is the\n * source of truth for content, the host's JSON content (via the `documents` port) is a mirror kept up to date on every store.\n */\n hocuspocus: Hocuspocus\n /** Schema of the collab server: `options.schema()` (default `advancedSchemaExtensions()`), built on first use. */\n schema(): Schema\n /**\n * Serves one websocket connection (any transport: crossws, `ws`, Bun, Deno). The user is authenticated in Hocuspocus'\n * `onAuthenticate` from the upgrade request's headers and the client's token, unless the transport already knows it\n * and passes it as `context.user`. Wire `handleMessage` and `handleClose` of the returned connection to the socket's events.\n */\n handleConnection(socket: WebSocketLike, request: Request, context?: { user?: object }): CollabConnection\n /** Tell connected clients the document is gone, disconnect them and drop the Yjs state. Call before removing the JSON. */\n forgetDocument(id: string): Promise<void>\n /** Current content of a document that is open in Hocuspocus (unsaved edits included), `null` when nobody has it open. */\n liveContent(id: string): JSONContent | null\n /**\n * The host rewrote the content behind the editor's back (an agent wrote the file): merge it into the open document now,\n * as a diff-update connected clients see as an ordinary edit. Without this the merge waits for the document's next store.\n * No-op when nobody has the document open or the content version is unchanged; needs the documents port's `contentVersion`.\n */\n contentChanged(id: string): Promise<void>\n /** Open collaboration connections and loaded documents of this server. */\n stats(): { connections: number, documents: number }\n /** Disconnect every client, write pending stores and wait until all documents are unloaded. The server accepts no connection afterwards. */\n close(): Promise<void>\n}\n\n/** ProseMirror document of stored JSON content: old persisted names and legacy nodes migrated, empty content replaced by the empty document, block IDs assigned. */\nfunction contentNode(schema: Schema, loaded: JSONContent) {\n const content = migrateLegacyContent(loaded)\n const node = schema.nodeFromJSON(content.type === 'doc' && content.content?.length ? content : EMPTY_EDITOR_CONTENT)\n return assignBlockIds(node) ?? node\n}\n\n/** Whether the stored Yjs content still uses an old persisted name (the names listed in `persisted-names.ts`); the current schema does not know those elements. */\nfunction hasOldPersistedNames(fragment: Y.XmlFragment): boolean {\n return !fragment.createTreeWalker(node => node instanceof Y.XmlElement && isOldPersistedNode(node.nodeName, node.getAttributes(), { earliest: true })).next().done\n}\n\n/** Whether a Typed block of the stored Yjs content still carries the flat `meta.transcript` that `migrateLegacyContent` turns into a `rendition` child; its Block type does not matter. */\nfunction hasFlatTranscriptBlock(fragment: Y.XmlFragment): boolean {\n return !fragment.createTreeWalker(node => node instanceof Y.XmlElement && node.nodeName === TYPED_BLOCK && hasFlatTranscript(node.getAttribute('meta'))).next().done\n}\n\n/** Hocuspocus answers a thrown error's `reason` as permission-denied. */\nconst denied = (message: string, reason: string) => Object.assign(new Error(message), { reason })\n\n/** The `collabState` port; the collaboration server cannot run without it. */\nfunction collabStateOf(ports: EditorPorts): CollabStateStore {\n if (!ports.collabState) throw new Error('@ecsplico/editor: collaboration needs the collabState port')\n return ports.collabState\n}\n\n/**\n * The editor's collaboration server, independent of any web framework: Hocuspocus with the editor's hooks (seeding from\n * the stored JSON, versioned state, block-ID backfill, debounced `storeContent`/`storeState`) over `options.ports`.\n * `/nuxt` mounts one in Nitro; any Node/Bun/Deno process can serve `handleConnection` on its own websocket transport.\n */\nexport function createCollabServer(options: CollabServerOptions): CollabServer {\n const { identity, documents } = options.ports\n const state = collabStateOf(options.ports)\n const authorize = (user: object, id: string) => options.hooks?.authorize ? options.hooks.authorize(user, id) : identity.authorize?.(user, id)\n const contentVersion = (id: string) => options.hooks?.contentVersion ? options.hooks.contentVersion(id) : documents.contentVersion?.(id)\n\n let schema: Schema | undefined\n const collabSchema = () => schema ??= getSchema((options.schema ?? advancedSchemaExtensions)())\n\n /** Loaded documents deleted via `forgetDocument`; a store still pending for them must not write their Yjs state back. */\n const forgotten = new Set<string>()\n /** `contentVersion` the loaded document's content mirror had when it was last read or written; only tracked for documents ports with `contentVersion`. */\n const loadedVersions = new Map<string, string | null>()\n /** Resolvers of `close()` waiting for the last document to unload. */\n const idle: (() => void)[] = []\n let closed = false\n\n /** Merges content rewritten behind the editor's back into the open document and stores its state against `version`. */\n async function mergeRewrite(id: string, document: Y.Doc, version: string | null): Promise<void> {\n const rewritten = await documents.loadContent(id)\n if (!rewritten) return // removed meanwhile: nothing to merge, nothing to mirror\n // A diff-update: unchanged blocks keep their Yjs items, so connected clients see an ordinary edit and need no reload.\n prosemirrorToYXmlFragment(contentNode(collabSchema(), rewritten), document.getXmlFragment(COLLAB_FIELD))\n loadedVersions.set(id, version)\n await state.storeState(id, encodeCollabState({ version, update: Y.encodeStateAsUpdate(document) }))\n }\n\n /** Stored state of a document, `null` when there is none usable: it was written against another content version and is removed. */\n async function loadCurrentState(id: string, liveVersion: string | null | undefined): Promise<Uint8Array | null> {\n const payload = await state.loadState(id)\n if (!payload) return null\n const { version, update } = decodeCollabState(payload)\n if (isStateStale(version, liveVersion)) {\n await state.removeState(id)\n return null\n }\n return update\n }\n\n const hocuspocus = new Hocuspocus({\n ...options.configuration,\n async onAuthenticate({ context, token, requestHeaders, documentName, connectionConfig }) {\n // The transport may know the user already; otherwise it comes from the upgrade request's headers or the client's token.\n let user = context.user as object | null | undefined\n if (!user) user = await identity.authenticate({ headers: requestHeaders, ...(token && { token }) })\n if (!user) {\n // After the permission-denied answer Hocuspocus sends: close the socket, so a client without credentials does not linger.\n const socketCloser = context[REJECT] as (() => void) | undefined\n socketCloser?.()\n throw denied('Unauthorized', 'unauthorized')\n }\n const access = await authorize(user, documentName)\n // `undefined`: no authorize hook, every authenticated user may edit. `null`: rejected; the thrown error's `reason` is answered as permission-denied.\n if (access === null) throw denied('Forbidden', 'forbidden')\n if (access?.readOnly) connectionConfig.readOnly = true\n return { user }\n },\n async onLoadDocument({ documentName }) {\n const currentSchema = collabSchema()\n // Read before the content: a change in between leaves an older version on the state, which only costs a re-seed.\n const version = await contentVersion(documentName)\n const stored = await loadCurrentState(documentName, version)\n if (version !== undefined) loadedVersions.set(documentName, version)\n const persist = (doc: Y.Doc) => state.storeState(documentName, encodeCollabState({ version: version ?? null, update: Y.encodeStateAsUpdate(doc) }))\n if (stored) {\n // Decoded into a scratch doc: Hocuspocus only gets it once it is known to be current.\n const doc = new Y.Doc()\n Y.applyUpdate(doc, stored)\n const fragment = doc.getXmlFragment(COLLAB_FIELD)\n if (hasOldPersistedNames(fragment)) {\n // State from before the rename of Typed blocks (or from before the composite family) holds element names and\n // attributes the schema no longer knows, at any depth. Migrate from the state itself, not from the JSON mirror: the\n // schema-less JSON of the fragment goes through the JSON migration (block IDs and content stay), and is written back\n // here, before any client syncs. Persisted right away, so a second load finds nothing left to migrate.\n const migrated = contentNode(currentSchema, yDocToProsemirrorJSON(doc, COLLAB_FIELD))\n prosemirrorToYXmlFragment(migrated, fragment)\n await persist(doc)\n return doc\n }\n // Blocks without an ID (state from before block IDs) and a Typed block's flat `meta.transcript` (it\n // became a `rendition` child): migrate here, before any client syncs, so clients never backfill concurrently.\n // The diff-update keeps the Yjs items of everything that did not change.\n const current = yXmlFragmentToProsemirror(currentSchema, fragment)\n const migrated = hasFlatTranscriptBlock(fragment) ? currentSchema.nodeFromJSON(migrateLegacyContent(current.toJSON())) : current\n const withIds = assignBlockIds(migrated) ?? (migrated === current ? null : migrated)\n if (withIds) {\n prosemirrorToYXmlFragment(withIds, fragment)\n // Persist right away: a backfill after a restart would assign other IDs than clients already hold.\n await persist(doc)\n }\n return doc\n }\n const loaded = await documents.loadContent(documentName)\n if (!loaded) throw new Error('Dokument nicht gefunden') // Hocuspocus closes the connection\n // No usable Yjs state (new, legacy, stale or restored document): seed it from the stored JSON, with block IDs.\n const seeded = prosemirrorToYDoc(contentNode(currentSchema, loaded), COLLAB_FIELD)\n // Persist right away: re-seeding later would create new Yjs items that duplicate what clients still hold.\n await persist(seeded)\n return seeded\n },\n async onStoreDocument({ documentName, document }) {\n if (forgotten.has(documentName)) return\n const tracked = loadedVersions.get(documentName)\n const current = tracked === undefined ? undefined : await contentVersion(documentName)\n if (current !== undefined && current !== tracked) {\n // The content was rewritten behind the editor's back: writing the Yjs content back would clobber the rewrite.\n // Merge the rewrite into the open document instead and store the state against the new version.\n await mergeRewrite(documentName, document, current)\n return\n }\n await documents.storeContent(documentName, yDocToProsemirrorJSON(document, COLLAB_FIELD))\n // After the content: the write itself moves the version, and the state must carry the one it matches.\n const version = await contentVersion(documentName)\n if (version !== undefined) loadedVersions.set(documentName, version)\n await state.storeState(documentName, encodeCollabState({ version: version ?? null, update: Y.encodeStateAsUpdate(document) }))\n },\n async afterUnloadDocument({ documentName }) {\n forgotten.delete(documentName)\n loadedVersions.delete(documentName)\n if (hocuspocus.getDocumentsCount() === 0) idle.splice(0).forEach(resolve => resolve())\n },\n })\n\n return {\n hocuspocus,\n schema: collabSchema,\n handleConnection(socket, request, context = {}) {\n if (closed) throw new Error('tiptap-editor: der Collab-Server ist geschlossen')\n // Deferred: Hocuspocus answers permission-denied first, then the socket closes.\n const reject = () => void setTimeout(() => socket.close(UNAUTHORIZED.code, UNAUTHORIZED.reason), 0)\n return hocuspocus.handleConnection(socket, request, { ...context, [REJECT]: reject })\n },\n async forgetDocument(id) {\n const doc = hocuspocus.documents.get(id)\n if (doc) {\n forgotten.add(id)\n doc.broadcastStateless('deleted')\n hocuspocus.closeConnections(id)\n }\n await state.removeState(id)\n },\n liveContent(id) {\n const doc = hocuspocus.documents.get(id)\n return doc ? yDocToProsemirrorJSON(doc, COLLAB_FIELD) as JSONContent : null\n },\n async contentChanged(id) {\n const doc = hocuspocus.documents.get(id)\n if (!doc || forgotten.has(id)) return\n const tracked = loadedVersions.get(id)\n if (tracked === undefined) return // no `contentVersion`: a rewrite cannot be told from the server's own writes\n const current = await contentVersion(id)\n if (current === undefined || current === tracked) return\n await mergeRewrite(id, doc, current)\n },\n stats: () => ({ connections: hocuspocus.getConnectionsCount(), documents: hocuspocus.getDocumentsCount() }),\n async close() {\n closed = true\n if (hocuspocus.getDocumentsCount() === 0) return\n await new Promise<void>((resolve) => {\n idle.push(resolve)\n hocuspocus.closeConnections()\n hocuspocus.flushPendingStores()\n })\n },\n }\n}\n\nexport type { WebSocketLike }\n"],"mappings":"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"}
|
|
@@ -66,7 +66,7 @@ export declare const chatBodySchema: z.ZodObject<{
|
|
|
66
66
|
model: z.ZodString;
|
|
67
67
|
apiKey: z.ZodString;
|
|
68
68
|
}, z.core.$strip>]>;
|
|
69
|
-
|
|
69
|
+
blockTypes: z.ZodArray<z.ZodObject<{
|
|
70
70
|
id: z.ZodString;
|
|
71
71
|
label: z.ZodString;
|
|
72
72
|
purpose: z.ZodOptional<z.ZodString>;
|
|
@@ -77,9 +77,12 @@ export declare const chatBodySchema: z.ZodObject<{
|
|
|
77
77
|
number: "number";
|
|
78
78
|
date: "date";
|
|
79
79
|
text: "text";
|
|
80
|
+
reference: "reference";
|
|
80
81
|
select: "select";
|
|
81
82
|
textarea: "textarea";
|
|
82
83
|
multiselect: "multiselect";
|
|
84
|
+
bibliography: "bibliography";
|
|
85
|
+
derivation: "derivation";
|
|
83
86
|
}>;
|
|
84
87
|
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
85
88
|
value: z.ZodString;
|
|
@@ -97,7 +100,7 @@ export declare const chatBodySchema: z.ZodObject<{
|
|
|
97
100
|
targets: z.ZodArray<z.ZodString>;
|
|
98
101
|
structure: z.ZodEnum<{
|
|
99
102
|
flat: "flat";
|
|
100
|
-
|
|
103
|
+
structured: "structured";
|
|
101
104
|
}>;
|
|
102
105
|
}, z.core.$strip>;
|
|
103
106
|
/**
|
|
@@ -105,7 +108,7 @@ export declare const chatBodySchema: z.ZodObject<{
|
|
|
105
108
|
* `edit_document` tool. Operations pointing to IDs that are unknown or outside `scope`, an `add` without `afterId`, an
|
|
106
109
|
* operation inside a block that another operation replaces or deletes, or converting into a type not in
|
|
107
110
|
* `targets`, make the model retry (at most `AGENT_LIMITS.retries` times); what is still invalid then, or left out by a
|
|
108
|
-
* corrected list, is dropped. The system prompt describes the editor's block model from `
|
|
111
|
+
* corrected list, is dropped. The system prompt describes the editor's block model from `blockTypes`, `blocks` and `targets`. SSE like
|
|
109
112
|
* `/api/ai/generate`: `delta` (text), `ops` (`{ ops: AgentOp[], dropped: AgentOpProblem[], retries: number }`, once,
|
|
110
113
|
* before `done`), `done`, `error`.
|
|
111
114
|
*/
|
|
@@ -116,7 +119,7 @@ export declare function completePost(ctx: AiHandlerContext, request: HandlerRequ
|
|
|
116
119
|
export declare function generatePost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
117
120
|
/**
|
|
118
121
|
* The models the client may choose from (without keys) — the `models` port's `listModels` for the user, else the
|
|
119
|
-
* server-defined presets — and the default preset id per model
|
|
122
|
+
* server-defined presets — and the default preset id per model role, for the client's model profiles. Requires an
|
|
120
123
|
* authenticated user through the `identity` port; not rate limited, since it calls no model.
|
|
121
124
|
*/
|
|
122
125
|
export declare function modelsGet(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|