@ecsplico/editor 2.1.2 → 3.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 +68 -1
- package/README.md +39 -156
- package/dist/adapters/comments/drizzle.d.ts +1 -1
- package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
- package/dist/adapters/comments/rest-model.js.map +1 -0
- package/dist/adapters/comments/types.d.ts +2 -2
- package/dist/adapters/document-state.d.ts +23 -0
- package/dist/adapters/document-state.js +66 -0
- package/dist/adapters/document-state.js.map +1 -0
- package/dist/adapters/documents.js +14 -11
- package/dist/adapters/documents.js.map +1 -1
- package/dist/adapters/drizzle.d.ts +2 -2
- package/dist/adapters/drizzle.js +97 -88
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/adapters/forge.d.ts +12 -7
- package/dist/adapters/forge.js +38 -45
- package/dist/adapters/forge.js.map +1 -1
- package/dist/adapters/http.d.ts +20 -0
- package/dist/adapters/http.js +68 -0
- package/dist/adapters/http.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/json.d.ts +3 -3
- package/dist/adapters/json.js +58 -49
- package/dist/adapters/json.js.map +1 -1
- package/dist/adapters/memory.d.ts +21 -0
- package/dist/adapters/memory.js +53 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/nitro.d.ts +23 -20
- package/dist/adapters/nitro.js +58 -163
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/react.d.ts +55 -0
- package/dist/adapters/react.js +209 -0
- package/dist/adapters/react.js.map +1 -0
- package/dist/adapters/shared.d.ts +15 -21
- package/dist/adapters/shared.js +10 -8
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/vue.d.ts +15 -30
- package/dist/adapters/vue.js +36 -87
- package/dist/adapters/vue.js.map +1 -1
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.js +95 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/abilities.js +1 -0
- package/dist/core/abilities.js.map +1 -1
- package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
- package/dist/core/api-routes.js.map +1 -0
- package/dist/core/backend-capability.d.ts +7 -0
- package/dist/core/backend-capability.js +17 -0
- package/dist/core/backend-capability.js.map +1 -0
- package/dist/core/backend-types.d.ts +245 -0
- package/dist/core/backend-types.js +17 -0
- package/dist/core/backend-types.js.map +1 -0
- package/dist/core/backend.d.ts +108 -0
- package/dist/core/backend.js +14 -0
- package/dist/core/backend.js.map +1 -0
- package/dist/core/block-convert.d.ts +7 -5
- package/dist/core/block-convert.js +28 -5
- package/dist/core/block-convert.js.map +1 -1
- package/dist/core/command-types.d.ts +89 -0
- package/dist/core/commands.d.ts +40 -0
- package/dist/core/commands.js +151 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/context.d.ts +17 -20
- package/dist/core/context.js +24 -21
- package/dist/core/context.js.map +1 -1
- package/dist/core/core-feature.d.ts +22 -7
- package/dist/core/core-feature.js +51 -18
- package/dist/core/core-feature.js.map +1 -1
- package/dist/core/define-editor.d.ts +4 -3
- package/dist/core/define-editor.js +14 -7
- package/dist/core/define-editor.js.map +1 -1
- package/dist/core/document-markdown.js +12 -3
- package/dist/core/document-markdown.js.map +1 -1
- package/dist/core/editor-ui.d.ts +8 -1
- package/dist/core/editor-ui.js +9 -8
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/extension-points.d.ts +3 -15
- package/dist/core/feature-context.d.ts +8 -7
- package/dist/core/feature-context.js +12 -11
- package/dist/core/feature-context.js.map +1 -1
- package/dist/core/feature.d.ts +35 -19
- package/dist/core/feature.js.map +1 -1
- package/dist/core/fences.d.ts +2 -1
- package/dist/core/fences.js +3 -2
- package/dist/core/fences.js.map +1 -1
- package/dist/core/fetch-error.d.ts +1 -1
- package/dist/core/fetch-error.js +3 -1
- package/dist/core/fetch-error.js.map +1 -1
- package/dist/core/hash.d.ts +5 -0
- package/dist/core/hash.js +11 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/http-backend.d.ts +11 -0
- package/dist/core/http-backend.js +195 -0
- package/dist/core/http-backend.js.map +1 -0
- package/dist/core/index.d.ts +14 -9
- package/dist/core/index.js +12 -9
- package/dist/core/palette.d.ts +48 -0
- package/dist/core/palette.js +95 -0
- package/dist/core/palette.js.map +1 -0
- package/dist/core/schema/abilities.d.ts +2 -1
- package/dist/core/schema/abilities.js +5 -1
- package/dist/core/schema/abilities.js.map +1 -1
- package/dist/core/schema/index.d.ts +11 -5
- package/dist/core/schema/index.js +13 -8
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/schema/mark-flags.d.ts +8 -1
- package/dist/core/schema/of-features.d.ts +13 -0
- package/dist/core/schema/of-features.js +14 -0
- package/dist/core/schema/of-features.js.map +1 -0
- package/dist/core/slash.d.ts +8 -6
- package/dist/core/slash.js +24 -16
- package/dist/core/slash.js.map +1 -1
- package/dist/core/surface-session.js +13 -5
- package/dist/core/surface-session.js.map +1 -1
- package/dist/core/toolbar-items.d.ts +10 -4
- package/dist/core/toolbar-items.js +269 -195
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/core/util.js +11 -0
- package/dist/core/util.js.map +1 -0
- package/dist/document/DocumentSurface.d.ts +1 -1
- package/dist/features/ai/agent-op-review.d.ts +1 -1
- package/dist/features/ai/agent-op-review.js +1 -1
- package/dist/features/ai/agent-ops.js +1 -1
- package/dist/features/ai/ai-apply.d.ts +2 -2
- package/dist/features/ai/ai-apply.js +1 -1
- package/dist/features/ai/ai-review.js +1 -1
- package/dist/features/ai/ai-sse.d.ts +6 -6
- package/dist/features/ai/ai-sse.js +20 -63
- package/dist/features/ai/ai-sse.js.map +1 -1
- package/dist/features/ai/assistant.d.ts +8 -7
- package/dist/features/ai/assistant.js +3 -3
- package/dist/features/ai/assistant.js.map +1 -1
- package/dist/features/ai/block-actions.d.ts +3 -2
- package/dist/features/ai/block-actions.js +3 -2
- package/dist/features/ai/block-actions.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +1 -1
- package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
- package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/document-chat-model.d.ts +8 -7
- 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 +1 -1
- package/dist/features/ai/feature.js +27 -14
- package/dist/features/ai/feature.js.map +1 -1
- package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
- package/dist/features/ai/ghost-autocomplete.js +6 -9
- package/dist/features/ai/ghost-autocomplete.js.map +1 -1
- package/dist/features/ai/index.d.ts +4 -3
- package/dist/features/ai/index.js +10 -10
- package/dist/features/ai/model-profiles.d.ts +5 -4
- package/dist/features/ai/model-profiles.js +2 -2
- package/dist/features/ai/model-profiles.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.js +1 -1
- package/dist/features/ai/protocol/block-lines.js.map +1 -1
- package/dist/features/ai/protocol/chat.js +4 -1
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -2
- package/dist/features/ai/protocol/models.d.ts +2 -16
- package/dist/features/ai/protocol/models.js.map +1 -1
- package/dist/features/ai/rate-limit.d.ts +1 -1
- package/dist/features/ai/rate-limit.js +3 -1
- package/dist/features/ai/rate-limit.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js +2 -2
- package/dist/features/ai/react/AiSidebar.js +4 -4
- package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
- package/dist/features/ai/react/DocumentChatOps.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
- 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 +3 -3
- package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/react/use-document-chat.js +2 -2
- package/dist/features/ai/react/use-document-chat.js.map +1 -1
- package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
- package/dist/features/ai/selection-flow.d.ts +1 -1
- package/dist/features/ai/selection-flow.js +1 -1
- package/dist/features/ai/sidebar-flow.d.ts +1 -1
- package/dist/features/ai/sidebar-flow.js +1 -1
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-ai-assistant.js +3 -3
- package/dist/features/ai/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/use-document-chat.js +2 -2
- package/dist/features/ai/use-document-chat.js.map +1 -1
- package/dist/features/ai/use-model-profiles.d.ts +2 -1
- package/dist/features/authors/attribution.d.ts +1 -1
- package/dist/features/authors/feature.js +12 -11
- package/dist/features/authors/feature.js.map +1 -1
- package/dist/features/block-ui/block-actions.d.ts +1 -1
- package/dist/features/block-ui/block-actions.js +1 -1
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/components/BlockUi.js +1 -1
- package/dist/features/block-ui/feature.js +1 -0
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.js +1 -1
- package/dist/features/collab/anchor-resolver.js +1 -1
- package/dist/features/collab/feature.js +2 -0
- package/dist/features/collab/feature.js.map +1 -1
- package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
- package/dist/features/comments/feature.js +12 -11
- package/dist/features/comments/feature.js.map +1 -1
- package/dist/features/comments/index.d.ts +3 -2
- package/dist/features/comments/index.js +1 -1
- package/dist/features/comments/margin-layout.d.ts +3 -2
- package/dist/features/comments/model.d.ts +3 -67
- package/dist/features/comments/model.js +2 -2
- package/dist/features/comments/model.js.map +1 -1
- package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
- package/dist/features/comments/react/use-comments.d.ts +2 -1
- package/dist/features/comments/react.d.ts +1 -1
- package/dist/features/comments/schema.d.ts +2 -52
- package/dist/features/comments/schema.js.map +1 -1
- package/dist/features/comments/snapshot-equality.js.map +1 -1
- package/dist/features/comments/use-comments.d.ts +3 -2
- package/dist/features/comments/vue.d.ts +1 -1
- package/dist/features/composites/accepted-children.d.ts +1 -0
- package/dist/features/composites/accepted-children.js +16 -1
- package/dist/features/composites/accepted-children.js.map +1 -1
- package/dist/features/composites/ai-brief.js +31 -0
- package/dist/features/composites/ai-brief.js.map +1 -1
- package/dist/features/composites/bibliography-command.d.ts +11 -0
- package/dist/features/composites/bibliography-form.js +90 -0
- package/dist/features/composites/bibliography-form.js.map +1 -0
- package/dist/features/composites/bibliography.d.ts +33 -0
- package/dist/features/composites/bibliography.js +120 -0
- package/dist/features/composites/bibliography.js.map +1 -0
- package/dist/features/composites/components/BibliographyEditor.js +261 -0
- package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/components/CompositeChrome.js +75 -26
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.js +46 -11
- package/dist/features/composites/components/CompositeDetails.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
- package/dist/features/composites/components/CompositeFieldEditor.js +37 -4
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeInspector.js +17 -5
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/components/CompositeTable.js +13 -4
- package/dist/features/composites/components/CompositeTable.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/components/CompositeView.js +11 -1
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +103 -0
- package/dist/features/composites/components/DerivationBadge.js.map +1 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.js +122 -0
- package/dist/features/composites/components/ReferenceView.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
- package/dist/features/composites/components/SettingsEditingTab.js +1 -1
- package/dist/features/composites/convert.js +2 -2
- package/dist/features/composites/convert.js.map +1 -1
- package/dist/features/composites/derivation.d.ts +61 -0
- package/dist/features/composites/derivation.js +98 -0
- package/dist/features/composites/derivation.js.map +1 -0
- package/dist/features/composites/derived-chrome.js +69 -0
- package/dist/features/composites/derived-chrome.js.map +1 -0
- package/dist/features/composites/feature.js +159 -109
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/fences.d.ts +4 -3
- package/dist/features/composites/fences.js +51 -7
- package/dist/features/composites/fences.js.map +1 -1
- package/dist/features/composites/index.d.ts +12 -3
- package/dist/features/composites/index.js +15 -6
- package/dist/features/composites/inspector-model.d.ts +4 -1
- package/dist/features/composites/inspector-model.js +8 -4
- package/dist/features/composites/inspector-model.js.map +1 -1
- package/dist/features/composites/kinds.d.ts +8 -1
- package/dist/features/composites/kinds.js +145 -6
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/legacy.d.ts +4 -1
- package/dist/features/composites/legacy.js +56 -1
- package/dist/features/composites/legacy.js.map +1 -1
- package/dist/features/composites/meta-display.js +40 -1
- package/dist/features/composites/meta-display.js.map +1 -1
- package/dist/features/composites/meta-keys.d.ts +7 -0
- package/dist/features/composites/meta-keys.js +21 -0
- package/dist/features/composites/meta-keys.js.map +1 -0
- package/dist/features/composites/node-views.d.ts +6 -3
- package/dist/features/composites/node-views.js +7 -3
- package/dist/features/composites/node-views.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +6 -1
- package/dist/features/composites/nodes.js +33 -7
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/react/BibliographyEditor.js +229 -0
- package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/react/CompositeChrome.js +65 -17
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
- package/dist/features/composites/react/CompositeDetails.js +45 -6
- package/dist/features/composites/react/CompositeDetails.js.map +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.js +24 -1
- package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeInspector.js +11 -5
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/react/CompositeTable.js +16 -2
- package/dist/features/composites/react/CompositeTable.js.map +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
- package/dist/features/composites/react/CompositeView.js +6 -1
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +116 -0
- package/dist/features/composites/react/DerivationBadge.js.map +1 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/react/ReferenceView.d.ts +11 -0
- package/dist/features/composites/react/ReferenceView.js +115 -0
- package/dist/features/composites/react/ReferenceView.js.map +1 -0
- package/dist/features/composites/react/SettingsEditingTab.js +2 -2
- package/dist/features/composites/react.d.ts +2 -1
- package/dist/features/composites/react.js +7 -3
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/reference-ai.js +16 -0
- package/dist/features/composites/reference-ai.js.map +1 -0
- package/dist/features/composites/reference-label.d.ts +19 -0
- package/dist/features/composites/reference-label.js +58 -0
- package/dist/features/composites/reference-label.js.map +1 -0
- package/dist/features/composites/reference-suggestion.d.ts +10 -0
- package/dist/features/composites/reference-suggestion.js +41 -0
- package/dist/features/composites/reference-suggestion.js.map +1 -0
- package/dist/features/composites/reference-text.d.ts +18 -0
- package/dist/features/composites/reference-text.js +89 -0
- package/dist/features/composites/reference-text.js.map +1 -0
- package/dist/features/composites/reference.d.ts +44 -0
- package/dist/features/composites/reference.js +148 -0
- package/dist/features/composites/reference.js.map +1 -0
- package/dist/features/composites/reveal-source.d.ts +32 -0
- package/dist/features/composites/reveal-source.js +89 -0
- package/dist/features/composites/reveal-source.js.map +1 -0
- package/dist/features/composites/rows.d.ts +21 -2
- package/dist/features/composites/rows.js +63 -9
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/features/composites/schema.d.ts +9 -3
- package/dist/features/composites/schema.js +11 -4
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/table-model.js +12 -21
- package/dist/features/composites/table-model.js.map +1 -1
- package/dist/features/composites/vue.d.ts +3 -2
- package/dist/features/composites/vue.js +8 -4
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/media/assets.d.ts +2 -0
- package/dist/features/media/assets.js +3 -2
- package/dist/features/media/assets.js.map +1 -1
- package/dist/features/media/components/FileView.js +13 -4
- package/dist/features/media/components/FileView.js.map +1 -1
- package/dist/features/media/feature.d.ts +3 -3
- package/dist/features/media/feature.js +21 -28
- package/dist/features/media/feature.js.map +1 -1
- package/dist/features/media/file-import.js +2 -2
- package/dist/features/media/file-import.js.map +1 -1
- package/dist/features/media/index.d.ts +2 -2
- package/dist/features/media/index.js +2 -2
- package/dist/features/media/react/FileView.js +19 -2
- package/dist/features/media/react/FileView.js.map +1 -1
- package/dist/features/media/schema.d.ts +8 -0
- package/dist/features/media/schema.js +126 -11
- package/dist/features/media/schema.js.map +1 -1
- package/dist/features/media/upload.d.ts +16 -14
- package/dist/features/media/upload.js +13 -19
- package/dist/features/media/upload.js.map +1 -1
- package/dist/features/mentions/index.d.ts +4 -3
- package/dist/features/mentions/index.js +4 -1
- package/dist/features/mentions/index.js.map +1 -1
- package/dist/features/mentions/mention-text.d.ts +1 -1
- package/dist/features/mentions/mention-text.js.map +1 -1
- package/dist/features/mentions/mention.d.ts +0 -6
- package/dist/features/mentions/mention.js.map +1 -1
- package/dist/features/mentions/metadata-defaults.d.ts +2 -2
- package/dist/features/mentions/metadata-defaults.js +3 -9
- package/dist/features/mentions/metadata-defaults.js.map +1 -1
- package/dist/features/mentions/schema.d.ts +2 -1
- package/dist/features/mentions/schema.js.map +1 -1
- package/dist/features/tables/feature.d.ts +2 -1
- package/dist/features/tables/feature.js +90 -70
- package/dist/features/tables/feature.js.map +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
- package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/feature.js +2 -0
- package/dist/features/themes/feature.js.map +1 -1
- package/dist/features/themes/index.d.ts +2 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
- package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/schema.d.ts +2 -6
- package/dist/features/themes/schema.js.map +1 -1
- package/dist/features/themes/theme-runtime.d.ts +1 -1
- package/dist/features/themes/theme-runtime.js +4 -2
- package/dist/features/themes/theme-runtime.js.map +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js +48 -28
- package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/feature.js +21 -19
- package/dist/features/transcript/feature.js.map +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
- package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/segment-node.d.ts +2 -0
- package/dist/features/transcript/segment-node.js +35 -7
- package/dist/features/transcript/segment-node.js.map +1 -1
- package/dist/i18n/de/composites.js +91 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/integrations.js +49 -8
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/media.js +0 -1
- package/dist/i18n/de/media.js.map +1 -1
- package/dist/i18n/de/palette.js +22 -0
- package/dist/i18n/de/palette.js.map +1 -0
- package/dist/i18n/de/tables.js +0 -1
- package/dist/i18n/de/tables.js.map +1 -1
- package/dist/i18n/de/transcript.js +2 -1
- package/dist/i18n/de/transcript.js.map +1 -1
- package/dist/i18n/de/tree.js +3 -0
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +157 -11
- package/dist/i18n/de.js +3 -1
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en/composites.js +89 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/integrations.js +49 -8
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/media.js +0 -1
- package/dist/i18n/en/media.js.map +1 -1
- package/dist/i18n/en/palette.js +22 -0
- package/dist/i18n/en/palette.js.map +1 -0
- package/dist/i18n/en/tables.js +0 -1
- package/dist/i18n/en/tables.js.map +1 -1
- package/dist/i18n/en/transcript.js +2 -1
- package/dist/i18n/en/transcript.js.map +1 -1
- package/dist/i18n/en/tree.js +3 -0
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/i18n/en.js +3 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
- package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
- package/dist/integrations/composite-ai/composite-fill.js +97 -56
- package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
- package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
- package/dist/integrations/composite-ai/composite-summary.js +7 -26
- package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +29 -0
- package/dist/integrations/composite-ai/derive.js +173 -0
- package/dist/integrations/composite-ai/derive.js.map +1 -0
- package/dist/integrations/composite-ai/index.d.ts +11 -7
- package/dist/integrations/composite-ai/index.js +72 -14
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/rendition.d.ts +45 -0
- package/dist/integrations/composite-ai/rendition.js +314 -0
- package/dist/integrations/composite-ai/rendition.js.map +1 -0
- package/dist/integrations/composite-media/index.d.ts +7 -5
- package/dist/integrations/composite-media/index.js +86 -13
- package/dist/integrations/composite-media/index.js.map +1 -1
- package/dist/integrations/transcript-ai/index.d.ts +1 -1
- package/dist/integrations/transcript-ai/index.js +5 -2
- package/dist/integrations/transcript-ai/index.js.map +1 -1
- package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
- package/dist/integrations/transcript-ai/protocol.js +0 -2
- package/dist/integrations/transcript-ai/protocol.js.map +1 -1
- package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
- package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
- package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +6 -5
- package/dist/markdown/MarkdownSurface2.js +3 -6
- package/dist/markdown/MarkdownSurface2.js.map +1 -1
- package/dist/markdown/document.js +1 -1
- package/dist/markdown/source-mode.d.ts +1 -1
- package/dist/nuxt/module.d.ts +1402 -54
- package/dist/nuxt/module.js +6 -49
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/install-context.d.ts +7 -3
- package/dist/nuxt/runtime/install-context.js +14 -8
- package/dist/nuxt/runtime/install-context.js.map +1 -1
- package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
- package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
- package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
- package/dist/presets/compose.d.ts +94 -0
- package/dist/presets/compose.js +164 -0
- package/dist/presets/compose.js.map +1 -0
- package/dist/react/AdvancedEditor.d.ts +15 -15
- package/dist/react/AdvancedEditor.js +15 -13
- package/dist/react/AdvancedEditor.js.map +1 -1
- package/dist/react/EditorCommandPalette.d.ts +13 -0
- package/dist/react/EditorCommandPalette.js +118 -0
- package/dist/react/EditorCommandPalette.js.map +1 -0
- package/dist/react/EditorSettingsModal.d.ts +5 -5
- package/dist/react/EditorSettingsModal.js +7 -4
- package/dist/react/EditorSettingsModal.js.map +1 -1
- package/dist/react/EditorSettingsPanel.d.ts +7 -7
- package/dist/react/EditorSettingsPanel.js +8 -5
- package/dist/react/EditorSettingsPanel.js.map +1 -1
- package/dist/react/EditorSurface.d.ts +7 -7
- package/dist/react/EditorSurface.js +10 -6
- package/dist/react/EditorSurface.js.map +1 -1
- package/dist/react/EditorToolbar.d.ts +4 -6
- package/dist/react/EditorToolbar.js +29 -37
- package/dist/react/EditorToolbar.js.map +1 -1
- package/dist/react/SimpleEditor.d.ts +22 -23
- package/dist/react/SimpleEditor.js +21 -7
- package/dist/react/SimpleEditor.js.map +1 -1
- package/dist/react/advanced.d.ts +4 -3
- package/dist/react/advanced.js +2 -2
- package/dist/react/compose.d.ts +20 -0
- package/dist/react/compose.js +49 -0
- package/dist/react/compose.js.map +1 -0
- package/dist/react/composed-surface.d.ts +39 -0
- package/dist/react/composed-surface.js +71 -0
- package/dist/react/composed-surface.js.map +1 -0
- package/dist/react/context.js +6 -2
- package/dist/react/context.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +2 -1
- package/dist/react/key-labels.js +15 -0
- package/dist/react/key-labels.js.map +1 -0
- package/dist/react/settings.d.ts +1 -1
- package/dist/react/simple.d.ts +2 -2
- package/dist/react/ui/command.js +36 -1
- package/dist/react/ui/command.js.map +1 -1
- package/dist/schema/advanced.d.ts +6 -6
- package/dist/schema/advanced.js +26 -29
- package/dist/schema/advanced.js.map +1 -1
- package/dist/server/ai.d.ts +5 -16
- package/dist/server/ai.js.map +1 -1
- package/dist/server/auth.d.ts +9 -0
- package/dist/server/collab-state.d.ts +1 -1
- package/dist/server/collab-state.js.map +1 -1
- package/dist/server/collab.d.ts +13 -13
- package/dist/server/collab.js +34 -18
- package/dist/server/collab.js.map +1 -1
- package/dist/server/crossref.js +78 -0
- package/dist/server/crossref.js.map +1 -0
- package/dist/server/errors.d.ts +1 -1
- package/dist/server/errors.js +1 -1
- package/dist/server/errors.js.map +1 -1
- package/dist/server/extract.js +308 -0
- package/dist/server/extract.js.map +1 -0
- package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
- package/dist/server/h3.js.map +1 -0
- package/dist/server/handlers/ai.d.ts +30 -9
- package/dist/server/handlers/ai.js +170 -36
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +12 -5
- package/dist/server/handlers/context.js +10 -4
- package/dist/server/handlers/context.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +19 -13
- package/dist/server/handlers/editor.js +49 -38
- package/dist/server/handlers/editor.js.map +1 -1
- package/dist/server/handlers/index.d.ts +4 -4
- package/dist/server/handlers/index.js +4 -4
- package/dist/server/host.d.ts +16 -25
- package/dist/server/host.js +58 -41
- package/dist/server/host.js.map +1 -1
- package/dist/server/index.d.ts +6 -4
- package/dist/server/index.js +3 -2
- package/dist/server/pdf.js +97 -0
- package/dist/server/pdf.js.map +1 -0
- package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
- package/dist/server/ports.js +29 -0
- package/dist/server/ports.js.map +1 -0
- package/dist/server/routes.js +144 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/styles/composites.css +25 -0
- package/dist/tree/DocumentTree.js +56 -29
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +23 -0
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +2 -2
- package/dist/tree/tree-items.js +1 -1
- package/dist/vue/AdvancedEditor.js +30 -9
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +28 -8
- package/dist/vue/SimpleEditor.js +62 -16
- package/dist/vue/SimpleEditor.js.map +1 -1
- package/dist/vue/SimpleEditor.vue.d.ts +37 -7
- package/dist/vue/advanced.d.ts +3 -2
- package/dist/vue/advanced.js +2 -2
- package/dist/vue/components/EditorCommandPalette.js +91 -0
- package/dist/vue/components/EditorCommandPalette.js.map +1 -0
- package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
- package/dist/vue/components/EditorSettingsModal.js +10 -6
- package/dist/vue/components/EditorSettingsModal.js.map +1 -1
- package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
- package/dist/vue/components/EditorSettingsPanel.js +10 -6
- package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
- package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
- package/dist/vue/components/EditorSurface.js +11 -6
- package/dist/vue/components/EditorSurface.js.map +1 -1
- package/dist/vue/components/EditorSurface.vue.d.ts +7 -7
- package/dist/vue/components/EditorToolbar.js +41 -49
- package/dist/vue/components/EditorToolbar.js.map +1 -1
- package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
- package/dist/vue/compose.d.ts +17 -0
- package/dist/vue/compose.js +51 -0
- package/dist/vue/compose.js.map +1 -0
- package/dist/vue/context-key.d.ts +6 -0
- package/dist/vue/context-key.js +6 -0
- package/dist/vue/context-key.js.map +1 -0
- package/dist/vue/context.d.ts +8 -6
- package/dist/vue/context.js +6 -5
- package/dist/vue/context.js.map +1 -1
- package/dist/vue/index.d.ts +10 -8
- package/dist/vue/index.js +10 -8
- package/dist/vue/kit/index.d.ts +2 -2
- package/dist/vue/kit/nuxt-ui.js +23 -0
- package/dist/vue/kit/nuxt-ui.js.map +1 -1
- package/dist/vue/kit/types.d.ts +40 -1
- package/package.json +30 -16
- package/dist/adapters/comments/client.d.ts +0 -64
- package/dist/adapters/comments/client.js.map +0 -1
- package/dist/adapters/routes.js.map +0 -1
- package/dist/core/toolbar.d.ts +0 -74
- package/dist/core/toolbar.js +0 -45
- package/dist/core/toolbar.js.map +0 -1
- package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
- package/dist/integrations/composite-ai/document-transcribe.js +0 -89
- package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
- package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
- package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
- package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
- package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
- package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
- package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
- package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
- package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
- package/dist/presets/advanced.d.ts +0 -33
- package/dist/presets/advanced.js +0 -50
- package/dist/presets/advanced.js.map +0 -1
- package/dist/presets/simple.d.ts +0 -9
- package/dist/presets/simple.js +0 -12
- package/dist/presets/simple.js.map +0 -1
- package/dist/react/advanced-preset.d.ts +0 -11
- package/dist/react/advanced-preset.js +0 -31
- package/dist/react/advanced-preset.js.map +0 -1
- package/dist/vue/advanced-preset.d.ts +0 -13
- package/dist/vue/advanced-preset.js +0 -33
- package/dist/vue/advanced-preset.js.map +0 -1
- package/docs/extending.md +0 -538
- package/docs/integration.md +0 -632
- package/docs/llms.md +0 -383
- package/docs/usage.md +0 -1076
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { HandlerContext, HandlerRequest, HandlerResponse } from "./context.js";
|
|
2
2
|
//#region src/server/handlers/editor.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The capabilities this server serves (`BackendCapability[]`, see `serverCapabilities`): what the client's probe
|
|
5
|
+
* (`createHttpBackend({ probe: true })`) prunes its backend to. Requires an authenticated user through the `identity` port, like the
|
|
6
|
+
* routes whose capabilities it lists.
|
|
7
|
+
*/
|
|
8
|
+
export declare function capabilitiesGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
3
9
|
/** Body schema of `settingsPut`: the complete editor settings. */
|
|
4
10
|
export declare const settingsPutBodySchema: import("zod").ZodObject<{
|
|
5
11
|
version: import("zod").ZodLiteral<1>;
|
|
@@ -58,44 +64,44 @@ export declare const settingsPutBodySchema: import("zod").ZodObject<{
|
|
|
58
64
|
}, import("zod/v4/core").$strip>;
|
|
59
65
|
}, import("zod/v4/core").$strip>;
|
|
60
66
|
/**
|
|
61
|
-
* The authenticated user's editor settings, normalized with `parseEditorSettings`. `204`
|
|
62
|
-
*
|
|
63
|
-
*
|
|
67
|
+
* The authenticated user's editor settings, normalized with `parseEditorSettings`. `204` without a `settings` port (the
|
|
68
|
+
* client stays on localStorage); `200` with JSON `null` when the user has none stored yet (the client uploads its local
|
|
69
|
+
* ones).
|
|
64
70
|
*/
|
|
65
71
|
export declare function settingsGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
66
|
-
/** Stores the authenticated user's editor settings (complete, validated: 400 otherwise); `501`
|
|
72
|
+
/** Stores the authenticated user's editor settings (complete, validated: 400 otherwise); `501` without a `settings` port. */
|
|
67
73
|
export declare function settingsPut(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
68
74
|
/**
|
|
69
75
|
* All `#` tag names used in any document, deduplicated and sorted. Documents currently open in the collaboration server
|
|
70
76
|
* are read from their live Y.Doc (a tag added in document A shows up in document B before the debounced store); all
|
|
71
|
-
* others from the
|
|
77
|
+
* others from the stored content of `ports.documents`.
|
|
72
78
|
*/
|
|
73
|
-
export declare function listWorkspaceTags({
|
|
79
|
+
export declare function listWorkspaceTags({ ports, collab }: HandlerContext): Promise<string[]>;
|
|
74
80
|
/**
|
|
75
81
|
* Tag names for the `#` popup: tags used in any document, filtered by the normalised `?q=` (prefix matches first, then
|
|
76
|
-
* substrings). Requires an authenticated user
|
|
82
|
+
* substrings). Requires an authenticated user through the `identity` port.
|
|
77
83
|
*/
|
|
78
84
|
export declare function tagsGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
79
85
|
/**
|
|
80
86
|
* Users for the `@` mention popup, filtered case-insensitively by `?q=` (prefix matches first, then substrings).
|
|
81
|
-
* Requires an authenticated user
|
|
87
|
+
* Requires an authenticated user through the `identity` port.
|
|
82
88
|
*/
|
|
83
89
|
export declare function usersGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
84
90
|
/**
|
|
85
91
|
* Stores a file (multipart field `file`) in the package of document `params.id` (or `scratch` for editors not bound to
|
|
86
|
-
* a stored document); answers `{ url, name }`. Requires an authenticated user
|
|
92
|
+
* a stored document); answers `{ url, name, sha256 }`. Requires an authenticated user through the `identity` port.
|
|
87
93
|
*/
|
|
88
94
|
export declare function assetPost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
89
95
|
/**
|
|
90
96
|
* The stored file `params.name` of document `params.id`, with `Range` support (media elements send the session cookie
|
|
91
|
-
* like any same-origin request). Requires an authenticated user
|
|
97
|
+
* like any same-origin request). Requires an authenticated user through the `identity` port.
|
|
92
98
|
*/
|
|
93
99
|
export declare function assetGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
94
|
-
/** The theme library (builtins and uploads) as `ThemeRecord[]`. `501`
|
|
100
|
+
/** The theme library (builtins and uploads) as `ThemeRecord[]`. `501` without a `themes` port. */
|
|
95
101
|
export declare function themesGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
96
|
-
/** `theme.css` of theme `params.id` as `text/css`. `400` for a malformed id, `404` for an unknown one, `501`
|
|
102
|
+
/** `theme.css` of theme `params.id` as `text/css`. `400` for a malformed id, `404` for an unknown one, `501` without a `themes` port. */
|
|
97
103
|
export declare function themeGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
98
|
-
/** Deletes the uploaded theme `params.id` (`204`). `400` for a malformed id, `404` for an unknown one, `403` for a builtin, `501`
|
|
104
|
+
/** Deletes the uploaded theme `params.id` (`204`). `400` for a malformed id, `404` for an unknown one, `403` for a builtin, `501` without a `themes` port. */
|
|
99
105
|
export declare function themeDelete(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse>;
|
|
100
106
|
/**
|
|
101
107
|
* Uploads an Obsidian theme into the shared library: multipart `css` (File, required), `manifest` (File) and `name`
|
|
@@ -4,49 +4,60 @@ import { ASSET_DOCUMENT_ID, ASSET_NAME, assetUrl } from "../../features/media/as
|
|
|
4
4
|
import { MAX_MEDIA_BYTES, MEDIA_TYPES, mediaKindOf, mediaMime } from "../../features/media/schema.js";
|
|
5
5
|
import { collectTagNames, normalizeTagName } from "../../features/mentions/tag.js";
|
|
6
6
|
import { COLLAB_FIELD } from "../collab.js";
|
|
7
|
-
import { authenticated, empty, json, readBody, text } from "./context.js";
|
|
7
|
+
import { authenticated, empty, json, port, readBody, text } from "./context.js";
|
|
8
|
+
import { serverCapabilities } from "../ports.js";
|
|
8
9
|
import { MAX_THEME_BYTES, themeManifestSchema } from "../../features/themes/schema.js";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
9
11
|
import { yDocToProsemirrorJSON } from "@tiptap/y-tiptap";
|
|
10
12
|
//#region src/server/handlers/editor.ts
|
|
13
|
+
/**
|
|
14
|
+
* The capabilities this server serves (`BackendCapability[]`, see `serverCapabilities`): what the client's probe
|
|
15
|
+
* (`createHttpBackend({ probe: true })`) prunes its backend to. Requires an authenticated user through the `identity` port, like the
|
|
16
|
+
* routes whose capabilities it lists.
|
|
17
|
+
*/
|
|
18
|
+
async function capabilitiesGet(ctx, request) {
|
|
19
|
+
await authenticated(ctx, request);
|
|
20
|
+
return json(serverCapabilities(ctx.ports, ctx.served));
|
|
21
|
+
}
|
|
11
22
|
/** Body schema of `settingsPut`: the complete editor settings. */
|
|
12
23
|
const settingsPutBodySchema = editorSettingsSchema;
|
|
13
24
|
const MAX_USERS = 20;
|
|
14
25
|
const MAX_TAGS = 20;
|
|
15
26
|
/**
|
|
16
|
-
* The authenticated user's editor settings, normalized with `parseEditorSettings`. `204`
|
|
17
|
-
*
|
|
18
|
-
*
|
|
27
|
+
* The authenticated user's editor settings, normalized with `parseEditorSettings`. `204` without a `settings` port (the
|
|
28
|
+
* client stays on localStorage); `200` with JSON `null` when the user has none stored yet (the client uploads its local
|
|
29
|
+
* ones).
|
|
19
30
|
*/
|
|
20
31
|
async function settingsGet(ctx, request) {
|
|
21
32
|
const user = await authenticated(ctx, request);
|
|
22
|
-
if (!ctx.
|
|
23
|
-
const stored = await ctx.
|
|
33
|
+
if (!ctx.ports.settings) return empty();
|
|
34
|
+
const stored = await ctx.ports.settings.loadSettings(user);
|
|
24
35
|
if (stored === null || stored === void 0) return json(null);
|
|
25
36
|
return json(parseEditorSettings(stored));
|
|
26
37
|
}
|
|
27
|
-
/** Stores the authenticated user's editor settings (complete, validated: 400 otherwise); `501`
|
|
38
|
+
/** Stores the authenticated user's editor settings (complete, validated: 400 otherwise); `501` without a `settings` port. */
|
|
28
39
|
async function settingsPut(ctx, request) {
|
|
29
40
|
const user = await authenticated(ctx, request);
|
|
30
|
-
|
|
41
|
+
const store = port(ctx, "settings");
|
|
31
42
|
const settings = await readBody(request, settingsPutBodySchema);
|
|
32
|
-
await
|
|
43
|
+
await store.storeSettings(user, settings);
|
|
33
44
|
return empty();
|
|
34
45
|
}
|
|
35
46
|
/**
|
|
36
47
|
* All `#` tag names used in any document, deduplicated and sorted. Documents currently open in the collaboration server
|
|
37
48
|
* are read from their live Y.Doc (a tag added in document A shows up in document B before the debounced store); all
|
|
38
|
-
* others from the
|
|
49
|
+
* others from the stored content of `ports.documents`.
|
|
39
50
|
*/
|
|
40
|
-
async function listWorkspaceTags({
|
|
51
|
+
async function listWorkspaceTags({ ports, collab }) {
|
|
41
52
|
const names = /* @__PURE__ */ new Set();
|
|
42
53
|
const open = collab?.hocuspocus.documents;
|
|
43
54
|
if (open) for (const doc of open.values()) collectTagNames(yDocToProsemirrorJSON(doc, COLLAB_FIELD), names);
|
|
44
|
-
for (const { id, content } of await
|
|
55
|
+
for (const { id, content } of await ports.documents.listContents()) if (!open?.has(id)) collectTagNames(content, names);
|
|
45
56
|
return [...names].sort((a, b) => a.localeCompare(b, "de"));
|
|
46
57
|
}
|
|
47
58
|
/**
|
|
48
59
|
* Tag names for the `#` popup: tags used in any document, filtered by the normalised `?q=` (prefix matches first, then
|
|
49
|
-
* substrings). Requires an authenticated user
|
|
60
|
+
* substrings). Requires an authenticated user through the `identity` port.
|
|
50
61
|
*/
|
|
51
62
|
async function tagsGet(ctx, request) {
|
|
52
63
|
await authenticated(ctx, request);
|
|
@@ -57,12 +68,12 @@ async function tagsGet(ctx, request) {
|
|
|
57
68
|
}
|
|
58
69
|
/**
|
|
59
70
|
* Users for the `@` mention popup, filtered case-insensitively by `?q=` (prefix matches first, then substrings).
|
|
60
|
-
* Requires an authenticated user
|
|
71
|
+
* Requires an authenticated user through the `identity` port.
|
|
61
72
|
*/
|
|
62
73
|
async function usersGet(ctx, request) {
|
|
63
74
|
await authenticated(ctx, request);
|
|
64
75
|
const q = String(request.query?.q ?? "").trim().toLowerCase();
|
|
65
|
-
const matches = (await ctx.
|
|
76
|
+
const matches = (await port(ctx, "directory").listUsers()).map(({ id, username, color }) => ({
|
|
66
77
|
id,
|
|
67
78
|
username,
|
|
68
79
|
color
|
|
@@ -72,7 +83,7 @@ async function usersGet(ctx, request) {
|
|
|
72
83
|
}
|
|
73
84
|
/**
|
|
74
85
|
* Stores a file (multipart field `file`) in the package of document `params.id` (or `scratch` for editors not bound to
|
|
75
|
-
* a stored document); answers `{ url, name }`. Requires an authenticated user
|
|
86
|
+
* a stored document); answers `{ url, name, sha256 }`. Requires an authenticated user through the `identity` port.
|
|
76
87
|
*/
|
|
77
88
|
async function assetPost(ctx, request) {
|
|
78
89
|
await authenticated(ctx, request);
|
|
@@ -86,30 +97,34 @@ async function assetPost(ctx, request) {
|
|
|
86
97
|
const nameExt = file.name.includes(".") ? file.name.split(".").pop()?.toLowerCase() : void 0;
|
|
87
98
|
const ext = MEDIA_TYPES[mime]?.ext ?? (nameExt && /^[a-z0-9]{1,8}$/.test(nameExt) ? nameExt : "bin");
|
|
88
99
|
const name = `${globalThis.crypto.randomUUID()}.${ext}`;
|
|
89
|
-
await
|
|
100
|
+
const data = Buffer.from(await file.arrayBuffer());
|
|
101
|
+
const sha256 = createHash("sha256").update(data).digest("hex");
|
|
102
|
+
await port(ctx, "assets").storeAsset(id, {
|
|
90
103
|
name,
|
|
91
104
|
filename: file.name.slice(0, 255) || name,
|
|
92
105
|
mimeType: mime,
|
|
93
106
|
size: file.size,
|
|
94
107
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
95
|
-
|
|
108
|
+
sha256,
|
|
109
|
+
data
|
|
96
110
|
});
|
|
97
111
|
return json({
|
|
98
112
|
url: assetUrl(id, name),
|
|
99
|
-
name
|
|
113
|
+
name,
|
|
114
|
+
sha256
|
|
100
115
|
});
|
|
101
116
|
}
|
|
102
117
|
const BYTE_RANGE = /^bytes=(\d*)-(\d*)$/;
|
|
103
118
|
/**
|
|
104
119
|
* The stored file `params.name` of document `params.id`, with `Range` support (media elements send the session cookie
|
|
105
|
-
* like any same-origin request). Requires an authenticated user
|
|
120
|
+
* like any same-origin request). Requires an authenticated user through the `identity` port.
|
|
106
121
|
*/
|
|
107
122
|
async function assetGet(ctx, request) {
|
|
108
123
|
await authenticated(ctx, request);
|
|
109
124
|
const id = request.params?.id ?? "";
|
|
110
125
|
const name = request.params?.name ?? "";
|
|
111
126
|
if (!ASSET_DOCUMENT_ID.test(id) || !ASSET_NAME.test(name)) throw new HttpError(404, "Datei nicht gefunden");
|
|
112
|
-
const asset = await ctx.
|
|
127
|
+
const asset = await port(ctx, "assets").loadAsset(id, name);
|
|
113
128
|
if (!asset) throw new HttpError(404, "Datei nicht gefunden");
|
|
114
129
|
const { record, data } = asset;
|
|
115
130
|
const total = data.length;
|
|
@@ -143,20 +158,18 @@ async function assetGet(ctx, request) {
|
|
|
143
158
|
body: data.subarray(start, end + 1)
|
|
144
159
|
};
|
|
145
160
|
}
|
|
146
|
-
|
|
147
|
-
/** The theme library (builtins and uploads) as `ThemeRecord[]`. `501` when the adapter has no theme support. */
|
|
161
|
+
/** The theme library (builtins and uploads) as `ThemeRecord[]`. `501` without a `themes` port. */
|
|
148
162
|
async function themesGet(ctx, request) {
|
|
149
163
|
await authenticated(ctx, request);
|
|
150
|
-
|
|
151
|
-
return json(await ctx.adapter.listThemes());
|
|
164
|
+
return json(await port(ctx, "themes").listThemes());
|
|
152
165
|
}
|
|
153
|
-
/** `theme.css` of theme `params.id` as `text/css`. `400` for a malformed id, `404` for an unknown one, `501`
|
|
166
|
+
/** `theme.css` of theme `params.id` as `text/css`. `400` for a malformed id, `404` for an unknown one, `501` without a `themes` port. */
|
|
154
167
|
async function themeGet(ctx, request) {
|
|
155
168
|
await authenticated(ctx, request);
|
|
156
|
-
|
|
169
|
+
const themes = port(ctx, "themes");
|
|
157
170
|
const id = request.params?.id ?? "";
|
|
158
171
|
if (!THEME_ID.test(id)) throw new HttpError(400, "Ungültige Theme-ID");
|
|
159
|
-
const theme = await
|
|
172
|
+
const theme = await themes.loadTheme(id);
|
|
160
173
|
if (!theme) throw new HttpError(404, "Theme nicht gefunden");
|
|
161
174
|
const etag = `"${theme.record.version ?? "0"}-${new TextEncoder().encode(theme.css).length}"`;
|
|
162
175
|
const headers = {
|
|
@@ -166,17 +179,16 @@ async function themeGet(ctx, request) {
|
|
|
166
179
|
if (request.headers.get("if-none-match") === etag) return empty(304, headers);
|
|
167
180
|
return text(theme.css, "text/css; charset=utf-8", headers);
|
|
168
181
|
}
|
|
169
|
-
/** Deletes the uploaded theme `params.id` (`204`). `400` for a malformed id, `404` for an unknown one, `403` for a builtin, `501`
|
|
182
|
+
/** Deletes the uploaded theme `params.id` (`204`). `400` for a malformed id, `404` for an unknown one, `403` for a builtin, `501` without a `themes` port. */
|
|
170
183
|
async function themeDelete(ctx, request) {
|
|
171
184
|
await authenticated(ctx, request);
|
|
172
|
-
const
|
|
173
|
-
if (!adapter.listThemes || !adapter.removeTheme) throw themesUnsupported();
|
|
185
|
+
const themes = port(ctx, "themes");
|
|
174
186
|
const id = request.params?.id ?? "";
|
|
175
187
|
if (!THEME_ID.test(id)) throw new HttpError(400, "Ungültige Theme-ID");
|
|
176
|
-
const record = (await
|
|
188
|
+
const record = (await themes.listThemes()).find((t) => t.id === id);
|
|
177
189
|
if (!record) throw new HttpError(404, "Theme nicht gefunden");
|
|
178
190
|
if (record.builtin) throw new HttpError(403, "Mitgelieferte Themes können nicht gelöscht werden");
|
|
179
|
-
if (!await
|
|
191
|
+
if (!await themes.removeTheme(id)) throw new HttpError(404, "Theme nicht gefunden");
|
|
180
192
|
return empty();
|
|
181
193
|
}
|
|
182
194
|
const MAX_MANIFEST_BYTES = 65536;
|
|
@@ -193,8 +205,7 @@ function slugOf(name, max) {
|
|
|
193
205
|
*/
|
|
194
206
|
async function themePost(ctx, request) {
|
|
195
207
|
await authenticated(ctx, request);
|
|
196
|
-
const
|
|
197
|
-
if (!adapter.listThemes || !adapter.storeTheme) throw themesUnsupported();
|
|
208
|
+
const themes = port(ctx, "themes");
|
|
198
209
|
const form = await request.readForm?.().catch(() => null);
|
|
199
210
|
const cssFile = form?.get("css");
|
|
200
211
|
if (!(cssFile instanceof File) || cssFile.size === 0) throw new HttpError(400, "Keine CSS-Datei übermittelt");
|
|
@@ -218,7 +229,7 @@ async function themePost(ctx, request) {
|
|
|
218
229
|
const nameField = form?.get("name");
|
|
219
230
|
const cssBaseName = cssFile.name.replace(/\.css$/i, "");
|
|
220
231
|
const name = manifest?.name ?? [typeof nameField === "string" ? nameField : "", cssBaseName].map((n) => n.trim().slice(0, 120)).find(Boolean) ?? "Theme";
|
|
221
|
-
const taken = new Set((await
|
|
232
|
+
const taken = new Set((await themes.listThemes()).map((t) => t.id));
|
|
222
233
|
let id = slugOf(name, MAX_ID_LENGTH);
|
|
223
234
|
for (let n = 2; taken.has(id); n++) {
|
|
224
235
|
const suffix = `-${n}`;
|
|
@@ -229,13 +240,13 @@ async function themePost(ctx, request) {
|
|
|
229
240
|
name,
|
|
230
241
|
id
|
|
231
242
|
};
|
|
232
|
-
await
|
|
243
|
+
await themes.storeTheme(record, css);
|
|
233
244
|
return json({
|
|
234
245
|
...record,
|
|
235
246
|
builtin: false
|
|
236
247
|
}, 201);
|
|
237
248
|
}
|
|
238
249
|
//#endregion
|
|
239
|
-
export { assetGet, assetPost, listWorkspaceTags, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, themeDelete, themeGet, themePost, themesGet, usersGet };
|
|
250
|
+
export { assetGet, assetPost, capabilitiesGet, listWorkspaceTags, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, themeDelete, themeGet, themePost, themesGet, usersGet };
|
|
240
251
|
|
|
241
252
|
//# sourceMappingURL=editor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.js","names":[],"sources":["../../../src/server/handlers/editor.ts"],"sourcesContent":["import { yDocToProsemirrorJSON } from '@tiptap/y-tiptap'\nimport { ASSET_DOCUMENT_ID, ASSET_NAME, assetUrl, MAX_MEDIA_BYTES, MEDIA_TYPES, mediaKindOf, mediaMime } from '../../features/media/schema'\nimport { collectTagNames, normalizeTagName } from '../../features/mentions/schema'\nimport { MAX_THEME_BYTES, THEME_ID, themeManifestSchema, type ThemeManifest, type ThemeRecord } from '../../features/themes/schema'\nimport { editorSettingsSchema, parseEditorSettings } from '../../settings'\nimport { COLLAB_FIELD } from '../collab'\nimport { HttpError } from '../errors'\nimport { authenticated, empty, type HandlerContext, type HandlerRequest, type HandlerResponse, json, readBody, text } from './context'\n\n/** Body schema of `settingsPut`: the complete editor settings. */\nexport const settingsPutBodySchema = editorSettingsSchema\n\nconst MAX_USERS = 20\nconst MAX_TAGS = 20\n\n/**\n * The authenticated user's editor settings, normalized with `parseEditorSettings`. `204` when the adapter keeps no\n * settings (the client stays on localStorage); `200` with JSON `null` when the user has none stored yet (the client\n * uploads its local ones).\n */\nexport async function settingsGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n if (!ctx.adapter.loadSettings) return empty()\n const stored = await ctx.adapter.loadSettings(user)\n if (stored === null || stored === undefined) return json(null)\n return json(parseEditorSettings(stored))\n}\n\n/** Stores the authenticated user's editor settings (complete, validated: 400 otherwise); `501` when the adapter keeps none. */\nexport async function settingsPut(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n if (!ctx.adapter.storeSettings) throw new HttpError(501, 'Einstellungen werden nicht gespeichert')\n const settings = await readBody(request, settingsPutBodySchema)\n await ctx.adapter.storeSettings(user, settings)\n return empty()\n}\n\n/**\n * All `#` tag names used in any document, deduplicated and sorted. Documents currently open in the collaboration server\n * are read from their live Y.Doc (a tag added in document A shows up in document B before the debounced store); all\n * others from the adapter's stored content.\n */\nexport async function listWorkspaceTags({ adapter, collab }: HandlerContext): Promise<string[]> {\n const names = new Set<string>()\n const open = collab?.hocuspocus.documents\n if (open) for (const doc of open.values()) collectTagNames(yDocToProsemirrorJSON(doc, COLLAB_FIELD), names)\n for (const { id, content } of await adapter.listContents()) {\n if (!open?.has(id)) collectTagNames(content, names)\n }\n return [...names].sort((a, b) => a.localeCompare(b, 'de'))\n}\n\n/**\n * Tag names for the `#` popup: tags used in any document, filtered by the normalised `?q=` (prefix matches first, then\n * substrings). Requires an authenticated user via the adapter.\n */\nexport async function tagsGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const q = normalizeTagName(String(request.query?.q ?? ''))\n const matches = (await listWorkspaceTags(ctx)).filter(tag => tag.includes(q))\n const prefixMatches = matches.filter(tag => tag.startsWith(q))\n return json([...prefixMatches, ...matches.filter(tag => !tag.startsWith(q))].slice(0, MAX_TAGS))\n}\n\n/**\n * Users for the `@` mention popup, filtered case-insensitively by `?q=` (prefix matches first, then substrings).\n * Requires an authenticated user via the adapter.\n */\nexport async function usersGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const q = String(request.query?.q ?? '').trim().toLowerCase()\n // Re-pick the fields so an adapter returning richer user records cannot leak them.\n const matches = (await ctx.adapter.listUsers())\n .map(({ id, username, color }) => ({ id, username, color }))\n .filter(u => u.username.toLowerCase().includes(q))\n .sort((a, b) => a.username.localeCompare(b.username, 'de'))\n const prefixMatches = matches.filter(u => u.username.toLowerCase().startsWith(q))\n return json([...prefixMatches, ...matches.filter(u => !prefixMatches.includes(u))].slice(0, MAX_USERS))\n}\n\n/**\n * Stores a file (multipart field `file`) in the package of document `params.id` (or `scratch` for editors not bound to\n * a stored document); answers `{ url, name }`. Requires an authenticated user via the adapter.\n */\nexport async function assetPost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const id = request.params?.id ?? ''\n if (!ASSET_DOCUMENT_ID.test(id)) throw new HttpError(400, 'Ungültige Dokument-ID')\n // A body that is no multipart form at all counts as „no file“.\n const form = await request.readForm?.().catch(() => null)\n const file = form?.get('file')\n if (!(file instanceof File)) throw new HttpError(400, 'Keine Datei übermittelt')\n const mime = mediaMime(file.type) || 'application/octet-stream'\n const maxBytes = MAX_MEDIA_BYTES[mediaKindOf(mime) ?? 'file']\n if (file.size > maxBytes) throw new HttpError(413, `Datei ist größer als ${maxBytes / 1024 / 1024} MB`)\n const nameExt = file.name.includes('.') ? file.name.split('.').pop()?.toLowerCase() : undefined\n const ext = MEDIA_TYPES[mime]?.ext ?? (nameExt && /^[a-z0-9]{1,8}$/.test(nameExt) ? nameExt : 'bin')\n const name = `${globalThis.crypto.randomUUID()}.${ext}`\n await ctx.adapter.storeAsset(id, {\n name,\n filename: file.name.slice(0, 255) || name,\n mimeType: mime,\n size: file.size,\n createdAt: new Date().toISOString(),\n data: Buffer.from(await file.arrayBuffer()),\n })\n return json({ url: assetUrl(id, name), name })\n}\n\n// Answers single byte ranges (`Range: bytes=a-b`, `bytes=a-`, `bytes=-n`): Safari only plays and seeks audio/video\n// served with range support. Other Range forms (several ranges) get the whole file, which RFC 9110 allows.\nconst BYTE_RANGE = /^bytes=(\\d*)-(\\d*)$/\n\n/**\n * The stored file `params.name` of document `params.id`, with `Range` support (media elements send the session cookie\n * like any same-origin request). Requires an authenticated user via the adapter.\n */\nexport async function assetGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const id = request.params?.id ?? ''\n const name = request.params?.name ?? ''\n if (!ASSET_DOCUMENT_ID.test(id) || !ASSET_NAME.test(name)) throw new HttpError(404, 'Datei nicht gefunden')\n const asset = await ctx.adapter.loadAsset(id, name)\n if (!asset) throw new HttpError(404, 'Datei nicht gefunden')\n const { record, data } = asset\n const total = data.length\n const disposition = record.mimeType === 'application/octet-stream' ? 'attachment' : 'inline'\n const headers: Record<string, string> = {\n 'accept-ranges': 'bytes',\n 'cache-control': 'private, max-age=31536000, immutable',\n 'content-type': record.mimeType,\n 'content-disposition': `${disposition}; filename*=UTF-8''${encodeURIComponent(record.filename)}`,\n }\n const range = BYTE_RANGE.exec(request.headers.get('range') ?? '')\n if (!range) return { status: 200, headers: { ...headers, 'content-length': String(total) }, body: data }\n const [, first, last] = range\n const start = first ? Number(first) : Math.max(0, total - Number(last))\n const end = first && last ? Math.min(Number(last), total - 1) : total - 1\n if (start >= total || start > end) return empty(416, { 'content-range': `bytes */${total}` })\n return {\n status: 206,\n headers: { ...headers, 'content-range': `bytes ${start}-${end}/${total}`, 'content-length': String(end - start + 1) },\n body: data.subarray(start, end + 1),\n }\n}\n\nconst themesUnsupported = () => new HttpError(501, 'Themes werden vom Adapter nicht unterstützt')\n\n/** The theme library (builtins and uploads) as `ThemeRecord[]`. `501` when the adapter has no theme support. */\nexport async function themesGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n if (!ctx.adapter.listThemes) throw themesUnsupported()\n return json(await ctx.adapter.listThemes())\n}\n\n/** `theme.css` of theme `params.id` as `text/css`. `400` for a malformed id, `404` for an unknown one, `501` when the adapter has no theme support. */\nexport async function themeGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n if (!ctx.adapter.loadTheme) throw themesUnsupported()\n const id = request.params?.id ?? ''\n if (!THEME_ID.test(id)) throw new HttpError(400, 'Ungültige Theme-ID')\n const theme = await ctx.adapter.loadTheme(id)\n if (!theme) throw new HttpError(404, 'Theme nicht gefunden')\n const etag = `\"${theme.record.version ?? '0'}-${new TextEncoder().encode(theme.css).length}\"`\n const headers = { 'cache-control': 'private, max-age=0, must-revalidate', 'etag': etag }\n if (request.headers.get('if-none-match') === etag) return empty(304, headers)\n return text(theme.css, 'text/css; charset=utf-8', headers)\n}\n\n/** Deletes the uploaded theme `params.id` (`204`). `400` for a malformed id, `404` for an unknown one, `403` for a builtin, `501` when the adapter has no theme write support. */\nexport async function themeDelete(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const { adapter } = ctx\n if (!adapter.listThemes || !adapter.removeTheme) throw themesUnsupported()\n const id = request.params?.id ?? ''\n if (!THEME_ID.test(id)) throw new HttpError(400, 'Ungültige Theme-ID')\n // `removeTheme` answers `false` for builtins and unknown ids alike, so the record decides between 403 and 404.\n const record = (await adapter.listThemes()).find(t => t.id === id)\n if (!record) throw new HttpError(404, 'Theme nicht gefunden')\n if (record.builtin) throw new HttpError(403, 'Mitgelieferte Themes können nicht gelöscht werden')\n if (!await adapter.removeTheme(id)) throw new HttpError(404, 'Theme nicht gefunden')\n return empty()\n}\n\nconst MAX_MANIFEST_BYTES = 64 * 1024\nconst MAX_ID_LENGTH = 64\n// Markers of an HTML/XML/SVG document in the head of a „stylesheet“.\nconst MARKUP_HEAD = /<script|<\\?xml/i\n\n/** `[^a-z0-9]+` → `-`, trimmed, at most `max` characters; `theme` when nothing is left. */\nfunction slugOf(name: string, max: number): string {\n const slug = name.normalize('NFKD').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+/, '').slice(0, max).replace(/-+$/, '')\n return slug || 'theme'\n}\n\n/**\n * Uploads an Obsidian theme into the shared library: multipart `css` (File, required), `manifest` (File) and `name`\n * (string) optional. Answers 201 with the new `ThemeRecord`; the id is the slug of the name, made unique with `-2`,\n * `-3` … against the library.\n */\nexport async function themePost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const { adapter } = ctx\n if (!adapter.listThemes || !adapter.storeTheme) throw themesUnsupported()\n // A body that is no multipart form counts as „no file“.\n const form = await request.readForm?.().catch(() => null)\n const cssFile = form?.get('css')\n if (!(cssFile instanceof File) || cssFile.size === 0) throw new HttpError(400, 'Keine CSS-Datei übermittelt')\n if (cssFile.size > MAX_THEME_BYTES) throw new HttpError(413, `Theme ist größer als ${MAX_THEME_BYTES / 1024 / 1024} MB`)\n const bytes = new Uint8Array(await cssFile.arrayBuffer())\n if (bytes.includes(0) || MARKUP_HEAD.test(new TextDecoder('latin1').decode(bytes.subarray(0, 512)))) {\n throw new HttpError(400, 'Theme ist keine CSS-Textdatei')\n }\n let css: string\n try {\n css = new TextDecoder('utf-8', { fatal: true }).decode(bytes)\n }\n catch {\n throw new HttpError(400, 'Theme ist keine gültige UTF-8-Textdatei')\n }\n\n let manifest: ThemeManifest | undefined\n const manifestFile = form?.get('manifest')\n if (manifestFile instanceof File && manifestFile.size > 0) {\n try {\n if (manifestFile.size > MAX_MANIFEST_BYTES) throw new Error('manifest too large')\n manifest = themeManifestSchema.parse(JSON.parse(await manifestFile.text()))\n }\n catch {\n throw new HttpError(400, 'Ungültiges manifest.json')\n }\n }\n\n const nameField = form?.get('name')\n const cssBaseName = cssFile.name.replace(/\\.css$/i, '')\n const name = manifest?.name\n ?? [typeof nameField === 'string' ? nameField : '', cssBaseName].map(n => n.trim().slice(0, 120)).find(Boolean)\n ?? 'Theme'\n\n const taken = new Set((await adapter.listThemes()).map(t => t.id))\n const base = slugOf(name, MAX_ID_LENGTH)\n let id = base\n for (let n = 2; taken.has(id); n++) {\n const suffix = `-${n}`\n id = `${slugOf(name, MAX_ID_LENGTH - suffix.length)}${suffix}`\n }\n\n const record: Omit<ThemeRecord, 'builtin'> = { ...manifest, name, id }\n await adapter.storeTheme(record, css)\n return json({ ...record, builtin: false } satisfies ThemeRecord, 201)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"editor.js","names":[],"sources":["../../../src/server/handlers/editor.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { yDocToProsemirrorJSON } from '@tiptap/y-tiptap'\nimport { ASSET_DOCUMENT_ID, ASSET_NAME, assetUrl, MAX_MEDIA_BYTES, MEDIA_TYPES, mediaKindOf, mediaMime } from '../../features/media/schema'\nimport { collectTagNames, normalizeTagName } from '../../features/mentions/schema'\nimport { MAX_THEME_BYTES, THEME_ID, themeManifestSchema, type ThemeManifest, type ThemeRecord } from '../../features/themes/schema'\nimport { editorSettingsSchema, parseEditorSettings } from '../../settings'\nimport { COLLAB_FIELD } from '../collab'\nimport { HttpError } from '../errors'\nimport { serverCapabilities } from '../ports'\nimport { authenticated, empty, type HandlerContext, type HandlerRequest, type HandlerResponse, json, port, readBody, text } from './context'\n\n/**\n * The capabilities this server serves (`BackendCapability[]`, see `serverCapabilities`): what the client's probe\n * (`createHttpBackend({ probe: true })`) prunes its backend to. Requires an authenticated user through the `identity` port, like the\n * routes whose capabilities it lists.\n */\nexport async function capabilitiesGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n return json(serverCapabilities(ctx.ports, ctx.served))\n}\n\n/** Body schema of `settingsPut`: the complete editor settings. */\nexport const settingsPutBodySchema = editorSettingsSchema\n\nconst MAX_USERS = 20\nconst MAX_TAGS = 20\n\n/**\n * The authenticated user's editor settings, normalized with `parseEditorSettings`. `204` without a `settings` port (the\n * client stays on localStorage); `200` with JSON `null` when the user has none stored yet (the client uploads its local\n * ones).\n */\nexport async function settingsGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n if (!ctx.ports.settings) return empty()\n const stored = await ctx.ports.settings.loadSettings(user)\n if (stored === null || stored === undefined) return json(null)\n return json(parseEditorSettings(stored))\n}\n\n/** Stores the authenticated user's editor settings (complete, validated: 400 otherwise); `501` without a `settings` port. */\nexport async function settingsPut(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n const store = port(ctx, 'settings')\n const settings = await readBody(request, settingsPutBodySchema)\n await store.storeSettings(user, settings)\n return empty()\n}\n\n/**\n * All `#` tag names used in any document, deduplicated and sorted. Documents currently open in the collaboration server\n * are read from their live Y.Doc (a tag added in document A shows up in document B before the debounced store); all\n * others from the stored content of `ports.documents`.\n */\nexport async function listWorkspaceTags({ ports, collab }: HandlerContext): Promise<string[]> {\n const names = new Set<string>()\n const open = collab?.hocuspocus.documents\n if (open) for (const doc of open.values()) collectTagNames(yDocToProsemirrorJSON(doc, COLLAB_FIELD), names)\n for (const { id, content } of await ports.documents.listContents()) {\n if (!open?.has(id)) collectTagNames(content, names)\n }\n return [...names].sort((a, b) => a.localeCompare(b, 'de'))\n}\n\n/**\n * Tag names for the `#` popup: tags used in any document, filtered by the normalised `?q=` (prefix matches first, then\n * substrings). Requires an authenticated user through the `identity` port.\n */\nexport async function tagsGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const q = normalizeTagName(String(request.query?.q ?? ''))\n const matches = (await listWorkspaceTags(ctx)).filter(tag => tag.includes(q))\n const prefixMatches = matches.filter(tag => tag.startsWith(q))\n return json([...prefixMatches, ...matches.filter(tag => !tag.startsWith(q))].slice(0, MAX_TAGS))\n}\n\n/**\n * Users for the `@` mention popup, filtered case-insensitively by `?q=` (prefix matches first, then substrings).\n * Requires an authenticated user through the `identity` port.\n */\nexport async function usersGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const q = String(request.query?.q ?? '').trim().toLowerCase()\n // Re-pick the fields so a directory returning richer user records cannot leak them.\n const matches = (await port(ctx, 'directory').listUsers())\n .map(({ id, username, color }) => ({ id, username, color }))\n .filter(u => u.username.toLowerCase().includes(q))\n .sort((a, b) => a.username.localeCompare(b.username, 'de'))\n const prefixMatches = matches.filter(u => u.username.toLowerCase().startsWith(q))\n return json([...prefixMatches, ...matches.filter(u => !prefixMatches.includes(u))].slice(0, MAX_USERS))\n}\n\n/**\n * Stores a file (multipart field `file`) in the package of document `params.id` (or `scratch` for editors not bound to\n * a stored document); answers `{ url, name, sha256 }`. Requires an authenticated user through the `identity` port.\n */\nexport async function assetPost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const id = request.params?.id ?? ''\n if (!ASSET_DOCUMENT_ID.test(id)) throw new HttpError(400, 'Ungültige Dokument-ID')\n // A body that is no multipart form at all counts as „no file“.\n const form = await request.readForm?.().catch(() => null)\n const file = form?.get('file')\n if (!(file instanceof File)) throw new HttpError(400, 'Keine Datei übermittelt')\n const mime = mediaMime(file.type) || 'application/octet-stream'\n const maxBytes = MAX_MEDIA_BYTES[mediaKindOf(mime) ?? 'file']\n if (file.size > maxBytes) throw new HttpError(413, `Datei ist größer als ${maxBytes / 1024 / 1024} MB`)\n const nameExt = file.name.includes('.') ? file.name.split('.').pop()?.toLowerCase() : undefined\n const ext = MEDIA_TYPES[mime]?.ext ?? (nameExt && /^[a-z0-9]{1,8}$/.test(nameExt) ? nameExt : 'bin')\n const name = `${globalThis.crypto.randomUUID()}.${ext}`\n const data = Buffer.from(await file.arrayBuffer())\n const sha256 = createHash('sha256').update(data).digest('hex')\n await port(ctx, 'assets').storeAsset(id, {\n name,\n filename: file.name.slice(0, 255) || name,\n mimeType: mime,\n size: file.size,\n createdAt: new Date().toISOString(),\n sha256,\n data,\n })\n return json({ url: assetUrl(id, name), name, sha256 })\n}\n\n// Answers single byte ranges (`Range: bytes=a-b`, `bytes=a-`, `bytes=-n`): Safari only plays and seeks audio/video\n// served with range support. Other Range forms (several ranges) get the whole file, which RFC 9110 allows.\nconst BYTE_RANGE = /^bytes=(\\d*)-(\\d*)$/\n\n/**\n * The stored file `params.name` of document `params.id`, with `Range` support (media elements send the session cookie\n * like any same-origin request). Requires an authenticated user through the `identity` port.\n */\nexport async function assetGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const id = request.params?.id ?? ''\n const name = request.params?.name ?? ''\n if (!ASSET_DOCUMENT_ID.test(id) || !ASSET_NAME.test(name)) throw new HttpError(404, 'Datei nicht gefunden')\n const asset = await port(ctx, 'assets').loadAsset(id, name)\n if (!asset) throw new HttpError(404, 'Datei nicht gefunden')\n const { record, data } = asset\n const total = data.length\n const disposition = record.mimeType === 'application/octet-stream' ? 'attachment' : 'inline'\n const headers: Record<string, string> = {\n 'accept-ranges': 'bytes',\n 'cache-control': 'private, max-age=31536000, immutable',\n 'content-type': record.mimeType,\n 'content-disposition': `${disposition}; filename*=UTF-8''${encodeURIComponent(record.filename)}`,\n }\n const range = BYTE_RANGE.exec(request.headers.get('range') ?? '')\n if (!range) return { status: 200, headers: { ...headers, 'content-length': String(total) }, body: data }\n const [, first, last] = range\n const start = first ? Number(first) : Math.max(0, total - Number(last))\n const end = first && last ? Math.min(Number(last), total - 1) : total - 1\n if (start >= total || start > end) return empty(416, { 'content-range': `bytes */${total}` })\n return {\n status: 206,\n headers: { ...headers, 'content-range': `bytes ${start}-${end}/${total}`, 'content-length': String(end - start + 1) },\n body: data.subarray(start, end + 1),\n }\n}\n\n/** The theme library (builtins and uploads) as `ThemeRecord[]`. `501` without a `themes` port. */\nexport async function themesGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n return json(await port(ctx, 'themes').listThemes())\n}\n\n/** `theme.css` of theme `params.id` as `text/css`. `400` for a malformed id, `404` for an unknown one, `501` without a `themes` port. */\nexport async function themeGet(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const themes = port(ctx, 'themes')\n const id = request.params?.id ?? ''\n if (!THEME_ID.test(id)) throw new HttpError(400, 'Ungültige Theme-ID')\n const theme = await themes.loadTheme(id)\n if (!theme) throw new HttpError(404, 'Theme nicht gefunden')\n const etag = `\"${theme.record.version ?? '0'}-${new TextEncoder().encode(theme.css).length}\"`\n const headers = { 'cache-control': 'private, max-age=0, must-revalidate', 'etag': etag }\n if (request.headers.get('if-none-match') === etag) return empty(304, headers)\n return text(theme.css, 'text/css; charset=utf-8', headers)\n}\n\n/** Deletes the uploaded theme `params.id` (`204`). `400` for a malformed id, `404` for an unknown one, `403` for a builtin, `501` without a `themes` port. */\nexport async function themeDelete(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const themes = port(ctx, 'themes')\n const id = request.params?.id ?? ''\n if (!THEME_ID.test(id)) throw new HttpError(400, 'Ungültige Theme-ID')\n // `removeTheme` answers `false` for builtins and unknown ids alike, so the record decides between 403 and 404.\n const record = (await themes.listThemes()).find(t => t.id === id)\n if (!record) throw new HttpError(404, 'Theme nicht gefunden')\n if (record.builtin) throw new HttpError(403, 'Mitgelieferte Themes können nicht gelöscht werden')\n if (!await themes.removeTheme(id)) throw new HttpError(404, 'Theme nicht gefunden')\n return empty()\n}\n\nconst MAX_MANIFEST_BYTES = 64 * 1024\nconst MAX_ID_LENGTH = 64\n// Markers of an HTML/XML/SVG document in the head of a „stylesheet“.\nconst MARKUP_HEAD = /<script|<\\?xml/i\n\n/** `[^a-z0-9]+` → `-`, trimmed, at most `max` characters; `theme` when nothing is left. */\nfunction slugOf(name: string, max: number): string {\n const slug = name.normalize('NFKD').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+/, '').slice(0, max).replace(/-+$/, '')\n return slug || 'theme'\n}\n\n/**\n * Uploads an Obsidian theme into the shared library: multipart `css` (File, required), `manifest` (File) and `name`\n * (string) optional. Answers 201 with the new `ThemeRecord`; the id is the slug of the name, made unique with `-2`,\n * `-3` … against the library.\n */\nexport async function themePost(ctx: HandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n await authenticated(ctx, request)\n const themes = port(ctx, 'themes')\n // A body that is no multipart form counts as „no file“.\n const form = await request.readForm?.().catch(() => null)\n const cssFile = form?.get('css')\n if (!(cssFile instanceof File) || cssFile.size === 0) throw new HttpError(400, 'Keine CSS-Datei übermittelt')\n if (cssFile.size > MAX_THEME_BYTES) throw new HttpError(413, `Theme ist größer als ${MAX_THEME_BYTES / 1024 / 1024} MB`)\n const bytes = new Uint8Array(await cssFile.arrayBuffer())\n if (bytes.includes(0) || MARKUP_HEAD.test(new TextDecoder('latin1').decode(bytes.subarray(0, 512)))) {\n throw new HttpError(400, 'Theme ist keine CSS-Textdatei')\n }\n let css: string\n try {\n css = new TextDecoder('utf-8', { fatal: true }).decode(bytes)\n }\n catch {\n throw new HttpError(400, 'Theme ist keine gültige UTF-8-Textdatei')\n }\n\n let manifest: ThemeManifest | undefined\n const manifestFile = form?.get('manifest')\n if (manifestFile instanceof File && manifestFile.size > 0) {\n try {\n if (manifestFile.size > MAX_MANIFEST_BYTES) throw new Error('manifest too large')\n manifest = themeManifestSchema.parse(JSON.parse(await manifestFile.text()))\n }\n catch {\n throw new HttpError(400, 'Ungültiges manifest.json')\n }\n }\n\n const nameField = form?.get('name')\n const cssBaseName = cssFile.name.replace(/\\.css$/i, '')\n const name = manifest?.name\n ?? [typeof nameField === 'string' ? nameField : '', cssBaseName].map(n => n.trim().slice(0, 120)).find(Boolean)\n ?? 'Theme'\n\n const taken = new Set((await themes.listThemes()).map(t => t.id))\n const base = slugOf(name, MAX_ID_LENGTH)\n let id = base\n for (let n = 2; taken.has(id); n++) {\n const suffix = `-${n}`\n id = `${slugOf(name, MAX_ID_LENGTH - suffix.length)}${suffix}`\n }\n\n const record: Omit<ThemeRecord, 'builtin'> = { ...manifest, name, id }\n await themes.storeTheme(record, css)\n return json({ ...record, builtin: false } satisfies ThemeRecord, 201)\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AiHandlerContext, HandlerContext, HandlerRequest, HandlerResponse, authenticated, empty, enforceRateLimit, eventStream, json, readBody, text } from "./context.js";
|
|
2
|
-
import { chatBodySchema, chatPost, completeBodySchema, completePost,
|
|
3
|
-
import { assetGet, assetPost, listWorkspaceTags, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, themeDelete, themeGet, themePost, themesGet, usersGet } from "./editor.js";
|
|
4
|
-
export { type AiHandlerContext, type HandlerContext, type HandlerRequest, type HandlerResponse, assetGet, assetPost, authenticated, chatBodySchema, chatPost, completeBodySchema, completePost, empty, enforceRateLimit, eventStream, generateBodySchema, generatePost, json, listWorkspaceTags, modelsGet, readBody, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, text, themeDelete, themeGet, themePost, themesGet,
|
|
1
|
+
import { AiHandlerContext, HandlerContext, HandlerRequest, HandlerResponse, authenticated, empty, enforceRateLimit, eventStream, json, port, readBody, text } from "./context.js";
|
|
2
|
+
import { chatBodySchema, chatPost, completeBodySchema, completePost, extractBodySchema, extractPost, generateBodySchema, generatePost, modelsGet, transcriptCleanupBodySchema, transcriptCleanupPost } from "./ai.js";
|
|
3
|
+
import { assetGet, assetPost, capabilitiesGet, listWorkspaceTags, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, themeDelete, themeGet, themePost, themesGet, usersGet } from "./editor.js";
|
|
4
|
+
export { type AiHandlerContext, type HandlerContext, type HandlerRequest, type HandlerResponse, assetGet, assetPost, authenticated, capabilitiesGet, chatBodySchema, chatPost, completeBodySchema, completePost, empty, enforceRateLimit, eventStream, extractBodySchema, extractPost, generateBodySchema, generatePost, json, listWorkspaceTags, modelsGet, port, readBody, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, text, themeDelete, themeGet, themePost, themesGet, transcriptCleanupBodySchema, transcriptCleanupPost, usersGet };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { authenticated, empty, enforceRateLimit, eventStream, json, readBody, text } from "./context.js";
|
|
2
|
-
import { chatBodySchema, chatPost, completeBodySchema, completePost,
|
|
3
|
-
import { assetGet, assetPost, listWorkspaceTags, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, themeDelete, themeGet, themePost, themesGet, usersGet } from "./editor.js";
|
|
4
|
-
export { assetGet, assetPost, authenticated, chatBodySchema, chatPost, completeBodySchema, completePost, empty, enforceRateLimit, eventStream, generateBodySchema, generatePost, json, listWorkspaceTags, modelsGet, readBody, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, text, themeDelete, themeGet, themePost, themesGet,
|
|
1
|
+
import { authenticated, empty, enforceRateLimit, eventStream, json, port, readBody, text } from "./context.js";
|
|
2
|
+
import { chatBodySchema, chatPost, completeBodySchema, completePost, extractBodySchema, extractPost, generateBodySchema, generatePost, modelsGet, transcriptCleanupBodySchema, transcriptCleanupPost } from "./ai.js";
|
|
3
|
+
import { assetGet, assetPost, capabilitiesGet, listWorkspaceTags, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, themeDelete, themeGet, themePost, themesGet, usersGet } from "./editor.js";
|
|
4
|
+
export { assetGet, assetPost, authenticated, capabilitiesGet, chatBodySchema, chatPost, completeBodySchema, completePost, empty, enforceRateLimit, eventStream, extractBodySchema, extractPost, generateBodySchema, generatePost, json, listWorkspaceTags, modelsGet, port, readBody, settingsGet, settingsPut, settingsPutBodySchema, tagsGet, text, themeDelete, themeGet, themePost, themesGet, transcriptCleanupBodySchema, transcriptCleanupPost, usersGet };
|
package/dist/server/host.d.ts
CHANGED
|
@@ -1,35 +1,26 @@
|
|
|
1
|
-
import { ModelProfile, ModelRef } from "../
|
|
1
|
+
import { ModelProfile, ModelRef } from "../core/backend-types.js";
|
|
2
2
|
import "../features/ai/protocol/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { AiModels } from "./ai.js";
|
|
4
|
+
import { EditorPorts } from "./ports.js";
|
|
5
5
|
import { CollabServer } from "./collab.js";
|
|
6
|
-
import { JSONContent } from "@tiptap/core";
|
|
6
|
+
import { AnyExtension, JSONContent } from "@tiptap/core";
|
|
7
7
|
import { LanguageModel } from "ai";
|
|
8
8
|
//#region src/server/host.d.ts
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
windowMs: number;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
/** Registers the host's adapter; the collaboration server and the AI models are created from it on first use. Replaces (and closes) those of a previous adapter. */
|
|
22
|
-
export declare function setEditorAdapter(next: EditorAdapter): void;
|
|
23
|
-
export declare function useEditorAdapter(): EditorAdapter;
|
|
24
|
-
/** Sets the AI configuration and the rate limit of the process-wide server. Call before the first request. */
|
|
25
|
-
export declare function configureEditorServer(next: EditorServerConfig): void;
|
|
26
|
-
/** The process-wide collaboration server, created from the registered adapter on first use. */
|
|
9
|
+
/**
|
|
10
|
+
* Registers the host's ports for installers that read them later (the Nuxt module's server plugin installs on the first
|
|
11
|
+
* request, after every Nitro plugin has run). `options.schema`: the collaboration schema, see `CollabServerOptions.schema`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function setEditorPorts(next: EditorPorts, options?: {
|
|
14
|
+
schema?: () => AnyExtension[];
|
|
15
|
+
}): void;
|
|
16
|
+
export declare function useEditorPorts(): EditorPorts;
|
|
17
|
+
/** The process-wide collaboration server of the installed server. Throws when none is installed or `collab: false`. */
|
|
27
18
|
export declare function useCollabServer(): CollabServer;
|
|
28
|
-
/** The process-wide AI models
|
|
19
|
+
/** 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`. */
|
|
29
20
|
export declare function useAiModels(): AiModels;
|
|
30
21
|
/** Closes the process-wide collaboration server (pending stores are written first) and forgets it. */
|
|
31
22
|
export declare function closeEditorServer(): Promise<void>;
|
|
32
|
-
/** Open collaboration connections and loaded documents of this process (zeros
|
|
23
|
+
/** Open collaboration connections and loaded documents of this process (zeros when collaboration is off). */
|
|
33
24
|
export declare function collabStats(): {
|
|
34
25
|
connections: number;
|
|
35
26
|
documents: number;
|
|
@@ -46,7 +37,7 @@ export declare function resolveModel(ref: ModelRef, user: object | null): Promis
|
|
|
46
37
|
export declare function listAiModels(user: object | null): Promise<ModelProfile[]>;
|
|
47
38
|
/**
|
|
48
39
|
* Per-user limit for host-defined AI routes: the same limiter as the editor's own AI endpoints. Call first in the route.
|
|
49
|
-
* Throws 401 (`HttpError`) without a user, 429 when exceeded; returns the user the
|
|
40
|
+
* Throws 401 (`HttpError`) without a user, 429 when exceeded; returns the user the identity port authenticated, for `resolveModel`.
|
|
50
41
|
* With a Node-backed request (`event.node.res`, as h3 events have) the 429's `Retry-After` header is set on the response too.
|
|
51
42
|
*/
|
|
52
43
|
export declare function enforceAiRateLimit(request: {
|