@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,7 +1,7 @@
|
|
|
1
1
|
import { HandlerContext, HandlerRequest, HandlerResponse } from "./context.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/server/handlers/images.d.ts
|
|
4
|
-
/** The
|
|
4
|
+
/** The prompt blocks the host manages (`ImageStudioPromptBlock[]`). `501` without an `images` port. */
|
|
5
5
|
export declare function imagePromptBlocksGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
6
6
|
/** Body schema of `imageGeneratePost`. */
|
|
7
7
|
export declare const imageGenerateBodySchema: z.ZodObject<{
|
|
@@ -4,7 +4,7 @@ import { MAX_MEDIA_BYTES, mediaKindOf } from "../../features/media/schema.js";
|
|
|
4
4
|
import { authenticated, json, port, readBody } from "./context.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/server/handlers/images.ts
|
|
7
|
-
/** The
|
|
7
|
+
/** The prompt blocks the host manages (`ImageStudioPromptBlock[]`). `501` without an `images` port. */
|
|
8
8
|
async function imagePromptBlocksGet(ctx, request) {
|
|
9
9
|
const user = await authenticated(ctx, request);
|
|
10
10
|
return json(await port(ctx, "images").promptBlocks(user));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images.js","names":[],"sources":["../../../src/server/handlers/images.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { ImageStudioGenerateBody } from '../../core/backend-types'\nimport { ASSET_DOCUMENT_ID, MAX_MEDIA_BYTES, mediaKindOf } from '../../features/media/schema'\nimport { HttpError } from '../errors'\nimport { authenticated, type HandlerContext, type HandlerRequest, type HandlerResponse, json, port, readBody } from './context'\n\n// The Bild-Studio's three routes over the `images` port: the host's
|
|
1
|
+
{"version":3,"file":"images.js","names":[],"sources":["../../../src/server/handlers/images.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { ImageStudioGenerateBody } from '../../core/backend-types'\nimport { ASSET_DOCUMENT_ID, MAX_MEDIA_BYTES, mediaKindOf } from '../../features/media/schema'\nimport { HttpError } from '../errors'\nimport { authenticated, type HandlerContext, type HandlerRequest, type HandlerResponse, json, port, readBody } from './context'\n\n// The Bild-Studio's three routes over the `images` port: the host's prompt blocks, one generation, one reference upload.\n// The host's provider owns the prompt, the model and the storage; these handlers only authenticate, validate and pass through.\n\n/** The prompt blocks the host manages (`ImageStudioPromptBlock[]`). `501` without an `images` port. */\nexport async function imagePromptBlocksGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n return json(await port(ctx, 'images').promptBlocks(user))\n}\n\n/** Body schema of `imageGeneratePost`. */\nexport const imageGenerateBodySchema = z.object({\n description: z.string().max(10_000),\n instruction: z.string().max(4_000),\n promptBlockIds: z.array(z.string().min(1).max(200)).max(50),\n referenceUrls: z.array(z.string().min(1).max(2_048)).max(20),\n}) satisfies z.ZodType<ImageStudioGenerateBody>\n\n/** Generates one image for the body (`ImageStudioGenerateBody`); answers the stored `ImageStudioImage`. `400` for a malformed body, `501` without an `images` port. */\nexport async function imageGeneratePost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n const images = port(ctx, 'images')\n const body = await readBody(request, imageGenerateBodySchema)\n return json(await images.generate(body, user))\n}\n\n/**\n * Stores a reference image: multipart fields `documentId` (the document it belongs to, or `scratch`) and `file` (an image);\n * answers the stored `ImageStudioImage`. `400` for a malformed `documentId` or a missing file, `413` for a file over the\n * image limit, `415` for a file that is no image, `501` without an `images` port.\n */\nexport async function imageUploadPost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n const images = port(ctx, 'images')\n // A body that is no multipart form at all counts as „no file“.\n const form = await request.readForm?.().catch(() => null)\n const documentId = form?.get('documentId')\n if (typeof documentId !== 'string' || !ASSET_DOCUMENT_ID.test(documentId)) throw new HttpError(400, 'Ungültige Dokument-ID')\n const file = form?.get('file')\n if (!(file instanceof File)) throw new HttpError(400, 'Keine Datei übermittelt')\n if (mediaKindOf(file.type) !== 'image') throw new HttpError(415, 'Nur Bilddateien werden akzeptiert')\n if (file.size > MAX_MEDIA_BYTES.image) throw new HttpError(413, `Datei ist größer als ${MAX_MEDIA_BYTES.image / 1024 / 1024} MB`)\n return json(await images.upload(documentId, file, user))\n}\n"],"mappings":";;;;;;;AAUA,eAAsB,qBAAqB,KAAqB,SAAmD;CACjH,MAAM,OAAO,MAAM,cAAc,KAAK,OAAO;CAC7C,OAAO,KAAK,MAAM,KAAK,KAAK,QAAQ,CAAC,CAAC,aAAa,IAAI,CAAC;AAC1D;;AAGA,MAAa,0BAA0B,EAAE,OAAO;CAC9C,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,GAAM;CAClC,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,GAAK;CACjC,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;CAC1D,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAK,CAAC,CAAC,CAAC,IAAI,EAAE;AAC7D,CAAC;;AAGD,eAAsB,kBAAkB,KAAqB,SAAmD;CAC9G,MAAM,OAAO,MAAM,cAAc,KAAK,OAAO;CAC7C,MAAM,SAAS,KAAK,KAAK,QAAQ;CACjC,MAAM,OAAO,MAAM,SAAS,SAAS,uBAAuB;CAC5D,OAAO,KAAK,MAAM,OAAO,SAAS,MAAM,IAAI,CAAC;AAC/C;;;;;;AAOA,eAAsB,gBAAgB,KAAqB,SAAmD;CAC5G,MAAM,OAAO,MAAM,cAAc,KAAK,OAAO;CAC7C,MAAM,SAAS,KAAK,KAAK,QAAQ;CAEjC,MAAM,OAAO,MAAM,QAAQ,WAAW,CAAC,CAAC,YAAY,IAAI;CACxD,MAAM,aAAa,MAAM,IAAI,YAAY;CACzC,IAAI,OAAO,eAAe,YAAY,CAAC,kBAAkB,KAAK,UAAU,GAAG,MAAM,IAAI,UAAU,KAAK,uBAAuB;CAC3H,MAAM,OAAO,MAAM,IAAI,MAAM;CAC7B,IAAI,EAAE,gBAAgB,OAAO,MAAM,IAAI,UAAU,KAAK,yBAAyB;CAC/E,IAAI,YAAY,KAAK,IAAI,MAAM,SAAS,MAAM,IAAI,UAAU,KAAK,mCAAmC;CACpG,IAAI,KAAK,OAAO,gBAAgB,OAAO,MAAM,IAAI,UAAU,KAAK,wBAAwB,gBAAgB,QAAQ,OAAO,KAAK,IAAI;CAChI,OAAO,KAAK,MAAM,OAAO,OAAO,YAAY,MAAM,IAAI,CAAC;AACzD"}
|
package/dist/server/host.d.ts
CHANGED
|
@@ -3,15 +3,19 @@ import "../features/ai/protocol/index.js";
|
|
|
3
3
|
import { AiModels } from "./ai.js";
|
|
4
4
|
import { EditorPorts } from "./ports.js";
|
|
5
5
|
import { CollabServer } from "./collab.js";
|
|
6
|
+
import { BlockType } from "../features/composites/block-types.js";
|
|
7
|
+
import "../features/composites/schema.js";
|
|
6
8
|
import { AnyExtension, JSONContent } from "@tiptap/core";
|
|
7
9
|
import { LanguageModel } from "ai";
|
|
8
10
|
//#region src/server/host.d.ts
|
|
9
11
|
/**
|
|
10
12
|
* Registers the host's ports for installers that read them later (the Nuxt module's server plugin installs on the first
|
|
11
13
|
* request, after every Nitro plugin has run). `options.schema`: the collaboration schema, see `CollabServerOptions.schema`.
|
|
14
|
+
* `options.blockTypes`: the host's Block types, which the AI chat describes to the model instead of the client's `blockTypes` (`EditorServerOptions.blockTypes`).
|
|
12
15
|
*/
|
|
13
16
|
export declare function setEditorPorts(next: EditorPorts, options?: {
|
|
14
17
|
schema?: () => AnyExtension[];
|
|
18
|
+
blockTypes?: readonly BlockType[];
|
|
15
19
|
}): void;
|
|
16
20
|
export declare function useEditorPorts(): EditorPorts;
|
|
17
21
|
/** The process-wide collaboration server of the installed server. Throws when none is installed or `collab: false`. */
|
package/dist/server/host.js
CHANGED
|
@@ -3,7 +3,8 @@ import { enforceRateLimit } from "./handlers/context.js";
|
|
|
3
3
|
//#region src/server/host.ts
|
|
4
4
|
let ports;
|
|
5
5
|
let schema;
|
|
6
|
-
|
|
6
|
+
let blockTypes;
|
|
7
|
+
/** Handle of the live installation; `adoptEditorServer` sets it, its release clears it. */
|
|
7
8
|
let installation;
|
|
8
9
|
let collab;
|
|
9
10
|
let models;
|
|
@@ -14,10 +15,12 @@ function installed() {
|
|
|
14
15
|
/**
|
|
15
16
|
* Registers the host's ports for installers that read them later (the Nuxt module's server plugin installs on the first
|
|
16
17
|
* request, after every Nitro plugin has run). `options.schema`: the collaboration schema, see `CollabServerOptions.schema`.
|
|
18
|
+
* `options.blockTypes`: the host's Block types, which the AI chat describes to the model instead of the client's `blockTypes` (`EditorServerOptions.blockTypes`).
|
|
17
19
|
*/
|
|
18
20
|
function setEditorPorts(next, options = {}) {
|
|
19
21
|
ports = next;
|
|
20
22
|
schema = options.schema;
|
|
23
|
+
blockTypes = options.blockTypes;
|
|
21
24
|
}
|
|
22
25
|
function useEditorPorts() {
|
|
23
26
|
if (!ports) throw new Error("@ecsplico/editor: setEditorPorts() was not called");
|
|
@@ -27,6 +30,10 @@ function useEditorPorts() {
|
|
|
27
30
|
function useEditorSchema() {
|
|
28
31
|
return schema;
|
|
29
32
|
}
|
|
33
|
+
/** The Block types registered with `setEditorPorts`, for installers that build the AI handlers themselves (`EditorServerOptions.blockTypes`). */
|
|
34
|
+
function useEditorBlockTypes() {
|
|
35
|
+
return blockTypes;
|
|
36
|
+
}
|
|
30
37
|
/**
|
|
31
38
|
* Makes the server `installEditorServer` built the process-wide one, so host code (`useCollabServer`, `useAiModels`,
|
|
32
39
|
* `liveContent`, `enforceAiRateLimit`, …) reaches the same instances the routes use. Returns the function that drops them
|
|
@@ -37,6 +44,7 @@ function adoptEditorServer(server) {
|
|
|
37
44
|
installation = own;
|
|
38
45
|
ports = server.ports;
|
|
39
46
|
schema = server.schema;
|
|
47
|
+
blockTypes = server.blockTypes;
|
|
40
48
|
collab = server.collab;
|
|
41
49
|
models = server.models;
|
|
42
50
|
limiter = server.limiter;
|
|
@@ -117,6 +125,6 @@ async function enforceAiRateLimit(request) {
|
|
|
117
125
|
}
|
|
118
126
|
}
|
|
119
127
|
//#endregion
|
|
120
|
-
export { adoptEditorServer, closeEditorServer, collabContentChanged, collabStats, enforceAiRateLimit, forgetCollabDocument, listAiModels, liveContent, resolveModel, setEditorPorts, useAiModels, useCollabServer, useEditorPorts, useEditorSchema, useRateLimiter };
|
|
128
|
+
export { adoptEditorServer, closeEditorServer, collabContentChanged, collabStats, enforceAiRateLimit, forgetCollabDocument, listAiModels, liveContent, resolveModel, setEditorPorts, useAiModels, useCollabServer, useEditorBlockTypes, useEditorPorts, useEditorSchema, useRateLimiter };
|
|
121
129
|
|
|
122
130
|
//# sourceMappingURL=host.js.map
|
package/dist/server/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","names":[],"sources":["../../src/server/host.ts"],"sourcesContent":["import type { AnyExtension, JSONContent } from '@tiptap/core'\nimport type { LanguageModel } from 'ai'\nimport type { ModelProfile, ModelRef } from '../features/ai/protocol'\nimport type { AiModels } from './ai'\nimport type { CollabServer } from './collab'\nimport { HttpError } from './errors'\nimport { enforceRateLimit } from './handlers/context'\nimport type { EditorPorts } from './ports'\nimport type { RateLimiter } from './rate-limit'\n\n// The process-wide editor server of hosts that wire the editor once at startup (the Nuxt module, a script with several\n// modules): the ports, collaboration server, AI models and rate limiter `installEditorServer` built, reachable from host\n// code (`useCollabServer`, `resolveModel`, `liveContent`, …). Their configuration is the installer's options (see\n// `defineEditorConfig` for the defaults); instances for tests or several tenants come from `createCollabServer` /\n// `createAiModels` / `createRateLimiter`, which take their configuration explicitly.\n\nlet ports: EditorPorts | undefined\nlet schema: (() => AnyExtension[]) | undefined\n/**
|
|
1
|
+
{"version":3,"file":"host.js","names":[],"sources":["../../src/server/host.ts"],"sourcesContent":["import type { AnyExtension, JSONContent } from '@tiptap/core'\nimport type { LanguageModel } from 'ai'\nimport type { BlockType } from '../features/composites/schema'\nimport type { ModelProfile, ModelRef } from '../features/ai/protocol'\nimport type { AiModels } from './ai'\nimport type { CollabServer } from './collab'\nimport { HttpError } from './errors'\nimport { enforceRateLimit } from './handlers/context'\nimport type { EditorPorts } from './ports'\nimport type { RateLimiter } from './rate-limit'\n\n// The process-wide editor server of hosts that wire the editor once at startup (the Nuxt module, a script with several\n// modules): the ports, collaboration server, AI models and rate limiter `installEditorServer` built, reachable from host\n// code (`useCollabServer`, `resolveModel`, `liveContent`, …). Their configuration is the installer's options (see\n// `defineEditorConfig` for the defaults); instances for tests or several tenants come from `createCollabServer` /\n// `createAiModels` / `createRateLimiter`, which take their configuration explicitly.\n\nlet ports: EditorPorts | undefined\nlet schema: (() => AnyExtension[]) | undefined\nlet blockTypes: readonly BlockType[] | undefined\n/** Handle of the live installation; `adoptEditorServer` sets it, its release clears it. */\nlet installation: object | undefined\nlet collab: CollabServer | undefined\nlet models: AiModels | undefined\nlet limiter: RateLimiter | undefined\n\nfunction installed() {\n if (!installation) throw new Error('@ecsplico/editor: server not installed')\n}\n\n/**\n * Registers the host's ports for installers that read them later (the Nuxt module's server plugin installs on the first\n * request, after every Nitro plugin has run). `options.schema`: the collaboration schema, see `CollabServerOptions.schema`.\n * `options.blockTypes`: the host's Block types, which the AI chat describes to the model instead of the client's `blockTypes` (`EditorServerOptions.blockTypes`).\n */\nexport function setEditorPorts(next: EditorPorts, options: { schema?: () => AnyExtension[], blockTypes?: readonly BlockType[] } = {}) {\n ports = next\n schema = options.schema\n blockTypes = options.blockTypes\n}\n\nexport function useEditorPorts(): EditorPorts {\n if (!ports) throw new Error('@ecsplico/editor: setEditorPorts() was not called')\n return ports\n}\n\n/** The collaboration schema registered with `setEditorPorts`, for installers that build the collaboration server themselves. */\nexport function useEditorSchema(): (() => AnyExtension[]) | undefined {\n return schema\n}\n\n/** The Block types registered with `setEditorPorts`, for installers that build the AI handlers themselves (`EditorServerOptions.blockTypes`). */\nexport function useEditorBlockTypes(): readonly BlockType[] | undefined {\n return blockTypes\n}\n\n/**\n * Makes the server `installEditorServer` built the process-wide one, so host code (`useCollabServer`, `useAiModels`,\n * `liveContent`, `enforceAiRateLimit`, …) reaches the same instances the routes use. Returns the function that drops them\n * again (only when no later installation replaced them); the installer calls it when its host closes.\n */\nexport function adoptEditorServer(server: { ports: EditorPorts, schema?: () => AnyExtension[], blockTypes?: readonly BlockType[], collab?: CollabServer, models?: AiModels, limiter?: RateLimiter }): () => void {\n const own = {}\n installation = own\n ports = server.ports\n schema = server.schema\n blockTypes = server.blockTypes\n collab = server.collab\n models = server.models\n limiter = server.limiter\n return () => {\n if (installation !== own) return\n installation = collab = models = limiter = undefined\n }\n}\n\n/** The process-wide collaboration server of the installed server. Throws when none is installed or `collab: false`. */\nexport function useCollabServer(): CollabServer {\n installed()\n if (!collab) throw new Error('@ecsplico/editor: collaboration is off (installEditorServer with collab: false)')\n return collab\n}\n\n/** The process-wide AI models of the installed server: its configured presets, with the `models` port's `languageModel`/`listModels` hooks. Throws when none is installed or it was installed without `ai`. */\nexport function useAiModels(): AiModels {\n installed()\n if (!models) throw new Error('@ecsplico/editor: AI is off (installEditorServer without ai)')\n return models\n}\n\n/** The process-wide AI rate limiter of the installed server (present with `ai`). */\nexport function useRateLimiter(): RateLimiter {\n installed()\n if (!limiter) throw new Error('@ecsplico/editor: AI is off (installEditorServer without ai)')\n return limiter\n}\n\n/** Closes the process-wide collaboration server (pending stores are written first) and forgets it. */\nexport async function closeEditorServer() {\n const open = collab\n collab = undefined\n await open?.close()\n}\n\n/** Open collaboration connections and loaded documents of this process (zeros when collaboration is off). */\nexport function collabStats(): { connections: number, documents: number } {\n installed()\n return collab?.stats() ?? { connections: 0, documents: 0 }\n}\n\n/** Tell connected clients the document is gone, disconnect them and drop the Yjs state. Call before removing the JSON. */\nexport function forgetCollabDocument(id: string): Promise<void> {\n return useCollabServer().forgetDocument(id)\n}\n\n/** Current content of a document that is open in the collaboration server (unsaved edits included), `null` when nobody has it open. */\nexport function liveContent(id: string): JSONContent | null {\n installed()\n return collab?.liveContent(id) ?? null\n}\n\n/** The host rewrote a document's content (an agent wrote the file): merge it into the open document now, see `CollabServer.contentChanged`. */\nexport function collabContentChanged(id: string): Promise<void> {\n installed()\n return collab?.contentChanged(id) ?? Promise.resolve()\n}\n\n/** AI SDK language model for `ref`, see `AiModels.resolveModel`. */\nexport function resolveModel(ref: ModelRef, user: object | null): Promise<LanguageModel> {\n return useAiModels().resolveModel(ref, user)\n}\n\n/** The models listed for `user`, see `AiModels.listModels`. */\nexport function listAiModels(user: object | null): Promise<ModelProfile[]> {\n return useAiModels().listModels(user)\n}\n\n/**\n * Per-user limit for host-defined AI routes: the same limiter as the editor's own AI endpoints. Call first in the route.\n * Throws 401 (`HttpError`) without a user, 429 when exceeded; returns the user the identity port authenticated, for `resolveModel`.\n * With a Node-backed request (`event.node.res`, as h3 events have) the 429's `Retry-After` header is set on the response too.\n */\nexport async function enforceAiRateLimit(request: { headers: Headers, node?: { res: { setHeader(name: string, value: string): unknown } } }): Promise<object> {\n try {\n return await enforceRateLimit({ ports: useEditorPorts(), ai: useAiModels(), limiter: useRateLimiter() }, { headers: request.headers })\n }\n catch (error) {\n if (error instanceof HttpError) for (const [name, value] of Object.entries(error.headers)) request.node?.res.setHeader(name, value)\n throw error\n }\n}\n"],"mappings":";;;AAiBA,IAAI;AACJ,IAAI;AACJ,IAAI;;AAEJ,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AAEJ,SAAS,YAAY;CACnB,IAAI,CAAC,cAAc,MAAM,IAAI,MAAM,wCAAwC;AAC7E;;;;;;AAOA,SAAgB,eAAe,MAAmB,UAAgF,CAAC,GAAG;CACpI,QAAQ;CACR,SAAS,QAAQ;CACjB,aAAa,QAAQ;AACvB;AAEA,SAAgB,iBAA8B;CAC5C,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,mDAAmD;CAC/E,OAAO;AACT;;AAGA,SAAgB,kBAAsD;CACpE,OAAO;AACT;;AAGA,SAAgB,sBAAwD;CACtE,OAAO;AACT;;;;;;AAOA,SAAgB,kBAAkB,QAA+K;CAC/M,MAAM,MAAM,CAAC;CACb,eAAe;CACf,QAAQ,OAAO;CACf,SAAS,OAAO;CAChB,aAAa,OAAO;CACpB,SAAS,OAAO;CAChB,SAAS,OAAO;CAChB,UAAU,OAAO;CACjB,aAAa;EACX,IAAI,iBAAiB,KAAK;EAC1B,eAAe,SAAS,SAAS,UAAU,KAAA;CAC7C;AACF;;AAGA,SAAgB,kBAAgC;CAC9C,UAAU;CACV,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,iFAAiF;CAC9G,OAAO;AACT;;AAGA,SAAgB,cAAwB;CACtC,UAAU;CACV,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,8DAA8D;CAC3F,OAAO;AACT;;AAGA,SAAgB,iBAA8B;CAC5C,UAAU;CACV,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,8DAA8D;CAC5F,OAAO;AACT;;AAGA,eAAsB,oBAAoB;CACxC,MAAM,OAAO;CACb,SAAS,KAAA;CACT,MAAM,MAAM,MAAM;AACpB;;AAGA,SAAgB,cAA0D;CACxE,UAAU;CACV,OAAO,QAAQ,MAAM,KAAK;EAAE,aAAa;EAAG,WAAW;CAAE;AAC3D;;AAGA,SAAgB,qBAAqB,IAA2B;CAC9D,OAAO,gBAAgB,CAAC,CAAC,eAAe,EAAE;AAC5C;;AAGA,SAAgB,YAAY,IAAgC;CAC1D,UAAU;CACV,OAAO,QAAQ,YAAY,EAAE,KAAK;AACpC;;AAGA,SAAgB,qBAAqB,IAA2B;CAC9D,UAAU;CACV,OAAO,QAAQ,eAAe,EAAE,KAAK,QAAQ,QAAQ;AACvD;;AAGA,SAAgB,aAAa,KAAe,MAA6C;CACvF,OAAO,YAAY,CAAC,CAAC,aAAa,KAAK,IAAI;AAC7C;;AAGA,SAAgB,aAAa,MAA8C;CACzE,OAAO,YAAY,CAAC,CAAC,WAAW,IAAI;AACtC;;;;;;AAOA,eAAsB,mBAAmB,SAAqH;CAC5J,IAAI;EACF,OAAO,MAAM,iBAAiB;GAAE,OAAO,eAAe;GAAG,IAAI,YAAY;GAAG,SAAS,eAAe;EAAE,GAAG,EAAE,SAAS,QAAQ,QAAQ,CAAC;CACvI,SACO,OAAO;EACZ,IAAI,iBAAiB,WAAW,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,OAAO,GAAG,QAAQ,MAAM,IAAI,UAAU,MAAM,KAAK;EAClI,MAAM;CACR;AACF"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ModelPresetConfig, TranscriptionConfig } from "../config/index.js";
|
|
2
2
|
import { AuthenticateRequest } from "./auth.js";
|
|
3
3
|
import { ChatMessage } from "../features/ai/protocol/generate.js";
|
|
4
4
|
import { HttpError } from "./errors.js";
|
|
5
5
|
import { AiModels, AiModelsConfig, ChatTurnPart, createAiModels, streamChat, streamChatTurn, upstreamError } from "./ai.js";
|
|
6
|
-
import { AssetStore, CollabStateStore, Directory, DocumentAdmin,
|
|
6
|
+
import { AccessControl, AssetStore, CollabStateStore, Directory, DocumentAdmin, DocumentContentStore, EditorPorts, ImageProvider, ModelProvider, PORT_CAPABILITY, SettingsStore, ThemeStore, serverCapabilities } from "./ports.js";
|
|
7
7
|
import { COLLAB_FIELD, CollabConnection, CollabServer, CollabServerOptions, WebSocketLike, createCollabServer } from "./collab.js";
|
|
8
8
|
import { decodeCollabState, encodeCollabState, isStateStale } from "./collab-state.js";
|
|
9
9
|
import { RateLimiter, RateLimiterOptions, createRateLimiter, userKey } from "./rate-limit.js";
|
|
10
10
|
import { closeEditorServer, collabContentChanged, collabStats, enforceAiRateLimit, forgetCollabDocument, listAiModels, liveContent, resolveModel, setEditorPorts, useAiModels, useCollabServer, useEditorPorts } from "./host.js";
|
|
11
|
-
export { type AiModels, type AiModelsConfig, type AssetStore, type AuthenticateRequest, COLLAB_FIELD, type ChatMessage, type ChatTurnPart, type CollabConnection, type CollabServer, type CollabServerOptions, type CollabStateStore, type Directory, type DocumentAdmin, type
|
|
11
|
+
export { type AccessControl, type AiModels, type AiModelsConfig, type AssetStore, type AuthenticateRequest, COLLAB_FIELD, type ChatMessage, type ChatTurnPart, type CollabConnection, type CollabServer, type CollabServerOptions, type CollabStateStore, type Directory, type DocumentAdmin, type DocumentContentStore, type EditorPorts, HttpError, type ImageProvider, type ModelPresetConfig, type ModelProvider, PORT_CAPABILITY, type RateLimiter, type RateLimiterOptions, type SettingsStore, type ThemeStore, type TranscriptionConfig, type WebSocketLike, closeEditorServer, collabContentChanged, collabStats, createAiModels, createCollabServer, createRateLimiter, decodeCollabState, encodeCollabState, enforceAiRateLimit, forgetCollabDocument, isStateStale, listAiModels, liveContent, resolveModel, serverCapabilities, setEditorPorts, streamChat, streamChatTurn, upstreamError, useAiModels, useCollabServer, useEditorPorts, userKey };
|
package/dist/server/ports.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { JSONContent } from "@tiptap/core";
|
|
|
12
12
|
import { LanguageModel } from "ai";
|
|
13
13
|
//#region src/server/ports.d.ts
|
|
14
14
|
/** Who a request or websocket connection belongs to, and which documents that user may open. */
|
|
15
|
-
export interface
|
|
15
|
+
export interface AccessControl {
|
|
16
16
|
/**
|
|
17
17
|
* The user behind a request, `null` = not authenticated (HTTP routes answer 401). Collaboration: called in Hocuspocus
|
|
18
18
|
* `onAuthenticate` with the upgrade request's `headers` and the client's `token` (`undefined` when it sent none, so cookie
|
|
@@ -30,8 +30,8 @@ export interface Identity {
|
|
|
30
30
|
readOnly: boolean;
|
|
31
31
|
} | null>;
|
|
32
32
|
}
|
|
33
|
-
/** The stored TipTap JSON of the workspace's documents. */
|
|
34
|
-
export interface
|
|
33
|
+
/** The stored TipTap JSON of the workspace's documents (content only; the structure around it is the tree store's). */
|
|
34
|
+
export interface DocumentContentStore {
|
|
35
35
|
/** TipTap JSON to seed a document's Y.Doc from; `null` = the document does not exist. */
|
|
36
36
|
loadContent(id: string): Promise<JSONContent | null>;
|
|
37
37
|
/** Mirror of the current Yjs state, called debounced after edits and on shutdown. Must tolerate ids deleted meanwhile. */
|
|
@@ -51,7 +51,7 @@ export interface DocumentStore {
|
|
|
51
51
|
content: JSONContent;
|
|
52
52
|
}[]>;
|
|
53
53
|
}
|
|
54
|
-
/** Document management a host needs next to the editor's own storage methods: `
|
|
54
|
+
/** Document management a host needs next to the editor's own storage methods: `DocumentContentStore.storeContent` only updates. */
|
|
55
55
|
export interface DocumentAdmin {
|
|
56
56
|
/** Creates the document or replaces its content; the content version changes. */
|
|
57
57
|
putDocument(id: string, content: JSONContent): Promise<void>;
|
|
@@ -122,9 +122,9 @@ export interface ModelProvider {
|
|
|
122
122
|
*/
|
|
123
123
|
listModels?(user: object | null): Promise<ModelProfile[] | null>;
|
|
124
124
|
}
|
|
125
|
-
/** The host's image generation for the Bild-Studio: its managed
|
|
125
|
+
/** The host's image generation for the Bild-Studio: its managed prompt blocks, the generator and the storage of uploaded references. Without this port the image routes answer 501. */
|
|
126
126
|
export interface ImageProvider {
|
|
127
|
-
/** The
|
|
127
|
+
/** The prompt blocks the host manages, in the order it lists them (`GET /api/editor/images/prompt-blocks`). `user` is what `authenticate` returned. */
|
|
128
128
|
promptBlocks(user: object): Promise<ImageStudioPromptBlock[]>;
|
|
129
129
|
/** Generates one image and stores it (`POST /api/editor/images/generate`); resolves with the stored image (`active: false`). The host builds the prompt from `body`. */
|
|
130
130
|
generate(body: ImageStudioGenerateBody, user: object): Promise<ImageStudioImage>;
|
|
@@ -133,8 +133,8 @@ export interface ImageProvider {
|
|
|
133
133
|
}
|
|
134
134
|
/** Everything the editor's server reads from the host, one port per capability. Only `identity` and `documents` are required. */
|
|
135
135
|
export interface EditorPorts {
|
|
136
|
-
identity:
|
|
137
|
-
documents:
|
|
136
|
+
identity: AccessControl;
|
|
137
|
+
documents: DocumentContentStore & Partial<DocumentAdmin>;
|
|
138
138
|
/** Required by the collaboration server. */
|
|
139
139
|
collabState?: CollabStateStore;
|
|
140
140
|
directory?: Directory;
|
package/dist/server/ports.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ports.js","names":[],"sources":["../../src/server/ports.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport type { LanguageModel } from 'ai'\nimport type { CommentStore } from '../adapters/comments/types'\nimport type { BackendCapability } from '../core/backend-capability'\nimport type { ImageStudioGenerateBody, ImageStudioImage, ImageStudioPromptBlock } from '../core/backend-types'\nimport type { ModelProfile, ModelRef } from '../features/ai/protocol'\nimport type { AssetRecord } from '../features/media/schema'\nimport type { MentionUser } from '../features/mentions/schema'\nimport type { ThemeRecord } from '../features/themes/schema'\nimport type { EditorSettings } from '../settings'\nimport type { AuthenticateRequest } from './auth'\n\n// The server side of the editor's backend, split by capability: a host (or an adapter) implements the ports it serves\n// and leaves the others out; routes and features that need an absent port answer 501.\n\n/** Who a request or websocket connection belongs to, and which documents that user may open. */\nexport interface Identity {\n /**\n * The user behind a request, `null` = not authenticated (HTTP routes answer 401). Collaboration: called in Hocuspocus\n * `onAuthenticate` with the upgrade request's `headers` and the client's `token` (`undefined` when it sent none, so cookie\n * hosts keep working and bearer-only hosts read `token`); `null` closes the connection with 4401. With the Nuxt module\n * option `collab.authenticateUpgrade` it runs once at the upgrade with the `headers` only and `null` refuses the upgrade\n * with HTTP 401. The value becomes the connection context's `user`.\n */\n authenticate(request: AuthenticateRequest): Promise<object | null>\n /**\n * Per-document access for the collaboration websocket, called in Hocuspocus `onAuthenticate` (right after `authenticate`) with the user.\n * `null` rejects the connection; `{ readOnly: true }` lets it sync but applies none of its edits. Omit it to let every\n * authenticated user edit every document.\n */\n authorize?(user: object, id: string): Promise<{ readOnly: boolean } | null>\n}\n\n/** The stored TipTap JSON of the workspace's documents. */\nexport interface DocumentStore {\n /** TipTap JSON to seed a document's Y.Doc from; `null` = the document does not exist. */\n loadContent(id: string): Promise<JSONContent | null>\n /** Mirror of the current Yjs state, called debounced after edits and on shutdown. Must tolerate ids deleted meanwhile. */\n storeContent(id: string, content: JSONContent): Promise<void>\n /**\n * Opaque version of the stored content of a document (mtime, hash, revision), `null` = unknown, no NUL. The collab server\n * keeps it next to the Yjs state. When it differs from the live version (the content changed behind the editor's back) the\n * state is discarded on load, removed through `removeState` and the document is re-seeded from `loadContent`. A document\n * that is open when its version moves is not written back: the next store merges the new `loadContent` into the open\n * Y.Doc. The version is read right after every `storeContent`, so it must reflect what the editor itself just wrote.\n * Omit it to never discard state.\n */\n contentVersion?(id: string): Promise<string | null>\n /** Stored TipTap JSON of every document in the workspace, e.g. to collect the `#` tags in use (`GET /api/editor/tags`). */\n listContents(): Promise<{ id: string, content: JSONContent }[]>\n}\n\n/** Document management a host needs next to the editor's own storage methods: `DocumentStore.storeContent` only updates. */\nexport interface DocumentAdmin {\n /** Creates the document or replaces its content; the content version changes. */\n putDocument(id: string, content: JSONContent): Promise<void>\n /** Removes the content, the Yjs state and the assets of a document; unknown ids are fine. Call `forgetCollabDocument(id)` first when it may be open. */\n deleteDocument(id: string): Promise<void>\n /** Ids of all stored documents. */\n listDocumentIds(): Promise<string[]>\n}\n\n/** Where the collaboration server keeps the Yjs state of open documents. */\nexport interface CollabStateStore {\n /** The bytes `storeState` stored (opaque to the host: the Yjs update behind a content-version prefix; state stored by earlier releases is a raw update and still loads); `null` = none yet (the Y.Doc is then seeded from `loadContent`). */\n loadState(id: string): Promise<Uint8Array | null>\n /** Persists the state bytes unchanged; called on every store and when block IDs were backfilled. Must tolerate ids deleted meanwhile. */\n storeState(id: string, state: Uint8Array): Promise<void>\n /** Drops the Yjs state of a deleted document (the next load re-seeds from `loadContent`). */\n removeState(id: string): Promise<void>\n}\n\n/** The users that can be `@`-mentioned. */\nexport interface Directory {\n /** All users that can be `@`-mentioned (served by `GET /api/editor/users`); must not include credentials. */\n listUsers(): Promise<MentionUser[]>\n}\n\n/** Uploaded files. */\nexport interface AssetStore {\n /** Stores an uploaded file (`GET /api/editor/assets/:id/:name` serves it). `id` is a document id or `SCRATCH_DOCUMENT_ID`; it may name no existing document. */\n storeAsset(id: string, asset: AssetRecord & { data: Buffer }): Promise<void>\n /** A stored file with its record; `null` = unknown id or name. */\n loadAsset(id: string, name: string): Promise<{ record: AssetRecord, data: Buffer } | null>\n}\n\n/** Per-user editor settings. Without this port the client keeps settings in localStorage only. */\nexport interface SettingsStore {\n /** Per-user editor settings as stored (`null` = none yet), validated by `GET /api/editor/settings`. */\n loadSettings(user: object): Promise<unknown | null>\n /** Stores validated settings for the user from `authenticate`. */\n storeSettings(user: object, settings: EditorSettings): Promise<void>\n}\n\n/** The theme library shared by all users. Without this port the themes feature is uninstalled: its routes answer 501. */\nexport interface ThemeStore {\n /** All themes (`GET /api/editor/themes`). */\n listThemes(): Promise<ThemeRecord[]>\n /** A theme's record and `theme.css` (`GET /api/editor/themes/:id`); `null` = unknown id. */\n loadTheme(id: string): Promise<{ record: ThemeRecord, css: string } | null>\n /** Stores an uploaded theme (`POST /api/editor/themes`). */\n storeTheme(record: Omit<ThemeRecord, 'builtin'>, css: string): Promise<void>\n /** Deletes an uploaded theme (`DELETE /api/editor/themes/:id`); `false` = not found or builtin. */\n removeTheme(id: string): Promise<boolean>\n}\n\n/** The host's own language models for the AI endpoints; both members fall through to the runtime-config presets. */\nexport interface ModelProvider {\n /**\n * The language model for `ref`, so the AI endpoints use the host's own providers; consulted before the runtime-config\n * presets for every model reference. `user` is what `authenticate` returned. `null` falls through to the presets\n * (and answers 400 for a preset that is not configured either).\n */\n languageModel?(ref: ModelRef, user: object | null): Promise<LanguageModel | null>\n /**\n * The models `GET /api/ai/models` lists for `user`, instead of the runtime-config presets (list them with\n * `source: 'preset'` and the `id` that `languageModel` receives as `{ preset: id }`). `null` (or omitting the method)\n * lists the presets.\n */\n listModels?(user: object | null): Promise<ModelProfile[] | null>\n}\n\n/** The host's image generation for the Bild-Studio: its managed Prompt-Bausteine, the generator and the storage of uploaded references. Without this port the image routes answer 501. */\nexport interface ImageProvider {\n /** The Prompt-Bausteine the host manages, in the order it lists them (`GET /api/editor/images/prompt-blocks`). `user` is what `authenticate` returned. */\n promptBlocks(user: object): Promise<ImageStudioPromptBlock[]>\n /** Generates one image and stores it (`POST /api/editor/images/generate`); resolves with the stored image (`active: false`). The host builds the prompt from `body`. */\n generate(body: ImageStudioGenerateBody, user: object): Promise<ImageStudioImage>\n /** Stores an uploaded reference image of document `documentId` (`POST /api/editor/images/upload`); resolves with the stored image (`active: true`). */\n upload(documentId: string, file: File, user: object): Promise<ImageStudioImage>\n}\n\n/** Everything the editor's server reads from the host, one port per capability. Only `identity` and `documents` are required. */\nexport interface EditorPorts {\n identity: Identity\n documents: DocumentStore & Partial<DocumentAdmin>\n /** Required by the collaboration server. */\n collabState?: CollabStateStore\n directory?: Directory\n assets?: AssetStore\n settings?: SettingsStore\n themes?: ThemeStore\n models?: ModelProvider\n images?: ImageProvider\n /** Where the comment threads live (`adapters/comments/{json,drizzle}`). */\n comments?: CommentStore\n}\n\n/** The capability each optional port provides; `identity` is no capability, `tags` derive from `documents` (see `serverCapabilities`). */\nexport const PORT_CAPABILITY: Record<Exclude<keyof EditorPorts, 'identity'>, BackendCapability> = {\n documents: 'documents',\n collabState: 'collaboration',\n directory: 'users',\n assets: 'assets',\n settings: 'settings',\n themes: 'themes',\n models: 'ai',\n comments: 'comments',\n images: 'images',\n}\n\n/**\n * The capabilities `ports` serves: those of every present port, plus `tags` (derived from `documents.listContents`).\n * Two capabilities depend on more than a port: `ai` (the AI routes need configured models) and `collaboration` (the\n * websocket needs the collab server). `served` states what the installed server really mounts and overrides the port-based\n * answer for them; omitted keys follow the ports.\n */\nexport function serverCapabilities(ports: EditorPorts, served: { ai?: boolean, collaboration?: boolean } = {}): BackendCapability[] {\n const capabilities = new Set<BackendCapability>(\n (Object.keys(PORT_CAPABILITY) as (keyof typeof PORT_CAPABILITY)[])\n .filter(port => ports[port] !== undefined)\n .map(port => PORT_CAPABILITY[port]),\n )\n for (const capability of ['ai', 'collaboration'] as const) {\n if (served[capability] === true) capabilities.add(capability)\n else if (served[capability] === false) capabilities.delete(capability)\n }\n if (ports.documents) capabilities.add('tags')\n return [...capabilities]\n}\n"],"mappings":";;AAqJA,MAAa,kBAAqF;CAChG,WAAW;CACX,aAAa;CACb,WAAW;CACX,QAAQ;CACR,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,QAAQ;AACV;;;;;;;AAQA,SAAgB,mBAAmB,OAAoB,SAAoD,CAAC,GAAwB;CAClI,MAAM,eAAe,IAAI,IACtB,OAAO,KAAK,eAAe,CAAC,CAC1B,QAAO,SAAQ,MAAM,UAAU,KAAA,CAAS,CAAC,CACzC,KAAI,SAAQ,gBAAgB,KAAK,CACtC;CACA,KAAK,MAAM,cAAc,CAAC,MAAM,eAAe,GAC7C,IAAI,OAAO,gBAAgB,MAAM,aAAa,IAAI,UAAU;MACvD,IAAI,OAAO,gBAAgB,OAAO,aAAa,OAAO,UAAU;CAEvE,IAAI,MAAM,WAAW,aAAa,IAAI,MAAM;CAC5C,OAAO,CAAC,GAAG,YAAY;AACzB"}
|
|
1
|
+
{"version":3,"file":"ports.js","names":[],"sources":["../../src/server/ports.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport type { LanguageModel } from 'ai'\nimport type { CommentStore } from '../adapters/comments/types'\nimport type { BackendCapability } from '../core/backend-capability'\nimport type { ImageStudioGenerateBody, ImageStudioImage, ImageStudioPromptBlock } from '../core/backend-types'\nimport type { ModelProfile, ModelRef } from '../features/ai/protocol'\nimport type { AssetRecord } from '../features/media/schema'\nimport type { MentionUser } from '../features/mentions/schema'\nimport type { ThemeRecord } from '../features/themes/schema'\nimport type { EditorSettings } from '../settings'\nimport type { AuthenticateRequest } from './auth'\n\n// The server side of the editor's backend, split by capability: a host (or an adapter) implements the ports it serves\n// and leaves the others out; routes and features that need an absent port answer 501.\n\n/** Who a request or websocket connection belongs to, and which documents that user may open. */\nexport interface AccessControl {\n /**\n * The user behind a request, `null` = not authenticated (HTTP routes answer 401). Collaboration: called in Hocuspocus\n * `onAuthenticate` with the upgrade request's `headers` and the client's `token` (`undefined` when it sent none, so cookie\n * hosts keep working and bearer-only hosts read `token`); `null` closes the connection with 4401. With the Nuxt module\n * option `collab.authenticateUpgrade` it runs once at the upgrade with the `headers` only and `null` refuses the upgrade\n * with HTTP 401. The value becomes the connection context's `user`.\n */\n authenticate(request: AuthenticateRequest): Promise<object | null>\n /**\n * Per-document access for the collaboration websocket, called in Hocuspocus `onAuthenticate` (right after `authenticate`) with the user.\n * `null` rejects the connection; `{ readOnly: true }` lets it sync but applies none of its edits. Omit it to let every\n * authenticated user edit every document.\n */\n authorize?(user: object, id: string): Promise<{ readOnly: boolean } | null>\n}\n\n/** The stored TipTap JSON of the workspace's documents (content only; the structure around it is the tree store's). */\nexport interface DocumentContentStore {\n /** TipTap JSON to seed a document's Y.Doc from; `null` = the document does not exist. */\n loadContent(id: string): Promise<JSONContent | null>\n /** Mirror of the current Yjs state, called debounced after edits and on shutdown. Must tolerate ids deleted meanwhile. */\n storeContent(id: string, content: JSONContent): Promise<void>\n /**\n * Opaque version of the stored content of a document (mtime, hash, revision), `null` = unknown, no NUL. The collab server\n * keeps it next to the Yjs state. When it differs from the live version (the content changed behind the editor's back) the\n * state is discarded on load, removed through `removeState` and the document is re-seeded from `loadContent`. A document\n * that is open when its version moves is not written back: the next store merges the new `loadContent` into the open\n * Y.Doc. The version is read right after every `storeContent`, so it must reflect what the editor itself just wrote.\n * Omit it to never discard state.\n */\n contentVersion?(id: string): Promise<string | null>\n /** Stored TipTap JSON of every document in the workspace, e.g. to collect the `#` tags in use (`GET /api/editor/tags`). */\n listContents(): Promise<{ id: string, content: JSONContent }[]>\n}\n\n/** Document management a host needs next to the editor's own storage methods: `DocumentContentStore.storeContent` only updates. */\nexport interface DocumentAdmin {\n /** Creates the document or replaces its content; the content version changes. */\n putDocument(id: string, content: JSONContent): Promise<void>\n /** Removes the content, the Yjs state and the assets of a document; unknown ids are fine. Call `forgetCollabDocument(id)` first when it may be open. */\n deleteDocument(id: string): Promise<void>\n /** Ids of all stored documents. */\n listDocumentIds(): Promise<string[]>\n}\n\n/** Where the collaboration server keeps the Yjs state of open documents. */\nexport interface CollabStateStore {\n /** The bytes `storeState` stored (opaque to the host: the Yjs update behind a content-version prefix; state stored by earlier releases is a raw update and still loads); `null` = none yet (the Y.Doc is then seeded from `loadContent`). */\n loadState(id: string): Promise<Uint8Array | null>\n /** Persists the state bytes unchanged; called on every store and when block IDs were backfilled. Must tolerate ids deleted meanwhile. */\n storeState(id: string, state: Uint8Array): Promise<void>\n /** Drops the Yjs state of a deleted document (the next load re-seeds from `loadContent`). */\n removeState(id: string): Promise<void>\n}\n\n/** The users that can be `@`-mentioned. */\nexport interface Directory {\n /** All users that can be `@`-mentioned (served by `GET /api/editor/users`); must not include credentials. */\n listUsers(): Promise<MentionUser[]>\n}\n\n/** Uploaded files. */\nexport interface AssetStore {\n /** Stores an uploaded file (`GET /api/editor/assets/:id/:name` serves it). `id` is a document id or `SCRATCH_DOCUMENT_ID`; it may name no existing document. */\n storeAsset(id: string, asset: AssetRecord & { data: Buffer }): Promise<void>\n /** A stored file with its record; `null` = unknown id or name. */\n loadAsset(id: string, name: string): Promise<{ record: AssetRecord, data: Buffer } | null>\n}\n\n/** Per-user editor settings. Without this port the client keeps settings in localStorage only. */\nexport interface SettingsStore {\n /** Per-user editor settings as stored (`null` = none yet), validated by `GET /api/editor/settings`. */\n loadSettings(user: object): Promise<unknown | null>\n /** Stores validated settings for the user from `authenticate`. */\n storeSettings(user: object, settings: EditorSettings): Promise<void>\n}\n\n/** The theme library shared by all users. Without this port the themes feature is uninstalled: its routes answer 501. */\nexport interface ThemeStore {\n /** All themes (`GET /api/editor/themes`). */\n listThemes(): Promise<ThemeRecord[]>\n /** A theme's record and `theme.css` (`GET /api/editor/themes/:id`); `null` = unknown id. */\n loadTheme(id: string): Promise<{ record: ThemeRecord, css: string } | null>\n /** Stores an uploaded theme (`POST /api/editor/themes`). */\n storeTheme(record: Omit<ThemeRecord, 'builtin'>, css: string): Promise<void>\n /** Deletes an uploaded theme (`DELETE /api/editor/themes/:id`); `false` = not found or builtin. */\n removeTheme(id: string): Promise<boolean>\n}\n\n/** The host's own language models for the AI endpoints; both members fall through to the runtime-config presets. */\nexport interface ModelProvider {\n /**\n * The language model for `ref`, so the AI endpoints use the host's own providers; consulted before the runtime-config\n * presets for every model reference. `user` is what `authenticate` returned. `null` falls through to the presets\n * (and answers 400 for a preset that is not configured either).\n */\n languageModel?(ref: ModelRef, user: object | null): Promise<LanguageModel | null>\n /**\n * The models `GET /api/ai/models` lists for `user`, instead of the runtime-config presets (list them with\n * `source: 'preset'` and the `id` that `languageModel` receives as `{ preset: id }`). `null` (or omitting the method)\n * lists the presets.\n */\n listModels?(user: object | null): Promise<ModelProfile[] | null>\n}\n\n/** The host's image generation for the Bild-Studio: its managed prompt blocks, the generator and the storage of uploaded references. Without this port the image routes answer 501. */\nexport interface ImageProvider {\n /** The prompt blocks the host manages, in the order it lists them (`GET /api/editor/images/prompt-blocks`). `user` is what `authenticate` returned. */\n promptBlocks(user: object): Promise<ImageStudioPromptBlock[]>\n /** Generates one image and stores it (`POST /api/editor/images/generate`); resolves with the stored image (`active: false`). The host builds the prompt from `body`. */\n generate(body: ImageStudioGenerateBody, user: object): Promise<ImageStudioImage>\n /** Stores an uploaded reference image of document `documentId` (`POST /api/editor/images/upload`); resolves with the stored image (`active: true`). */\n upload(documentId: string, file: File, user: object): Promise<ImageStudioImage>\n}\n\n/** Everything the editor's server reads from the host, one port per capability. Only `identity` and `documents` are required. */\nexport interface EditorPorts {\n identity: AccessControl\n documents: DocumentContentStore & Partial<DocumentAdmin>\n /** Required by the collaboration server. */\n collabState?: CollabStateStore\n directory?: Directory\n assets?: AssetStore\n settings?: SettingsStore\n themes?: ThemeStore\n models?: ModelProvider\n images?: ImageProvider\n /** Where the comment threads live (`adapters/comments/{json,drizzle}`). */\n comments?: CommentStore\n}\n\n/** The capability each optional port provides; `identity` is no capability, `tags` derive from `documents` (see `serverCapabilities`). */\nexport const PORT_CAPABILITY: Record<Exclude<keyof EditorPorts, 'identity'>, BackendCapability> = {\n documents: 'documents',\n collabState: 'collaboration',\n directory: 'users',\n assets: 'assets',\n settings: 'settings',\n themes: 'themes',\n models: 'ai',\n comments: 'comments',\n images: 'images',\n}\n\n/**\n * The capabilities `ports` serves: those of every present port, plus `tags` (derived from `documents.listContents`).\n * Two capabilities depend on more than a port: `ai` (the AI routes need configured models) and `collaboration` (the\n * websocket needs the collab server). `served` states what the installed server really mounts and overrides the port-based\n * answer for them; omitted keys follow the ports.\n */\nexport function serverCapabilities(ports: EditorPorts, served: { ai?: boolean, collaboration?: boolean } = {}): BackendCapability[] {\n const capabilities = new Set<BackendCapability>(\n (Object.keys(PORT_CAPABILITY) as (keyof typeof PORT_CAPABILITY)[])\n .filter(port => ports[port] !== undefined)\n .map(port => PORT_CAPABILITY[port]),\n )\n for (const capability of ['ai', 'collaboration'] as const) {\n if (served[capability] === true) capabilities.add(capability)\n else if (served[capability] === false) capabilities.delete(capability)\n }\n if (ports.documents) capabilities.add('tags')\n return [...capabilities]\n}\n"],"mappings":";;AAqJA,MAAa,kBAAqF;CAChG,WAAW;CACX,aAAa;CACb,WAAW;CACX,QAAQ;CACR,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,QAAQ;AACV;;;;;;;AAQA,SAAgB,mBAAmB,OAAoB,SAAoD,CAAC,GAAwB;CAClI,MAAM,eAAe,IAAI,IACtB,OAAO,KAAK,eAAe,CAAC,CAC1B,QAAO,SAAQ,MAAM,UAAU,KAAA,CAAS,CAAC,CACzC,KAAI,SAAQ,gBAAgB,KAAK,CACtC;CACA,KAAK,MAAM,cAAc,CAAC,MAAM,eAAe,GAC7C,IAAI,OAAO,gBAAgB,MAAM,aAAa,IAAI,UAAU;MACvD,IAAI,OAAO,gBAAgB,OAAO,aAAa,OAAO,UAAU;CAEvE,IAAI,MAAM,WAAW,aAAa,IAAI,MAAM;CAC5C,OAAO,CAAC,GAAG,YAAY;AACzB"}
|
package/dist/settings/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AI_LANGUAGES, AUTHOR_COLOR_STYLES, AiLanguage, AuthorColorStyle, COMMENTS_LAYOUTS, CommentsLayout, DRAG_HANDLE_VISIBILITIES, DragHandleVisibility, HEADING_CONVERSION_SCOPES, HeadingConversionScope,
|
|
1
|
+
import { AI_LANGUAGES, AUTHOR_COLOR_STYLES, AiLanguage, AuthorColorStyle, BLOCK_TYPE_ICON_VISIBILITIES, BlockTypeIconVisibility, COMMENTS_LAYOUTS, CommentsLayout, DRAG_HANDLE_VISIBILITIES, DragHandleVisibility, HEADING_CONVERSION_SCOPES, HeadingConversionScope, MODEL_ROLES, ModelRole, STRUCTURE_MODES, StructureMode, TREE_ROOTS, TreeRoot } from "./values.js";
|
|
2
|
+
import { EDITOR_SETTINGS_VERSION, isOutdatedEditorSettings, migrateEditorSettings } from "./migrate.js";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
//#region src/settings/index.d.ts
|
|
4
|
-
export declare const EDITOR_SETTINGS_VERSION = 1;
|
|
5
5
|
/** Valid theme id: lowercase slug, used as directory name and in `/api/editor/themes/:id`. Defined here so core never imports a feature. */
|
|
6
6
|
export declare const THEME_ID: RegExp;
|
|
7
7
|
/** A user's editor preferences, shared by all editors on the page; persisted per user through the `settings` port. */
|
|
@@ -20,23 +20,23 @@ export interface EditorSettings {
|
|
|
20
20
|
ai: {
|
|
21
21
|
/** Ghost-text suggestions while typing (`GhostAutocomplete`). */
|
|
22
22
|
autocomplete: boolean;
|
|
23
|
-
/** Model profile id per
|
|
24
|
-
|
|
23
|
+
/** Model profile id per model role; a missing or unknown id falls back to the server's default preset for the role. */
|
|
24
|
+
roles: Partial<Record<ModelRole, string>>;
|
|
25
25
|
/** Preselected target language of „Übersetzen“. */
|
|
26
26
|
translateLanguage: AiLanguage;
|
|
27
27
|
};
|
|
28
28
|
blocks: {
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/** Block type id the Typed block shortcuts create (toolbar button, heading conversion). */
|
|
30
|
+
defaultBlockType: string;
|
|
31
31
|
/** Preferred document structure: toolbar block types and the structure of AI-generated blocks. */
|
|
32
32
|
structure: StructureMode;
|
|
33
|
-
/** Root of the document drawer while a
|
|
33
|
+
/** Root of the document drawer while a Document with Typed blocks is loaded: nested in the folders, or the file itself. */
|
|
34
34
|
treeRoot: TreeRoot;
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
/** Standard drag handle of blocks without
|
|
35
|
+
/** Block type icon of Typed blocks (their drag handle): always visible, or only on hover, focus or while the details are open. */
|
|
36
|
+
blockTypeIcon: BlockTypeIconVisibility;
|
|
37
|
+
/** Standard drag handle of blocks without Block type icon: on the hovered block, or permanently beside every block. */
|
|
38
38
|
dragHandle: DragHandleVisibility;
|
|
39
|
-
/** Target size in characters of the parts a length split cuts a
|
|
39
|
+
/** Target size in characters of the parts a length split cuts a Typed block into (200–20000). */
|
|
40
40
|
splitTargetChars: number;
|
|
41
41
|
};
|
|
42
42
|
appearance: {
|
|
@@ -52,7 +52,7 @@ export declare function clampSplitTargetChars(typed: string, current: number): n
|
|
|
52
52
|
export declare const EDITOR_SETTINGS_DEFAULTS: EditorSettings;
|
|
53
53
|
/** Complete, valid settings (the body of `PUT /api/editor/settings`). */
|
|
54
54
|
export declare const editorSettingsSchema: z.ZodObject<{
|
|
55
|
-
version: z.ZodLiteral<
|
|
55
|
+
version: z.ZodLiteral<2>;
|
|
56
56
|
editing: z.ZodObject<{
|
|
57
57
|
headingConversionScope: z.ZodEnum<{
|
|
58
58
|
container: "container";
|
|
@@ -71,7 +71,7 @@ export declare const editorSettingsSchema: z.ZodObject<{
|
|
|
71
71
|
}, z.core.$strip>;
|
|
72
72
|
ai: z.ZodObject<{
|
|
73
73
|
autocomplete: z.ZodBoolean;
|
|
74
|
-
|
|
74
|
+
roles: z.ZodRecord<z.ZodEnum<{
|
|
75
75
|
complete: "complete";
|
|
76
76
|
rewrite: "rewrite";
|
|
77
77
|
generate: "generate";
|
|
@@ -85,16 +85,16 @@ export declare const editorSettingsSchema: z.ZodObject<{
|
|
|
85
85
|
}>;
|
|
86
86
|
}, z.core.$strip>;
|
|
87
87
|
blocks: z.ZodObject<{
|
|
88
|
-
|
|
88
|
+
defaultBlockType: z.ZodString;
|
|
89
89
|
structure: z.ZodEnum<{
|
|
90
90
|
flat: "flat";
|
|
91
|
-
|
|
91
|
+
structured: "structured";
|
|
92
92
|
}>;
|
|
93
93
|
treeRoot: z.ZodEnum<{
|
|
94
94
|
file: "file";
|
|
95
95
|
folders: "folders";
|
|
96
96
|
}>;
|
|
97
|
-
|
|
97
|
+
blockTypeIcon: z.ZodEnum<{
|
|
98
98
|
always: "always";
|
|
99
99
|
hover: "hover";
|
|
100
100
|
}>;
|
|
@@ -109,10 +109,11 @@ export declare const editorSettingsSchema: z.ZodObject<{
|
|
|
109
109
|
}, z.core.$strip>;
|
|
110
110
|
}, z.core.$strip>;
|
|
111
111
|
/**
|
|
112
|
-
* Deep-merges unknown input over the defaults; unknown keys dropped, invalid values replaced.
|
|
113
|
-
* `
|
|
112
|
+
* Deep-merges unknown input over the defaults; unknown keys dropped, invalid values replaced. Reads older stored settings
|
|
113
|
+
* through `migrateEditorSettings` (`./migrate`), including the legacy flat `{ headingConversionScope }` per-browser shape.
|
|
114
|
+
* Always returns fresh objects of the current version.
|
|
114
115
|
*/
|
|
115
116
|
export declare function parseEditorSettings(input: unknown): EditorSettings;
|
|
116
117
|
//#endregion
|
|
117
|
-
export { AI_LANGUAGES, AUTHOR_COLOR_STYLES, AiLanguage, AuthorColorStyle, COMMENTS_LAYOUTS, CommentsLayout, DRAG_HANDLE_VISIBILITIES, DragHandleVisibility, HEADING_CONVERSION_SCOPES, HeadingConversionScope,
|
|
118
|
+
export { AI_LANGUAGES, AUTHOR_COLOR_STYLES, AiLanguage, AuthorColorStyle, BLOCK_TYPE_ICON_VISIBILITIES, BlockTypeIconVisibility, COMMENTS_LAYOUTS, CommentsLayout, DRAG_HANDLE_VISIBILITIES, DragHandleVisibility, EDITOR_SETTINGS_VERSION, HEADING_CONVERSION_SCOPES, HeadingConversionScope, MODEL_ROLES, ModelRole, STRUCTURE_MODES, StructureMode, TREE_ROOTS, TreeRoot, isOutdatedEditorSettings, migrateEditorSettings };
|
|
118
119
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/settings/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AI_LANGUAGES, AUTHOR_COLOR_STYLES, COMMENTS_LAYOUTS, DRAG_HANDLE_VISIBILITIES, HEADING_CONVERSION_SCOPES,
|
|
1
|
+
import { AI_LANGUAGES, AUTHOR_COLOR_STYLES, BLOCK_TYPE_ICON_VISIBILITIES, COMMENTS_LAYOUTS, DRAG_HANDLE_VISIBILITIES, HEADING_CONVERSION_SCOPES, MODEL_ROLES, STRUCTURE_MODES, TREE_ROOTS } from "./values.js";
|
|
2
|
+
import { EDITOR_SETTINGS_VERSION, isOutdatedEditorSettings, migrateEditorSettings } from "./migrate.js";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
//#region src/settings/index.ts
|
|
4
|
-
const EDITOR_SETTINGS_VERSION = 1;
|
|
5
5
|
/** Valid theme id: lowercase slug, used as directory name and in `/api/editor/themes/:id`. Defined here so core never imports a feature. */
|
|
6
6
|
const THEME_ID = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
7
7
|
/** Bounds of `blocks.splitTargetChars`. */
|
|
@@ -13,7 +13,7 @@ function clampSplitTargetChars(typed, current) {
|
|
|
13
13
|
return typed.trim() !== "" && Number.isFinite(value) ? Math.min(SPLIT_TARGET_CHARS_MAX, Math.max(200, value)) : current;
|
|
14
14
|
}
|
|
15
15
|
const EDITOR_SETTINGS_DEFAULTS = {
|
|
16
|
-
version:
|
|
16
|
+
version: 2,
|
|
17
17
|
editing: {
|
|
18
18
|
headingConversionScope: "container",
|
|
19
19
|
authorColors: false,
|
|
@@ -22,14 +22,14 @@ const EDITOR_SETTINGS_DEFAULTS = {
|
|
|
22
22
|
},
|
|
23
23
|
ai: {
|
|
24
24
|
autocomplete: false,
|
|
25
|
-
|
|
25
|
+
roles: {},
|
|
26
26
|
translateLanguage: "Englisch"
|
|
27
27
|
},
|
|
28
28
|
blocks: {
|
|
29
|
-
|
|
30
|
-
structure: "
|
|
29
|
+
defaultBlockType: "section",
|
|
30
|
+
structure: "structured",
|
|
31
31
|
treeRoot: "folders",
|
|
32
|
-
|
|
32
|
+
blockTypeIcon: "always",
|
|
33
33
|
dragHandle: "hover",
|
|
34
34
|
splitTargetChars: 1500
|
|
35
35
|
},
|
|
@@ -42,16 +42,16 @@ const commentsLayoutSchema = z.enum(COMMENTS_LAYOUTS);
|
|
|
42
42
|
const autocompleteSchema = z.boolean();
|
|
43
43
|
const profileIdSchema = z.string().min(1).max(100);
|
|
44
44
|
const translateLanguageSchema = z.enum(AI_LANGUAGES);
|
|
45
|
-
const
|
|
45
|
+
const defaultBlockTypeSchema = z.string().min(1).max(100);
|
|
46
46
|
const structureSchema = z.enum(STRUCTURE_MODES);
|
|
47
47
|
const treeRootSchema = z.enum(TREE_ROOTS);
|
|
48
|
-
const
|
|
48
|
+
const blockTypeIconSchema = z.enum(BLOCK_TYPE_ICON_VISIBILITIES);
|
|
49
49
|
const dragHandleSchema = z.enum(DRAG_HANDLE_VISIBILITIES);
|
|
50
50
|
const splitTargetCharsSchema = z.number().int().min(200).max(SPLIT_TARGET_CHARS_MAX);
|
|
51
51
|
const themeSchema = z.string().regex(THEME_ID).nullable();
|
|
52
52
|
/** Complete, valid settings (the body of `PUT /api/editor/settings`). */
|
|
53
53
|
const editorSettingsSchema = z.object({
|
|
54
|
-
version: z.literal(
|
|
54
|
+
version: z.literal(2),
|
|
55
55
|
editing: z.object({
|
|
56
56
|
headingConversionScope: headingConversionScopeSchema,
|
|
57
57
|
authorColors: authorColorsSchema,
|
|
@@ -60,14 +60,14 @@ const editorSettingsSchema = z.object({
|
|
|
60
60
|
}),
|
|
61
61
|
ai: z.object({
|
|
62
62
|
autocomplete: autocompleteSchema,
|
|
63
|
-
|
|
63
|
+
roles: z.partialRecord(z.enum(MODEL_ROLES), profileIdSchema),
|
|
64
64
|
translateLanguage: translateLanguageSchema
|
|
65
65
|
}),
|
|
66
66
|
blocks: z.object({
|
|
67
|
-
|
|
67
|
+
defaultBlockType: defaultBlockTypeSchema,
|
|
68
68
|
structure: structureSchema,
|
|
69
69
|
treeRoot: treeRootSchema,
|
|
70
|
-
|
|
70
|
+
blockTypeIcon: blockTypeIconSchema,
|
|
71
71
|
dragHandle: dragHandleSchema,
|
|
72
72
|
splitTargetChars: splitTargetCharsSchema
|
|
73
73
|
}),
|
|
@@ -81,20 +81,21 @@ function valid(schema, value, fallback) {
|
|
|
81
81
|
return parsed.success ? parsed.data : fallback;
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
|
-
* Deep-merges unknown input over the defaults; unknown keys dropped, invalid values replaced.
|
|
85
|
-
* `
|
|
84
|
+
* Deep-merges unknown input over the defaults; unknown keys dropped, invalid values replaced. Reads older stored settings
|
|
85
|
+
* through `migrateEditorSettings` (`./migrate`), including the legacy flat `{ headingConversionScope }` per-browser shape.
|
|
86
|
+
* Always returns fresh objects of the current version.
|
|
86
87
|
*/
|
|
87
88
|
function parseEditorSettings(input) {
|
|
88
|
-
const root = record(input);
|
|
89
|
+
const root = record(migrateEditorSettings(input));
|
|
89
90
|
const editing = record(root.editing);
|
|
90
91
|
const ai = record(root.ai);
|
|
91
|
-
const
|
|
92
|
+
const roles = record(ai.roles);
|
|
92
93
|
const blocks = record(root.blocks);
|
|
93
94
|
const appearance = record(root.appearance);
|
|
94
95
|
const defaults = EDITOR_SETTINGS_DEFAULTS;
|
|
95
96
|
const legacyScope = valid(headingConversionScopeSchema, root.headingConversionScope, defaults.editing.headingConversionScope);
|
|
96
97
|
return {
|
|
97
|
-
version:
|
|
98
|
+
version: 2,
|
|
98
99
|
editing: {
|
|
99
100
|
headingConversionScope: valid(headingConversionScopeSchema, editing.headingConversionScope, legacyScope),
|
|
100
101
|
authorColors: valid(authorColorsSchema, editing.authorColors, defaults.editing.authorColors),
|
|
@@ -103,17 +104,17 @@ function parseEditorSettings(input) {
|
|
|
103
104
|
},
|
|
104
105
|
ai: {
|
|
105
106
|
autocomplete: valid(autocompleteSchema, ai.autocomplete, defaults.ai.autocomplete),
|
|
106
|
-
|
|
107
|
-
const parsed = profileIdSchema.safeParse(
|
|
108
|
-
return parsed.success ? [[
|
|
107
|
+
roles: Object.fromEntries(MODEL_ROLES.flatMap((role) => {
|
|
108
|
+
const parsed = profileIdSchema.safeParse(roles[role]);
|
|
109
|
+
return parsed.success ? [[role, parsed.data]] : [];
|
|
109
110
|
})),
|
|
110
111
|
translateLanguage: valid(translateLanguageSchema, ai.translateLanguage, defaults.ai.translateLanguage)
|
|
111
112
|
},
|
|
112
113
|
blocks: {
|
|
113
|
-
|
|
114
|
+
defaultBlockType: valid(defaultBlockTypeSchema, blocks.defaultBlockType, defaults.blocks.defaultBlockType),
|
|
114
115
|
structure: valid(structureSchema, blocks.structure, defaults.blocks.structure),
|
|
115
116
|
treeRoot: valid(treeRootSchema, blocks.treeRoot, defaults.blocks.treeRoot),
|
|
116
|
-
|
|
117
|
+
blockTypeIcon: valid(blockTypeIconSchema, blocks.blockTypeIcon, defaults.blocks.blockTypeIcon),
|
|
117
118
|
dragHandle: valid(dragHandleSchema, blocks.dragHandle, defaults.blocks.dragHandle),
|
|
118
119
|
splitTargetChars: valid(splitTargetCharsSchema, blocks.splitTargetChars, defaults.blocks.splitTargetChars)
|
|
119
120
|
},
|
|
@@ -121,6 +122,6 @@ function parseEditorSettings(input) {
|
|
|
121
122
|
};
|
|
122
123
|
}
|
|
123
124
|
//#endregion
|
|
124
|
-
export { AI_LANGUAGES, AUTHOR_COLOR_STYLES, COMMENTS_LAYOUTS, DRAG_HANDLE_VISIBILITIES, EDITOR_SETTINGS_DEFAULTS, EDITOR_SETTINGS_VERSION, HEADING_CONVERSION_SCOPES,
|
|
125
|
+
export { AI_LANGUAGES, AUTHOR_COLOR_STYLES, BLOCK_TYPE_ICON_VISIBILITIES, COMMENTS_LAYOUTS, DRAG_HANDLE_VISIBILITIES, EDITOR_SETTINGS_DEFAULTS, EDITOR_SETTINGS_VERSION, HEADING_CONVERSION_SCOPES, MODEL_ROLES, SPLIT_TARGET_CHARS_MAX, SPLIT_TARGET_CHARS_MIN, STRUCTURE_MODES, THEME_ID, TREE_ROOTS, clampSplitTargetChars, editorSettingsSchema, isOutdatedEditorSettings, migrateEditorSettings, parseEditorSettings };
|
|
125
126
|
|
|
126
127
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/settings/index.ts"],"sourcesContent":["import { z } from 'zod'\nimport {\n AI_LANGUAGES,\n type AiLanguage,\n AUTHOR_COLOR_STYLES,\n type AuthorColorStyle,\n COMMENTS_LAYOUTS,\n type CommentsLayout,\n DRAG_HANDLE_VISIBILITIES,\n type DragHandleVisibility,\n HEADING_CONVERSION_SCOPES,\n type HeadingConversionScope,\n KIND_ICON_VISIBILITIES,\n type KindIconVisibility,\n MODEL_SLOTS,\n type ModelSlot,\n STRUCTURE_MODES,\n type StructureMode,\n TREE_ROOTS,\n type TreeRoot,\n} from './values'\n\nexport * from './values'\n\nexport const EDITOR_SETTINGS_VERSION = 1\n\n/** Valid theme id: lowercase slug, used as directory name and in `/api/editor/themes/:id`. Defined here so core never imports a feature. */\nexport const THEME_ID = /^[a-z0-9][a-z0-9-]{0,63}$/\n\n/** A user's editor preferences, shared by all editors on the page; persisted per user through the `settings` port. */\nexport interface EditorSettings {\n version: typeof EDITOR_SETTINGS_VERSION\n editing: {\n /** Blocks „Gliederung umwandeln“ (both directions) converts when nothing is selected. */\n headingConversionScope: HeadingConversionScope\n /** Text is tinted per author (authors feature): each run in the colour of the user who wrote it. */\n authorColors: boolean\n /** `background`: tinted background; `text`: text in the author's colour. */\n authorColorStyle: AuthorColorStyle\n /** `margin`: comment cards in a column beside the text; `inline`: popover at the text plus the toolbar's list (comments feature). */\n commentsLayout: CommentsLayout\n }\n ai: {\n /** Ghost-text suggestions while typing (`GhostAutocomplete`). */\n autocomplete: boolean\n /** Model profile id per slot; a missing or unknown id falls back to the server's default preset for the slot. */\n slots: Partial<Record<ModelSlot, string>>\n /** Preselected target language of „Übersetzen“. */\n translateLanguage: AiLanguage\n }\n blocks: {\n /** Composite kind id the „Baustein“ shortcuts create (toolbar button, heading conversion). */\n defaultKind: string\n /** Preferred document structure: toolbar block types and the structure of AI-generated blocks. */\n structure: StructureMode\n /** Root of the document drawer while a composite file is loaded: nested in the folders, or the file itself. */\n treeRoot: TreeRoot\n /** Kind icon of composites (their drag handle): always visible, or only on hover, focus or while the details are open. */\n kindIcon: KindIconVisibility\n /** Standard drag handle of blocks without kind icon: on the hovered block, or permanently beside every block. */\n dragHandle: DragHandleVisibility\n /** Target size in characters of the parts a length split cuts a Baustein into (200–20000). */\n splitTargetChars: number\n }\n appearance: {\n /** Id of the Obsidian theme applied to the whole app (themes feature); `null` = the standard Nuxt UI look. */\n theme: string | null\n }\n}\n\n/** Bounds of `blocks.splitTargetChars`. */\nexport const SPLIT_TARGET_CHARS_MIN = 200\nexport const SPLIT_TARGET_CHARS_MAX = 20000\n\n/** The split target typed into the field as a valid setting: rounded and clamped to its bounds; `current` when `typed` is empty or not a number. */\nexport function clampSplitTargetChars(typed: string, current: number): number {\n const value = Math.round(Number(typed))\n return typed.trim() !== '' && Number.isFinite(value) ? Math.min(SPLIT_TARGET_CHARS_MAX, Math.max(SPLIT_TARGET_CHARS_MIN, value)) : current\n}\n\nexport const EDITOR_SETTINGS_DEFAULTS: EditorSettings = {\n version: EDITOR_SETTINGS_VERSION,\n editing: { headingConversionScope: 'container', authorColors: false, authorColorStyle: 'background', commentsLayout: 'margin' },\n ai: { autocomplete: false, slots: {}, translateLanguage: 'Englisch' },\n blocks: { defaultKind: 'section', structure: 'composite', treeRoot: 'folders', kindIcon: 'always', dragHandle: 'hover', splitTargetChars: 1500 },\n appearance: { theme: null },\n}\n\nconst headingConversionScopeSchema = z.enum(HEADING_CONVERSION_SCOPES)\nconst authorColorsSchema = z.boolean()\nconst authorColorStyleSchema = z.enum(AUTHOR_COLOR_STYLES)\nconst commentsLayoutSchema = z.enum(COMMENTS_LAYOUTS)\nconst autocompleteSchema = z.boolean()\nconst profileIdSchema = z.string().min(1).max(100)\nconst translateLanguageSchema = z.enum(AI_LANGUAGES)\nconst defaultKindSchema = z.string().min(1).max(100)\nconst structureSchema = z.enum(STRUCTURE_MODES)\nconst treeRootSchema = z.enum(TREE_ROOTS)\nconst kindIconSchema = z.enum(KIND_ICON_VISIBILITIES)\nconst dragHandleSchema = z.enum(DRAG_HANDLE_VISIBILITIES)\nconst splitTargetCharsSchema = z.number().int().min(SPLIT_TARGET_CHARS_MIN).max(SPLIT_TARGET_CHARS_MAX)\nconst themeSchema = z.string().regex(THEME_ID).nullable()\n\n/** Complete, valid settings (the body of `PUT /api/editor/settings`). */\nexport const editorSettingsSchema = z.object({\n version: z.literal(EDITOR_SETTINGS_VERSION),\n editing: z.object({\n headingConversionScope: headingConversionScopeSchema,\n authorColors: authorColorsSchema,\n authorColorStyle: authorColorStyleSchema,\n commentsLayout: commentsLayoutSchema,\n }),\n ai: z.object({\n autocomplete: autocompleteSchema,\n slots: z.partialRecord(z.enum(MODEL_SLOTS), profileIdSchema),\n translateLanguage: translateLanguageSchema,\n }),\n blocks: z.object({\n defaultKind: defaultKindSchema,\n structure: structureSchema,\n treeRoot: treeRootSchema,\n kindIcon: kindIconSchema,\n dragHandle: dragHandleSchema,\n splitTargetChars: splitTargetCharsSchema,\n }),\n appearance: z.object({ theme: themeSchema }),\n}) satisfies z.ZodType<EditorSettings>\n\nfunction record(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction valid<T>(schema: z.ZodType<T>, value: unknown, fallback: T): T {\n const parsed = schema.safeParse(value)\n return parsed.success ? parsed.data : fallback\n}\n\n/**\n * Deep-merges unknown input over the defaults; unknown keys dropped, invalid values replaced. Accepts the legacy flat\n * `{ headingConversionScope }` shape of the per-browser settings before version 1. Always returns fresh objects.\n */\nexport function parseEditorSettings(input: unknown): EditorSettings {\n const root = record(input)\n const editing = record(root.editing)\n const ai = record(root.ai)\n const slots = record(ai.slots)\n const blocks = record(root.blocks)\n const appearance = record(root.appearance)\n const defaults = EDITOR_SETTINGS_DEFAULTS\n const legacyScope = valid(headingConversionScopeSchema, root.headingConversionScope, defaults.editing.headingConversionScope)\n return {\n version: EDITOR_SETTINGS_VERSION,\n editing: {\n headingConversionScope: valid(headingConversionScopeSchema, editing.headingConversionScope, legacyScope),\n authorColors: valid(authorColorsSchema, editing.authorColors, defaults.editing.authorColors),\n authorColorStyle: valid(authorColorStyleSchema, editing.authorColorStyle, defaults.editing.authorColorStyle),\n commentsLayout: valid(commentsLayoutSchema, editing.commentsLayout, defaults.editing.commentsLayout),\n },\n ai: {\n autocomplete: valid(autocompleteSchema, ai.autocomplete, defaults.ai.autocomplete),\n slots: Object.fromEntries(MODEL_SLOTS.flatMap((slot) => {\n const parsed = profileIdSchema.safeParse(slots[slot])\n return parsed.success ? [[slot, parsed.data]] : []\n })),\n translateLanguage: valid(translateLanguageSchema, ai.translateLanguage, defaults.ai.translateLanguage),\n },\n blocks: {\n defaultKind: valid(defaultKindSchema, blocks.defaultKind, defaults.blocks.defaultKind),\n structure: valid(structureSchema, blocks.structure, defaults.blocks.structure),\n treeRoot: valid(treeRootSchema, blocks.treeRoot, defaults.blocks.treeRoot),\n kindIcon: valid(kindIconSchema, blocks.kindIcon, defaults.blocks.kindIcon),\n dragHandle: valid(dragHandleSchema, blocks.dragHandle, defaults.blocks.dragHandle),\n splitTargetChars: valid(splitTargetCharsSchema, blocks.splitTargetChars, defaults.blocks.splitTargetChars),\n },\n appearance: {\n theme: valid(themeSchema, appearance.theme, defaults.appearance.theme),\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/settings/index.ts"],"sourcesContent":["import { z } from 'zod'\nimport {\n AI_LANGUAGES,\n type AiLanguage,\n AUTHOR_COLOR_STYLES,\n type AuthorColorStyle,\n BLOCK_TYPE_ICON_VISIBILITIES,\n type BlockTypeIconVisibility,\n COMMENTS_LAYOUTS,\n type CommentsLayout,\n DRAG_HANDLE_VISIBILITIES,\n type DragHandleVisibility,\n HEADING_CONVERSION_SCOPES,\n type HeadingConversionScope,\n MODEL_ROLES,\n type ModelRole,\n STRUCTURE_MODES,\n type StructureMode,\n TREE_ROOTS,\n type TreeRoot,\n} from './values'\nimport { EDITOR_SETTINGS_VERSION, migrateEditorSettings } from './migrate'\n\nexport * from './values'\nexport { EDITOR_SETTINGS_VERSION, isOutdatedEditorSettings, migrateEditorSettings } from './migrate'\n\n/** Valid theme id: lowercase slug, used as directory name and in `/api/editor/themes/:id`. Defined here so core never imports a feature. */\nexport const THEME_ID = /^[a-z0-9][a-z0-9-]{0,63}$/\n\n/** A user's editor preferences, shared by all editors on the page; persisted per user through the `settings` port. */\nexport interface EditorSettings {\n version: typeof EDITOR_SETTINGS_VERSION\n editing: {\n /** Blocks „Gliederung umwandeln“ (both directions) converts when nothing is selected. */\n headingConversionScope: HeadingConversionScope\n /** Text is tinted per author (authors feature): each run in the colour of the user who wrote it. */\n authorColors: boolean\n /** `background`: tinted background; `text`: text in the author's colour. */\n authorColorStyle: AuthorColorStyle\n /** `margin`: comment cards in a column beside the text; `inline`: popover at the text plus the toolbar's list (comments feature). */\n commentsLayout: CommentsLayout\n }\n ai: {\n /** Ghost-text suggestions while typing (`GhostAutocomplete`). */\n autocomplete: boolean\n /** Model profile id per model role; a missing or unknown id falls back to the server's default preset for the role. */\n roles: Partial<Record<ModelRole, string>>\n /** Preselected target language of „Übersetzen“. */\n translateLanguage: AiLanguage\n }\n blocks: {\n /** Block type id the Typed block shortcuts create (toolbar button, heading conversion). */\n defaultBlockType: string\n /** Preferred document structure: toolbar block types and the structure of AI-generated blocks. */\n structure: StructureMode\n /** Root of the document drawer while a Document with Typed blocks is loaded: nested in the folders, or the file itself. */\n treeRoot: TreeRoot\n /** Block type icon of Typed blocks (their drag handle): always visible, or only on hover, focus or while the details are open. */\n blockTypeIcon: BlockTypeIconVisibility\n /** Standard drag handle of blocks without Block type icon: on the hovered block, or permanently beside every block. */\n dragHandle: DragHandleVisibility\n /** Target size in characters of the parts a length split cuts a Typed block into (200–20000). */\n splitTargetChars: number\n }\n appearance: {\n /** Id of the Obsidian theme applied to the whole app (themes feature); `null` = the standard Nuxt UI look. */\n theme: string | null\n }\n}\n\n/** Bounds of `blocks.splitTargetChars`. */\nexport const SPLIT_TARGET_CHARS_MIN = 200\nexport const SPLIT_TARGET_CHARS_MAX = 20000\n\n/** The split target typed into the field as a valid setting: rounded and clamped to its bounds; `current` when `typed` is empty or not a number. */\nexport function clampSplitTargetChars(typed: string, current: number): number {\n const value = Math.round(Number(typed))\n return typed.trim() !== '' && Number.isFinite(value) ? Math.min(SPLIT_TARGET_CHARS_MAX, Math.max(SPLIT_TARGET_CHARS_MIN, value)) : current\n}\n\nexport const EDITOR_SETTINGS_DEFAULTS: EditorSettings = {\n version: EDITOR_SETTINGS_VERSION,\n editing: { headingConversionScope: 'container', authorColors: false, authorColorStyle: 'background', commentsLayout: 'margin' },\n ai: { autocomplete: false, roles: {}, translateLanguage: 'Englisch' },\n blocks: { defaultBlockType: 'section', structure: 'structured', treeRoot: 'folders', blockTypeIcon: 'always', dragHandle: 'hover', splitTargetChars: 1500 },\n appearance: { theme: null },\n}\n\nconst headingConversionScopeSchema = z.enum(HEADING_CONVERSION_SCOPES)\nconst authorColorsSchema = z.boolean()\nconst authorColorStyleSchema = z.enum(AUTHOR_COLOR_STYLES)\nconst commentsLayoutSchema = z.enum(COMMENTS_LAYOUTS)\nconst autocompleteSchema = z.boolean()\nconst profileIdSchema = z.string().min(1).max(100)\nconst translateLanguageSchema = z.enum(AI_LANGUAGES)\nconst defaultBlockTypeSchema = z.string().min(1).max(100)\nconst structureSchema = z.enum(STRUCTURE_MODES)\nconst treeRootSchema = z.enum(TREE_ROOTS)\nconst blockTypeIconSchema = z.enum(BLOCK_TYPE_ICON_VISIBILITIES)\nconst dragHandleSchema = z.enum(DRAG_HANDLE_VISIBILITIES)\nconst splitTargetCharsSchema = z.number().int().min(SPLIT_TARGET_CHARS_MIN).max(SPLIT_TARGET_CHARS_MAX)\nconst themeSchema = z.string().regex(THEME_ID).nullable()\n\n/** Complete, valid settings (the body of `PUT /api/editor/settings`). */\nexport const editorSettingsSchema = z.object({\n version: z.literal(EDITOR_SETTINGS_VERSION),\n editing: z.object({\n headingConversionScope: headingConversionScopeSchema,\n authorColors: authorColorsSchema,\n authorColorStyle: authorColorStyleSchema,\n commentsLayout: commentsLayoutSchema,\n }),\n ai: z.object({\n autocomplete: autocompleteSchema,\n roles: z.partialRecord(z.enum(MODEL_ROLES), profileIdSchema),\n translateLanguage: translateLanguageSchema,\n }),\n blocks: z.object({\n defaultBlockType: defaultBlockTypeSchema,\n structure: structureSchema,\n treeRoot: treeRootSchema,\n blockTypeIcon: blockTypeIconSchema,\n dragHandle: dragHandleSchema,\n splitTargetChars: splitTargetCharsSchema,\n }),\n appearance: z.object({ theme: themeSchema }),\n}) satisfies z.ZodType<EditorSettings>\n\nfunction record(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\nfunction valid<T>(schema: z.ZodType<T>, value: unknown, fallback: T): T {\n const parsed = schema.safeParse(value)\n return parsed.success ? parsed.data : fallback\n}\n\n/**\n * Deep-merges unknown input over the defaults; unknown keys dropped, invalid values replaced. Reads older stored settings\n * through `migrateEditorSettings` (`./migrate`), including the legacy flat `{ headingConversionScope }` per-browser shape.\n * Always returns fresh objects of the current version.\n */\nexport function parseEditorSettings(input: unknown): EditorSettings {\n const root = record(migrateEditorSettings(input))\n const editing = record(root.editing)\n const ai = record(root.ai)\n const roles = record(ai.roles)\n const blocks = record(root.blocks)\n const appearance = record(root.appearance)\n const defaults = EDITOR_SETTINGS_DEFAULTS\n const legacyScope = valid(headingConversionScopeSchema, root.headingConversionScope, defaults.editing.headingConversionScope)\n return {\n version: EDITOR_SETTINGS_VERSION,\n editing: {\n headingConversionScope: valid(headingConversionScopeSchema, editing.headingConversionScope, legacyScope),\n authorColors: valid(authorColorsSchema, editing.authorColors, defaults.editing.authorColors),\n authorColorStyle: valid(authorColorStyleSchema, editing.authorColorStyle, defaults.editing.authorColorStyle),\n commentsLayout: valid(commentsLayoutSchema, editing.commentsLayout, defaults.editing.commentsLayout),\n },\n ai: {\n autocomplete: valid(autocompleteSchema, ai.autocomplete, defaults.ai.autocomplete),\n roles: Object.fromEntries(MODEL_ROLES.flatMap((role) => {\n const parsed = profileIdSchema.safeParse(roles[role])\n return parsed.success ? [[role, parsed.data]] : []\n })),\n translateLanguage: valid(translateLanguageSchema, ai.translateLanguage, defaults.ai.translateLanguage),\n },\n blocks: {\n defaultBlockType: valid(defaultBlockTypeSchema, blocks.defaultBlockType, defaults.blocks.defaultBlockType),\n structure: valid(structureSchema, blocks.structure, defaults.blocks.structure),\n treeRoot: valid(treeRootSchema, blocks.treeRoot, defaults.blocks.treeRoot),\n blockTypeIcon: valid(blockTypeIconSchema, blocks.blockTypeIcon, defaults.blocks.blockTypeIcon),\n dragHandle: valid(dragHandleSchema, blocks.dragHandle, defaults.blocks.dragHandle),\n splitTargetChars: valid(splitTargetCharsSchema, blocks.splitTargetChars, defaults.blocks.splitTargetChars),\n },\n appearance: {\n theme: valid(themeSchema, appearance.theme, defaults.appearance.theme),\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/settings/migrate.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Version 2 renamed `ai.slots` to `ai.roles`, `blocks.defaultKind` to `blocks.defaultBlockType`, `blocks.kindIcon` to
|
|
4
|
+
* `blocks.blockTypeIcon` and the `blocks.structure` value `'composite'` to `'structured'` (`migrateEditorSettings`).
|
|
5
|
+
*/
|
|
6
|
+
export declare const EDITOR_SETTINGS_VERSION = 2;
|
|
7
|
+
/**
|
|
8
|
+
* Maps stored settings of version 1 (and the unversioned flat per-browser shape before it) to version 2: `ai.slots` →
|
|
9
|
+
* `ai.roles`, `blocks.defaultKind` → `blocks.defaultBlockType`, `blocks.kindIcon` → `blocks.blockTypeIcon`, the
|
|
10
|
+
* `blocks.structure` value `'composite'` → `'structured'`. A key that is already new wins over the old one; unknown keys
|
|
11
|
+
* and values pass through (`parseEditorSettings` validates). Input of version 2 or later, and anything that is not an
|
|
12
|
+
* object, is returned as it is, so the migration is idempotent. Never mutates `input`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function migrateEditorSettings(input: unknown): unknown;
|
|
15
|
+
/** True when `stored` is a settings record older than `EDITOR_SETTINGS_VERSION`: it is written back once, migrated (`migrateEditorSettings`). */
|
|
16
|
+
export declare function isOutdatedEditorSettings(stored: unknown): boolean;
|
|
17
|
+
//#endregion
|
|
18
|
+
//# sourceMappingURL=migrate.d.ts.map
|