@ecsplico/editor 2.1.1 → 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 +74 -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/code/CodeSurface.vue.d.ts +3 -3
- 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/document/DocumentSurface.vue.d.ts +3 -3
- 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 +22 -1
- package/dist/features/block-ui/block-actions.js +26 -2
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/components/BlockActionsOverlay.js +33 -4
- package/dist/features/block-ui/components/BlockActionsOverlay.js.map +1 -1
- package/dist/features/block-ui/components/BlockActionsOverlay.vue.d.ts +4 -0
- 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/index.d.ts +2 -2
- package/dist/features/block-ui/index.js +2 -2
- package/dist/features/block-ui/react/BlockActionsOverlay.d.ts +4 -0
- package/dist/features/block-ui/react/BlockActionsOverlay.js +38 -12
- package/dist/features/block-ui/react/BlockActionsOverlay.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 +10 -9
- 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 +1 -1
- package/dist/vue/AdvancedEditor.js +30 -9
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +32 -12
- 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 +11 -11
- 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 -630
- package/docs/llms.md +0 -383
- package/docs/usage.md +0 -1076
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-chat-model.js","names":[],"sources":["../../../src/features/ai/document-chat-model.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { $Fetch } from 'ofetch'\nimport { aiContextOf, CORE_TARGET_IDS, convertTargetId, conversionsOf, parseConversionTarget } from '../../core'\nimport { type Notifier, silentNotifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { ModelSlot } from '../../settings'\nimport { type AgentOpReviewState, createAgentOpReview, NO_AGENT_OPS } from './agent-op-review'\nimport type { AgentScope } from './agent-ops'\nimport type { AiTarget } from './ai-apply'\nimport { postAiSse } from './ai-sse'\nimport { documentChatText } from './document-chat'\nimport { type AgentOp, type AgentOpProblem, CHAT_LIMITS, type AiTask, type DocumentChatMessage, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\n/**\n * A block generation run from the AI sidebar's quick actions, shown as an assistant turn. Client only: it never goes\n * to `/api/ai/chat`. `markdown` is the streamed result without the task heading (shown with `withTaskHeading`, applied\n * as `taskResultMarkdown`), `target` the apply target captured when it started; only a result that streamed to the end\n * (`done`) can be applied.\n */\nexport interface DocumentChatGeneration {\n task: AiTask\n /** The instruction of a task with `needsPrompt` (empty otherwise). */\n prompt: string\n markdown: string\n target: AiTarget\n done: boolean\n}\n\n/**\n * A chat turn as shown: an assistant turn also keeps the problems of the operations the server dropped, or is a block\n * generation of the sidebar (`generation`).\n */\nexport type DocumentChatTurn =\n | Extract<DocumentChatMessage, { role: 'user' }>\n | (Extract<DocumentChatMessage, { role: 'assistant' }> & { problems?: AgentOpProblem[], generation?: DocumentChatGeneration })\n\n/** Snapshot of a conversation; every change replaces `messages`/`ops` (never mutated in place). */\nexport interface DocumentChatState {\n messages: readonly DocumentChatTurn[]\n /** Operation states by operation id. */\n ops: AgentOpReviewState['ops']\n streaming: boolean\n error: string | null\n}\n\nexport interface DocumentChatOptions {\n fetch: $Fetch\n modelRef: (slot: ModelSlot) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`): error messages, the 429 notice and the snippets of proposed operations. */\n t: Translate\n}\n\nexport interface DocumentChat {\n state: ReadonlyStore<DocumentChatState>\n /**\n * Sends `message` about the editor's current document; `scope` limits proposed edits to the selected blocks (null:\n * whole document). Returns false when it was not answered (error, rate limit, cancelled before any answer): it is\n * then removed again so the caller can restore the input.\n */\n ask: (message: string, editor: Editor, scope: AgentScope | null) => Promise<boolean>\n abort: () => void\n reset: () => void\n /** Accepts or rejects the open hunks of operation `id`. */\n decide: (editor: Editor, id: string, accept: boolean) => void\n /** Scrolls to operation `op`: its first open hunk (selected for accept/reject), else its target block. */\n reveal: (editor: Editor, op: AgentOp) => boolean\n /** Appends the assistant turn of a block generation. `generation` keeps its identity in `state`. */\n addGeneration: (generation: DocumentChatGeneration) => void\n /** Updates `generation` in place (same object) and publishes a new `messages` array. */\n updateGeneration: (generation: DocumentChatGeneration, patch: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) => void\n /** Removes the turn of `generation`. */\n removeGeneration: (generation: DocumentChatGeneration) => void\n clearError: () => void\n}\n\nconst EMPTY: DocumentChatState = { messages: [], ops: NO_AGENT_OPS, streaming: false, error: null }\n\n/**\n * Document chat state: the conversation lives only here (client side) and goes to `/api/ai/chat` in full with every\n * message, together with the document as it is at that moment and the edit scope. Proposed edit operations open an\n * in-document review; their decisions go to the model with the next message.\n */\nexport function createDocumentChat({ fetch, modelRef, notify, t }: DocumentChatOptions): DocumentChat {\n const store = createStore<DocumentChatState>(EMPTY)\n const review = createAgentOpReview({ notify: silentNotifier, t })\n let controller: AbortController | null = null\n\n const patch = (changes: Partial<DocumentChatState>) => store.set({ ...store.get(), ...changes })\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(EMPTY)\n review.reset()\n }\n\n /** Replaces the turn `turn` (by identity) with `next`; returns `next`. */\n function replaceTurn<T extends DocumentChatTurn>(turn: DocumentChatTurn, next: T): T {\n const { messages } = store.get()\n const index = messages.indexOf(turn)\n if (index !== -1) patch({ messages: messages.map((m, i) => (i === index ? next : m)) })\n return next\n }\n\n // The proposed operations live in the review (the chat shows their outcomes inline, so it needs no extra notices); its state is the chat's `ops`.\n review.state.subscribe(({ ops }) => {\n if (ops !== store.get().ops) patch({ ops })\n })\n\n async function ask(message: string, editor: Editor, scope: AgentScope | null): Promise<boolean> {\n if (store.get().streaming || !message.trim()) return false\n if (scope && !scope.ids.length) {\n patch({ error: t('ai.chat.emptyScope') })\n return false\n }\n // The block model of the system prompt: the editor's `aiContext` kinds and host blocks, and what `convert` may turn blocks into.\n // Read before any state changes: a merge error in the contributions throws here and must not leave the chat streaming.\n const { kinds, blocks, structure } = aiContextOf(editor)\n const providers = conversionsOf(editor)\n const targets = [\n ...CORE_TARGET_IDS,\n ...kinds.map(kind => convertTargetId({ type: 'composite', kind: kind.id })),\n ...blocks.filter(block => parseConversionTarget(providers, block.id) !== null).map(block => block.id),\n ]\n const own = new AbortController()\n controller = own\n const feedback = review.unreported()\n const userTurn: DocumentChatTurn = { role: 'user', content: message.trim(), ...(feedback.length ? { feedback } : {}) }\n let answer: Extract<DocumentChatTurn, { role: 'assistant' }> = { role: 'assistant', content: '' }\n // The newest chat turns (block generations are the sidebar's own); the request must start with a user message.\n const sent: DocumentChatMessage[] = [...store.get().messages, userTurn].filter(turn => turn.role === 'user' || !turn.generation).slice(-CHAT_LIMITS.messages).map(turn => turn.role === 'user'\n ? { role: 'user', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.feedback && { feedback: turn.feedback }) }\n : { role: 'assistant', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.ops?.length ? { ops: turn.ops } : {}), ...(turn.dropped ? { dropped: turn.dropped } : {}) })\n if (sent[0]?.role === 'assistant') sent.shift()\n patch({ error: null, streaming: true, messages: [...store.get().messages, userTurn, answer] })\n const received: { proposal?: { ops: AgentOp[], dropped: AgentOpProblem[] } } = {}\n const body = { messages: sent, document: documentChatText(editor), scope: scope?.ids ?? null, nextOp: review.nextOp(), model: modelRef('generate'), kinds, blocks, targets, structure }\n const outcome = await postAiSse(fetch, '/api/ai/chat', body, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) answer = replaceTurn(answer, { ...answer, content: answer.content + text })\n },\n onEvent: (event, data) => {\n if (event === 'ops' && controller === own) received.proposal = data as typeof received.proposal\n },\n })\n if (controller !== own) return false // superseded by reset()\n controller = null\n patch({ streaming: false })\n const { ops: proposed = [], dropped = [] } = outcome.status === 'done' ? received.proposal ?? {} : {}\n if (outcome.status === 'done' || (outcome.status === 'aborted' && answer.content)) {\n review.markReported(feedback.filter(({ outcome: sentOutcome }) => sentOutcome !== 'pending').map(({ op }) => op))\n if (proposed.length || dropped.length) {\n answer = replaceTurn(answer, { ...answer, ops: proposed, dropped: dropped.length, problems: dropped })\n review.advanceOp(proposed.length)\n if (!editor.isDestroyed) review.propose(editor, proposed, scope)\n }\n return true\n }\n // Drop the unanswered turn: a later message should not carry a dangling one in its history.\n patch({ messages: store.get().messages.slice(0, -2) })\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n else if (outcome.status === 'error') patch({ error: outcome.message })\n return false\n }\n\n const decide = review.decide\n const reveal = review.reveal\n\n function addGeneration(generation: DocumentChatGeneration) {\n patch({ error: null, messages: [...store.get().messages, { role: 'assistant', content: '', generation }] })\n }\n\n function updateGeneration(generation: DocumentChatGeneration, changes: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) {\n Object.assign(generation, changes)\n const { messages } = store.get()\n patch({ messages: messages.map(turn => turn.role === 'assistant' && turn.generation === generation ? { ...turn } : turn) })\n }\n\n function removeGeneration(generation: DocumentChatGeneration) {\n const { messages } = store.get()\n const index = messages.findIndex(turn => turn.role === 'assistant' && turn.generation === generation)\n if (index !== -1) patch({ messages: messages.filter((_, i) => i !== index) })\n }\n\n function clearError() {\n if (store.get().error !== null) patch({ error: null })\n }\n\n return { state: store, ask, abort, reset, decide, reveal, addGeneration, updateGeneration, removeGeneration, clearError }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"document-chat-model.js","names":[],"sources":["../../../src/features/ai/document-chat-model.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { EditorBackend } from '../../core/backend'\nimport { aiContextOf, CORE_TARGET_IDS, convertTargetId, conversionsOf, parseConversionTarget } from '../../core'\nimport { type Notifier, silentNotifier } from '../../core/notify'\nimport { createStore, type ReadonlyStore } from '../../core/store'\nimport type { Translate } from '../../i18n'\nimport type { ModelSlot } from '../../settings'\nimport { type AgentOpReviewState, createAgentOpReview, NO_AGENT_OPS } from './agent-op-review'\nimport type { AgentScope } from './agent-ops'\nimport type { AiTarget } from './ai-apply'\nimport { postAiSse } from './ai-sse'\nimport { documentChatText } from './document-chat'\nimport { type AgentOp, type AgentOpProblem, CHAT_LIMITS, type AiTask, type DocumentChatMessage, type ModelRef } from './protocol'\nimport { notifyAiRateLimited } from './rate-limit'\n\n/**\n * A block generation run from the AI sidebar's quick actions, shown as an assistant turn. Client only: it never goes\n * to the `chat` stream. `markdown` is the streamed result without the task heading (shown with `withTaskHeading`, applied\n * as `taskResultMarkdown`), `target` the apply target captured when it started; only a result that streamed to the end\n * (`done`) can be applied.\n */\nexport interface DocumentChatGeneration {\n task: AiTask\n /** The instruction of a task with `needsPrompt` (empty otherwise). */\n prompt: string\n markdown: string\n target: AiTarget\n done: boolean\n}\n\n/**\n * A chat turn as shown: an assistant turn also keeps the problems of the operations the server dropped, or is a block\n * generation of the sidebar (`generation`).\n */\nexport type DocumentChatTurn =\n | Extract<DocumentChatMessage, { role: 'user' }>\n | (Extract<DocumentChatMessage, { role: 'assistant' }> & { problems?: AgentOpProblem[], generation?: DocumentChatGeneration })\n\n/** Snapshot of a conversation; every change replaces `messages`/`ops` (never mutated in place). */\nexport interface DocumentChatState {\n messages: readonly DocumentChatTurn[]\n /** Operation states by operation id. */\n ops: AgentOpReviewState['ops']\n streaming: boolean\n error: string | null\n}\n\nexport interface DocumentChatOptions {\n /** `EditorContext.backend.ai`; without it every question ends in the error state (501), like a server without AI. */\n ai: EditorBackend['ai']\n modelRef: (slot: ModelSlot) => ModelRef\n /** Receives the 429 notice. */\n notify: Notifier\n /** The app's translator (`EditorContext.t`): error messages, the 429 notice and the snippets of proposed operations. */\n t: Translate\n}\n\nexport interface DocumentChat {\n state: ReadonlyStore<DocumentChatState>\n /**\n * Sends `message` about the editor's current document; `scope` limits proposed edits to the selected blocks (null:\n * whole document). Returns false when it was not answered (error, rate limit, cancelled before any answer): it is\n * then removed again so the caller can restore the input.\n */\n ask: (message: string, editor: Editor, scope: AgentScope | null) => Promise<boolean>\n abort: () => void\n reset: () => void\n /** Accepts or rejects the open hunks of operation `id`. */\n decide: (editor: Editor, id: string, accept: boolean) => void\n /** Scrolls to operation `op`: its first open hunk (selected for accept/reject), else its target block. */\n reveal: (editor: Editor, op: AgentOp) => boolean\n /** Appends the assistant turn of a block generation. `generation` keeps its identity in `state`. */\n addGeneration: (generation: DocumentChatGeneration) => void\n /** Updates `generation` in place (same object) and publishes a new `messages` array. */\n updateGeneration: (generation: DocumentChatGeneration, patch: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) => void\n /** Removes the turn of `generation`. */\n removeGeneration: (generation: DocumentChatGeneration) => void\n clearError: () => void\n}\n\nconst EMPTY: DocumentChatState = { messages: [], ops: NO_AGENT_OPS, streaming: false, error: null }\n\n/**\n * Document chat state: the conversation lives only here (client side) and goes to the backend's `chat` stream in full with every\n * message, together with the document as it is at that moment and the edit scope. Proposed edit operations open an\n * in-document review; their decisions go to the model with the next message.\n */\nexport function createDocumentChat({ ai, modelRef, notify, t }: DocumentChatOptions): DocumentChat {\n const store = createStore<DocumentChatState>(EMPTY)\n const review = createAgentOpReview({ notify: silentNotifier, t })\n let controller: AbortController | null = null\n\n const patch = (changes: Partial<DocumentChatState>) => store.set({ ...store.get(), ...changes })\n\n function abort() {\n controller?.abort()\n }\n\n function reset() {\n abort()\n controller = null\n store.set(EMPTY)\n review.reset()\n }\n\n /** Replaces the turn `turn` (by identity) with `next`; returns `next`. */\n function replaceTurn<T extends DocumentChatTurn>(turn: DocumentChatTurn, next: T): T {\n const { messages } = store.get()\n const index = messages.indexOf(turn)\n if (index !== -1) patch({ messages: messages.map((m, i) => (i === index ? next : m)) })\n return next\n }\n\n // The proposed operations live in the review (the chat shows their outcomes inline, so it needs no extra notices); its state is the chat's `ops`.\n review.state.subscribe(({ ops }) => {\n if (ops !== store.get().ops) patch({ ops })\n })\n\n async function ask(message: string, editor: Editor, scope: AgentScope | null): Promise<boolean> {\n if (store.get().streaming || !message.trim()) return false\n if (scope && !scope.ids.length) {\n patch({ error: t('ai.chat.emptyScope') })\n return false\n }\n // The block model of the system prompt: the editor's `aiContext` kinds and host blocks, and what `convert` may turn blocks into.\n // Read before any state changes: a merge error in the contributions throws here and must not leave the chat streaming.\n const { kinds, blocks, structure } = aiContextOf(editor)\n const providers = conversionsOf(editor)\n const targets = [\n ...CORE_TARGET_IDS,\n ...kinds.map(kind => convertTargetId({ type: 'composite', kind: kind.id })),\n ...blocks.filter(block => parseConversionTarget(providers, block.id) !== null).map(block => block.id),\n ]\n const own = new AbortController()\n controller = own\n const feedback = review.unreported()\n const userTurn: DocumentChatTurn = { role: 'user', content: message.trim(), ...(feedback.length ? { feedback } : {}) }\n let answer: Extract<DocumentChatTurn, { role: 'assistant' }> = { role: 'assistant', content: '' }\n // The newest chat turns (block generations are the sidebar's own); the request must start with a user message.\n const sent: DocumentChatMessage[] = [...store.get().messages, userTurn].filter(turn => turn.role === 'user' || !turn.generation).slice(-CHAT_LIMITS.messages).map(turn => turn.role === 'user'\n ? { role: 'user', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.feedback && { feedback: turn.feedback }) }\n : { role: 'assistant', content: turn.content.slice(0, CHAT_LIMITS.message), ...(turn.ops?.length ? { ops: turn.ops } : {}), ...(turn.dropped ? { dropped: turn.dropped } : {}) })\n if (sent[0]?.role === 'assistant') sent.shift()\n patch({ error: null, streaming: true, messages: [...store.get().messages, userTurn, answer] })\n const received: { proposal?: { ops: AgentOp[], dropped: AgentOpProblem[] } } = {}\n const body = { messages: sent, document: documentChatText(editor), scope: scope?.ids ?? null, nextOp: review.nextOp(), model: modelRef('generate'), kinds, blocks, targets, structure }\n const outcome = await postAiSse(ai, 'chat', body, {\n signal: own.signal,\n t,\n onDelta: (text) => {\n if (controller === own) answer = replaceTurn(answer, { ...answer, content: answer.content + text })\n },\n onEvent: (event, data) => {\n if (event === 'ops' && controller === own) received.proposal = data as typeof received.proposal\n },\n })\n if (controller !== own) return false // superseded by reset()\n controller = null\n patch({ streaming: false })\n const { ops: proposed = [], dropped = [] } = outcome.status === 'done' ? received.proposal ?? {} : {}\n if (outcome.status === 'done' || (outcome.status === 'aborted' && answer.content)) {\n review.markReported(feedback.filter(({ outcome: sentOutcome }) => sentOutcome !== 'pending').map(({ op }) => op))\n if (proposed.length || dropped.length) {\n answer = replaceTurn(answer, { ...answer, ops: proposed, dropped: dropped.length, problems: dropped })\n review.advanceOp(proposed.length)\n if (!editor.isDestroyed) review.propose(editor, proposed, scope)\n }\n return true\n }\n // Drop the unanswered turn: a later message should not carry a dangling one in its history.\n patch({ messages: store.get().messages.slice(0, -2) })\n if (outcome.status === 'rate-limited') notifyAiRateLimited(notify, t)\n else if (outcome.status === 'error') patch({ error: outcome.message })\n return false\n }\n\n const decide = review.decide\n const reveal = review.reveal\n\n function addGeneration(generation: DocumentChatGeneration) {\n patch({ error: null, messages: [...store.get().messages, { role: 'assistant', content: '', generation }] })\n }\n\n function updateGeneration(generation: DocumentChatGeneration, changes: Partial<Pick<DocumentChatGeneration, 'markdown' | 'done'>>) {\n Object.assign(generation, changes)\n const { messages } = store.get()\n patch({ messages: messages.map(turn => turn.role === 'assistant' && turn.generation === generation ? { ...turn } : turn) })\n }\n\n function removeGeneration(generation: DocumentChatGeneration) {\n const { messages } = store.get()\n const index = messages.findIndex(turn => turn.role === 'assistant' && turn.generation === generation)\n if (index !== -1) patch({ messages: messages.filter((_, i) => i !== index) })\n }\n\n function clearError() {\n if (store.get().error !== null) patch({ error: null })\n }\n\n return { state: store, ask, abort, reset, decide, reveal, addGeneration, updateGeneration, removeGeneration, clearError }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { parseBlocks } from "../../core/pipeline.js";
|
|
1
2
|
import { blockIdOf, findBlockById } from "../../core/schema/block-ids.js";
|
|
2
3
|
import { isBlockContainer, isChromeNode } from "../../core/schema/node-flags.js";
|
|
3
4
|
import { isWrapperBlock } from "../../core/convert.js";
|
|
4
|
-
import { parseBlocks } from "../../core/pipeline.js";
|
|
5
5
|
import { TRUNCATED_LINE, blockLine, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, singleBlockLine } from "./protocol/block-lines.js";
|
|
6
6
|
import { CHAT_LIMITS, CITATION_HREF_PREFIX, citationBlockId } from "./protocol/chat.js";
|
|
7
7
|
import { Fragment } from "@tiptap/pm/model";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AiReview } from "./ai-review.js";
|
|
2
2
|
import { AiSelectionHighlight } from "./ai-selection-highlight.js";
|
|
3
|
-
import { aiBlockAction,
|
|
3
|
+
import { aiBlockAction, aiCommand } from "./block-actions.js";
|
|
4
4
|
import { completionFetcher, ghostAutocomplete } from "./ghost-autocomplete.js";
|
|
5
5
|
import { modelProfiles } from "./model-profiles.js";
|
|
6
6
|
import { Extension } from "@tiptap/core";
|
|
@@ -16,13 +16,19 @@ function sidebarShortcut(ctx) {
|
|
|
16
16
|
} })
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
const aiToolbarGroups = [{
|
|
20
|
+
id: "ai",
|
|
21
|
+
order: 1e3,
|
|
22
|
+
labelKey: "palette.group.ai"
|
|
23
|
+
}];
|
|
24
|
+
function aiCommands(ctx) {
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
24
27
|
id: "sidebar",
|
|
25
|
-
|
|
28
|
+
toolbar: {
|
|
29
|
+
group: "ai",
|
|
30
|
+
order: 10
|
|
31
|
+
},
|
|
26
32
|
icon: "i-lucide-panel-right",
|
|
27
33
|
label: ctx.t("ai.toolbar.sidebar"),
|
|
28
34
|
kbds: [
|
|
@@ -31,9 +37,13 @@ function aiToolbar(ctx) {
|
|
|
31
37
|
"k"
|
|
32
38
|
],
|
|
33
39
|
run: () => ctx.ui.toggleSidebar()
|
|
34
|
-
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
35
42
|
id: "autocomplete",
|
|
36
|
-
|
|
43
|
+
toolbar: {
|
|
44
|
+
group: "ai",
|
|
45
|
+
order: 20
|
|
46
|
+
},
|
|
37
47
|
icon: "i-lucide-wand-sparkles",
|
|
38
48
|
label: ctx.t("ai.toolbar.autocomplete"),
|
|
39
49
|
active: () => ctx.settings.get().ai.autocomplete,
|
|
@@ -42,8 +52,9 @@ function aiToolbar(ctx) {
|
|
|
42
52
|
s.ai.autocomplete = !s.ai.autocomplete;
|
|
43
53
|
});
|
|
44
54
|
}
|
|
45
|
-
}
|
|
46
|
-
|
|
55
|
+
},
|
|
56
|
+
aiCommand(ctx)
|
|
57
|
+
];
|
|
47
58
|
}
|
|
48
59
|
/**
|
|
49
60
|
* AI on a core editor: the selection menu and `/ai` (rewrite and generate at the caret), in-document review of
|
|
@@ -58,6 +69,8 @@ function aiToolbar(ctx) {
|
|
|
58
69
|
function aiFeatureCore(ui) {
|
|
59
70
|
return {
|
|
60
71
|
id: "ai",
|
|
72
|
+
dimension: "ai",
|
|
73
|
+
backend: ["ai"],
|
|
61
74
|
extensions: (ctx) => [
|
|
62
75
|
AiSelectionHighlight,
|
|
63
76
|
AiReview,
|
|
@@ -65,15 +78,15 @@ function aiFeatureCore(ui) {
|
|
|
65
78
|
settings: ctx.settings,
|
|
66
79
|
debounce: GHOST_DEBOUNCE,
|
|
67
80
|
fetchSuggestion: completionFetcher({
|
|
68
|
-
|
|
81
|
+
ai: ctx.backend.ai,
|
|
69
82
|
modelRef: modelProfiles(ctx.app).modelRef
|
|
70
83
|
})
|
|
71
84
|
}),
|
|
72
85
|
sidebarShortcut(ctx)
|
|
73
86
|
],
|
|
74
|
-
|
|
87
|
+
toolbarGroups: aiToolbarGroups,
|
|
88
|
+
commands: aiCommands,
|
|
75
89
|
blockActions: (ctx) => [aiBlockAction(ctx)],
|
|
76
|
-
slashCommands: (ctx) => [aiSlashCommand(ctx)],
|
|
77
90
|
overlays: [ui.selectionMenu, ui.reviewBar],
|
|
78
91
|
settingsTabs: (ctx) => [{
|
|
79
92
|
id: "ai",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/ai/feature.ts"],"sourcesContent":["import { type AnyExtension, Extension } from '@tiptap/core'\nimport type { EditorFeature, FeatureContext,
|
|
1
|
+
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/ai/feature.ts"],"sourcesContent":["import { type AnyExtension, Extension } from '@tiptap/core'\nimport type { CommandDef, EditorFeature, FeatureContext, ToolbarGroupDef } from '../../core'\nimport { AiReview } from './ai-review'\nimport { AiSelectionHighlight } from './ai-selection-highlight'\nimport { aiBlockAction, aiCommand } from './block-actions'\nimport { completionFetcher, ghostAutocomplete } from './ghost-autocomplete'\nimport { modelProfiles } from './model-profiles'\n\n/** What a framework binding supplies to the AI feature: the components of its UI layer. */\nexport interface AiUi<C> {\n /** The selection bubble menu and the caret menu (rewrite, translate, generate; preview and apply). */\n selectionMenu: C\n /** The accept/reject bar of the in-document review. */\n reviewBar: C\n /** The „KI“ settings tab component of the framework. */\n settingsTab: C\n}\n\n/** Pause after the last keystroke before an autocomplete suggestion is requested (ms). */\nconst GHOST_DEBOUNCE = 500\n\n// Mod-Shift-k toggles this editor's AI sidebar while the editor has focus (shown on the toolbar's „KI-Seitenleiste“).\nfunction sidebarShortcut(ctx: FeatureContext): AnyExtension {\n return Extension.create({\n name: 'aiSidebarShortcut',\n addKeyboardShortcuts: () => ({\n 'Mod-Shift-k': () => {\n ctx.ui.toggleSidebar()\n return true\n },\n }),\n })\n}\n\nconst aiToolbarGroups: ToolbarGroupDef[] = [{ id: 'ai', order: 1000, labelKey: 'palette.group.ai' }]\n\nfunction aiCommands<C>(ctx: FeatureContext): CommandDef<C>[] {\n return [\n {\n id: 'sidebar',\n toolbar: { group: 'ai', order: 10 },\n icon: 'i-lucide-panel-right',\n label: ctx.t('ai.toolbar.sidebar'),\n kbds: ['meta', 'shift', 'k'],\n run: () => ctx.ui.toggleSidebar(),\n },\n {\n id: 'autocomplete',\n toolbar: { group: 'ai', order: 20 },\n icon: 'i-lucide-wand-sparkles',\n label: ctx.t('ai.toolbar.autocomplete'),\n active: () => ctx.settings.get().ai.autocomplete,\n run: () => { ctx.settings.update((s) => { s.ai.autocomplete = !s.ai.autocomplete }) },\n },\n aiCommand(ctx),\n ]\n}\n\n/**\n * AI on a core editor: the selection menu and `/ai` (rewrite and generate at the caret), in-document review of\n * rewrites, ghost autocomplete, the model profiles and the `ai` settings tab, the „Mit KI bearbeiten“ block action, the\n * toolbar group (sidebar toggle, autocomplete switch) and Mod-Shift-k. The sidebar (`AiSidebar`) and the model switch\n * are components the host places. Reads the document model through the merged `aiContext` (kinds, host blocks,\n * structure) and the `formatAiResult` contribution of other\n * features (defaults: no kinds, no blocks, flat structure) and maps positions through the preset's anchor resolver, so it imports\n * no composites, transcript, media, mentions, collaboration or Yjs code. The model profiles come from the app context\n * (`modelProfiles(ctx.app)`), shared by all editors of the app.\n */\nexport function aiFeatureCore<C>(ui: AiUi<C>): EditorFeature<C> {\n return {\n id: 'ai',\n dimension: 'ai',\n backend: ['ai'],\n extensions: ctx => [\n AiSelectionHighlight,\n AiReview,\n ghostAutocomplete({\n settings: ctx.settings,\n debounce: GHOST_DEBOUNCE,\n fetchSuggestion: completionFetcher({ ai: ctx.backend.ai, modelRef: modelProfiles(ctx.app).modelRef }),\n }),\n sidebarShortcut(ctx),\n ],\n toolbarGroups: aiToolbarGroups,\n commands: aiCommands<C>,\n blockActions: ctx => [aiBlockAction(ctx)],\n overlays: [ui.selectionMenu, ui.reviewBar],\n settingsTabs: ctx => [{ id: 'ai', label: ctx.t('ai.tab'), icon: 'i-lucide-sparkles', component: ui.settingsTab }],\n }\n}\n"],"mappings":";;;;;;;;AAmBA,MAAM,iBAAiB;AAGvB,SAAS,gBAAgB,KAAmC;CAC1D,OAAO,UAAU,OAAO;EACtB,MAAM;EACN,6BAA6B,EAC3B,qBAAqB;GACnB,IAAI,GAAG,cAAc;GACrB,OAAO;EACT,EACF;CACF,CAAC;AACH;AAEA,MAAM,kBAAqC,CAAC;CAAE,IAAI;CAAM,OAAO;CAAM,UAAU;AAAmB,CAAC;AAEnG,SAAS,WAAc,KAAsC;CAC3D,OAAO;EACL;GACE,IAAI;GACJ,SAAS;IAAE,OAAO;IAAM,OAAO;GAAG;GAClC,MAAM;GACN,OAAO,IAAI,EAAE,oBAAoB;GACjC,MAAM;IAAC;IAAQ;IAAS;GAAG;GAC3B,WAAW,IAAI,GAAG,cAAc;EAClC;EACA;GACE,IAAI;GACJ,SAAS;IAAE,OAAO;IAAM,OAAO;GAAG;GAClC,MAAM;GACN,OAAO,IAAI,EAAE,yBAAyB;GACtC,cAAc,IAAI,SAAS,IAAI,CAAC,CAAC,GAAG;GACpC,WAAW;IAAE,IAAI,SAAS,QAAQ,MAAM;KAAE,EAAE,GAAG,eAAe,CAAC,EAAE,GAAG;IAAa,CAAC;GAAE;EACtF;EACA,UAAU,GAAG;CACf;AACF;;;;;;;;;;;AAYA,SAAgB,cAAiB,IAA+B;CAC9D,OAAO;EACL,IAAI;EACJ,WAAW;EACX,SAAS,CAAC,IAAI;EACd,aAAY,QAAO;GACjB;GACA;GACA,kBAAkB;IAChB,UAAU,IAAI;IACd,UAAU;IACV,iBAAiB,kBAAkB;KAAE,IAAI,IAAI,QAAQ;KAAI,UAAU,cAAc,IAAI,GAAG,CAAC,CAAC;IAAS,CAAC;GACtG,CAAC;GACD,gBAAgB,GAAG;EACrB;EACA,eAAe;EACf,UAAU;EACV,eAAc,QAAO,CAAC,cAAc,GAAG,CAAC;EACxC,UAAU,CAAC,GAAG,eAAe,GAAG,SAAS;EACzC,eAAc,QAAO,CAAC;GAAE,IAAI;GAAM,OAAO,IAAI,EAAE,QAAQ;GAAG,MAAM;GAAqB,WAAW,GAAG;EAAY,CAAC;CAClH;AACF"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { Store } from "../../core/store.js";
|
|
1
2
|
import { ModelSlot } from "../../settings/values.js";
|
|
2
3
|
import { EditorSettings } from "../../settings/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import { ModelRef } from "
|
|
4
|
+
import { EditorBackend } from "../../core/backend.js";
|
|
5
|
+
import { ModelRef } from "../../core/backend-types.js";
|
|
5
6
|
import "./protocol/index.js";
|
|
6
7
|
import { Extension } from "@tiptap/core";
|
|
7
8
|
import { PluginKey } from "@tiptap/pm/state";
|
|
8
|
-
import { $Fetch } from "ofetch";
|
|
9
9
|
//#region src/features/ai/ghost-autocomplete.d.ts
|
|
10
10
|
export interface GhostAutocompleteOptions {
|
|
11
11
|
/** The app's shared settings: `ai.autocomplete` switches the suggestions on. */
|
|
@@ -27,12 +27,13 @@ interface GhostState {
|
|
|
27
27
|
activity: number;
|
|
28
28
|
}
|
|
29
29
|
export declare const ghostAutocompleteKey: PluginKey<GhostState>;
|
|
30
|
-
/** What the suggestion source needs of the app: its
|
|
30
|
+
/** What the suggestion source needs of the app: its AI port and the model profile of a slot (`useModelProfiles`). */
|
|
31
31
|
export interface CompletionHost {
|
|
32
|
-
|
|
32
|
+
/** `EditorContext.backend.ai`; without it there are no suggestions. */
|
|
33
|
+
ai: EditorBackend['ai'];
|
|
33
34
|
modelRef: (slot: ModelSlot) => ModelRef;
|
|
34
35
|
}
|
|
35
|
-
/** The suggestion source of the app behind `host`: `
|
|
36
|
+
/** The suggestion source of the app behind `host`: `ai.complete` on the profile of the `complete` slot. */
|
|
36
37
|
export declare function completionFetcher(host: CompletionHost): GhostAutocompleteOptions['fetchSuggestion'];
|
|
37
38
|
/**
|
|
38
39
|
* Inline AI autocomplete: after a typing pause at the end of a non-empty textblock, a short continuation is shown as grey
|
|
@@ -28,17 +28,14 @@ function mapSuggestion(suggestion, tr, oldState, newState) {
|
|
|
28
28
|
pos: mapped.pos
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
/** The suggestion source of the app behind `host`: `
|
|
31
|
+
/** The suggestion source of the app behind `host`: `ai.complete` on the profile of the `complete` slot. */
|
|
32
32
|
function completionFetcher(host) {
|
|
33
33
|
return async (before, signal) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
signal
|
|
41
|
-
});
|
|
34
|
+
if (!host.ai) return "";
|
|
35
|
+
const { text } = await host.ai.complete({
|
|
36
|
+
before,
|
|
37
|
+
model: host.modelRef("complete")
|
|
38
|
+
}, signal);
|
|
42
39
|
return text;
|
|
43
40
|
};
|
|
44
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ghost-autocomplete.js","names":[],"sources":["../../../src/features/ai/ghost-autocomplete.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { type EditorState, Plugin, PluginKey, TextSelection, type Transaction } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet, type EditorView } from '@tiptap/pm/view'\nimport type { Store } from '../../core/store'\nimport { anchorResolverOf, hasAbility } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport type { EditorSettings, ModelSlot } from '../../settings'\nimport type { $Fetch } from 'ofetch'\nimport { AI_COMPLETE_LIMITS, type ModelRef } from './protocol'\n\nexport interface GhostAutocompleteOptions {\n /** The app's shared settings: `ai.autocomplete` switches the suggestions on. */\n settings: Store<EditorSettings>\n /** Pause after the last keystroke before a suggestion is requested (ms). */\n debounce: number\n /** Fetches a continuation of `before` (document text up to the caret); '' for none. Must honour `signal`. */\n fetchSuggestion: (before: string, signal: AbortSignal) => Promise<string>\n}\n\ninterface Suggestion { text: string, pos: number }\ninterface GhostState {\n suggestion: Suggestion | null\n /** Bumped by local edits: each bump starts a new pause, so at most one request follows it. */\n edits: number\n /** Bumped by local edits and caret moves: an in-flight request is stale once this changed. */\n activity: number\n}\ntype GhostMeta = { type: 'show', suggestion: Suggestion } | { type: 'clear' }\n\nexport const ghostAutocompleteKey = new PluginKey<GhostState>('ghostAutocomplete')\n\n/** Text of the caret's textblock, when the caret is an empty selection at the end of a non-empty, non-code textblock. */\nfunction blockTextAtEnd(state: EditorState): string | null {\n const { selection } = state\n if (!(selection instanceof TextSelection) || !selection.empty) return null\n const { $head } = selection\n const block = $head.parent\n if (!block.isTextblock || block.type.spec.code || $head.parentOffset !== block.content.size) return null\n const text = block.textContent\n return text.trim() ? text : null\n}\n\n/** Keeps a suggestion across a collaborator's change only if the caret block is untouched and the caret stayed with it. */\nfunction mapSuggestion(suggestion: Suggestion, tr: Transaction, oldState: EditorState, newState: EditorState): Suggestion | null {\n const mapped = tr.mapping.mapResult(suggestion.pos)\n if (mapped.deleted || newState.selection.head !== mapped.pos) return null\n const text = blockTextAtEnd(newState)\n if (text === null || text !== oldState.doc.resolve(suggestion.pos).parent.textContent) return null\n return { text: suggestion.text, pos: mapped.pos }\n}\n\n/** What the suggestion source needs of the app: its HTTP client and the model profile of a slot (`useModelProfiles`). */\nexport interface CompletionHost {\n fetch: $Fetch\n modelRef: (slot: ModelSlot) => ModelRef\n}\n\n/** The suggestion source of the app behind `host`: `POST /api/ai/complete` on the profile of the `complete` slot. */\nexport function completionFetcher(host: CompletionHost): GhostAutocompleteOptions['fetchSuggestion'] {\n return async (before, signal) => {\n const { text } = await host.fetch<{ text: string }>('/api/ai/complete', { method: 'POST', body: { before, model: host.modelRef('complete') }, signal })\n return text\n }\n}\n\n/**\n * Inline AI autocomplete: after a typing pause at the end of a non-empty textblock, a short continuation is shown as grey\n * ghost text after the caret. Tab accepts it as normal text, Esc dismisses it; typing, caret moves and blur clear it.\n * The suggestion is a local widget decoration — never part of the document, so collaborators never see it.\n * On while the editor setting `ai.autocomplete` is (see `useEditorSettings`); turning it off clears the ghost.\n * Client-only: not part of `schemaExtensions()`.\n */\nexport const ghostAutocomplete = (options: GhostAutocompleteOptions) => Extension.create({\n name: 'ghostAutocomplete',\n // Runs its Tab/Escape bindings before ListItem's Tab (sinkListItem); both return false without a suggestion.\n priority: 1000,\n\n addKeyboardShortcuts() {\n return {\n Tab: ({ editor }) => {\n const suggestion = ghostAutocompleteKey.getState(editor.state)?.suggestion\n const { selection } = editor.state\n if (!suggestion || !selection.empty || selection.head !== suggestion.pos) return false\n const end = suggestion.pos + suggestion.text.length\n const tr = editor.state.tr.insertText(suggestion.text, suggestion.pos)\n editor.view.dispatch(tr.setSelection(TextSelection.create(tr.doc, end)).scrollIntoView())\n return true\n },\n Escape: ({ editor }) => {\n if (!ghostAutocompleteKey.getState(editor.state)?.suggestion) return false\n editor.view.dispatch(editor.state.tr.setMeta(ghostAutocompleteKey, { type: 'clear' } satisfies GhostMeta))\n return true\n },\n }\n },\n\n addProseMirrorPlugins() {\n const { editor } = this\n const { settings } = options\n let timer: number | undefined\n let inFlight: AbortController | null = null\n\n function cancel() {\n clearTimeout(timer)\n timer = undefined\n inFlight?.abort()\n inFlight = null\n }\n\n function eligible(view: EditorView): boolean {\n const { state } = view\n if (!settings.get().ai.autocomplete || !editor.isEditable || !view.hasFocus() || view.composing || blockTextAtEnd(state) === null) return false\n if (!hasAbility(editor, ABILITY.aiAutocomplete)) return false\n // No ghost while an @tiptap/suggestion popup (e.g. `@` mentions) is open: its state is `{ active, query, … }`.\n return !state.plugins.some((plugin) => {\n const pluginState: unknown = plugin.getState(state)\n return typeof pluginState === 'object' && pluginState !== null && 'query' in pluginState && 'active' in pluginState && pluginState.active === true\n })\n }\n\n async function request(view: EditorView, activity: number) {\n timer = undefined\n const { state } = view\n if (ghostAutocompleteKey.getState(state)?.activity !== activity || !eligible(view)) return\n const pos = state.selection.head\n const blockText = blockTextAtEnd(state)\n // Positions count at least one per character, so twice the limit in positions covers the character limit.\n const before = state.doc.textBetween(Math.max(0, pos - AI_COMPLETE_LIMITS.before * 2), pos, '\\n\\n').slice(-AI_COMPLETE_LIMITS.before)\n const controller = new AbortController()\n inFlight = controller\n let text: string\n try {\n text = await options.fetchSuggestion(before, controller.signal)\n } catch {\n // Aborted, rate-limited (429) or failed: autocomplete is a silent nicety, so no ghost and no toast.\n return\n } finally {\n if (inFlight === controller) inFlight = null\n }\n if (controller.signal.aborted || !text || view.isDestroyed) return\n // Stale-response guard: no local edit or caret move since the request, and the caret block text is unchanged\n // (a collaborator's change elsewhere may have shifted the caret position).\n const now = view.state\n if (ghostAutocompleteKey.getState(now)?.activity !== activity || !eligible(view) || blockTextAtEnd(now) !== blockText) return\n view.dispatch(now.tr.setMeta(ghostAutocompleteKey, { type: 'show', suggestion: { text, pos: now.selection.head } } satisfies GhostMeta))\n }\n\n return [\n new Plugin<GhostState>({\n key: ghostAutocompleteKey,\n state: {\n init: () => ({ suggestion: null, edits: 0, activity: 0 }),\n apply(tr, prev, oldState, newState) {\n const meta = tr.getMeta(ghostAutocompleteKey) as GhostMeta | undefined\n // A collaborator's edit (the collaboration feature's resolver tells); the user's own undo/redo is not remote.\n const remote = anchorResolverOf(editor)?.isRemoteChange(tr) ?? false\n const edited = tr.docChanged && !remote\n const moved = tr.selectionSet && !remote\n let suggestion = prev.suggestion\n if (meta?.type === 'show') suggestion = meta.suggestion\n else if (meta?.type === 'clear' || edited || moved) suggestion = null\n else if (suggestion && tr.docChanged) suggestion = mapSuggestion(suggestion, tr, oldState, newState)\n if (suggestion === prev.suggestion && !edited && !moved) return prev\n return { suggestion, edits: prev.edits + (edited ? 1 : 0), activity: prev.activity + (edited || moved ? 1 : 0) }\n },\n },\n props: {\n decorations(state) {\n const suggestion = ghostAutocompleteKey.getState(state)?.suggestion\n if (!suggestion) return null\n const widget = Decoration.widget(suggestion.pos, () => {\n const span = document.createElement('span')\n span.className = 'ghost-autocomplete'\n span.contentEditable = 'false'\n span.setAttribute('aria-hidden', 'true')\n span.textContent = suggestion.text\n return span\n }, { side: 1, key: `ghost:${suggestion.text}`, ignoreSelection: true })\n return DecorationSet.create(state.doc, [widget])\n },\n handleDOMEvents: {\n blur(view) {\n cancel()\n if (ghostAutocompleteKey.getState(view.state)?.suggestion) {\n view.dispatch(view.state.tr.setMeta(ghostAutocompleteKey, { type: 'clear' } satisfies GhostMeta))\n }\n return false\n },\n },\n },\n view: (view) => {\n // Turned off (toolbar or settings): drop the pending request and the shown ghost right away.\n let last = settings.get().ai.autocomplete\n const stop = settings.subscribe((value) => {\n const enabled = value.ai.autocomplete\n if (enabled === last) return\n last = enabled\n if (enabled || view.isDestroyed) return\n cancel()\n if (ghostAutocompleteKey.getState(view.state)?.suggestion) {\n view.dispatch(view.state.tr.setMeta(ghostAutocompleteKey, { type: 'clear' } satisfies GhostMeta))\n }\n })\n return {\n update(view, prevState) {\n const next = ghostAutocompleteKey.getState(view.state)\n const prev = ghostAutocompleteKey.getState(prevState)\n if (!next || !prev) return\n if (!settings.get().ai.autocomplete) return cancel()\n if (next.activity === prev.activity) return\n // Typing or a caret move: drop the pending pause and abort the in-flight request.\n cancel()\n // Only a local edit starts a new pause; caret moves alone never request.\n if (next.edits === prev.edits || !eligible(view)) return\n const activity = next.activity\n timer = window.setTimeout(() => void request(view, activity), options.debounce)\n },\n destroy() {\n stop()\n cancel()\n },\n }\n },\n }),\n ]\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ghost-autocomplete.js","names":[],"sources":["../../../src/features/ai/ghost-autocomplete.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { type EditorState, Plugin, PluginKey, TextSelection, type Transaction } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet, type EditorView } from '@tiptap/pm/view'\nimport type { Store } from '../../core/store'\nimport { anchorResolverOf, hasAbility } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport type { EditorSettings, ModelSlot } from '../../settings'\nimport type { EditorBackend } from '../../core/backend'\nimport { AI_COMPLETE_LIMITS, type ModelRef } from './protocol'\n\nexport interface GhostAutocompleteOptions {\n /** The app's shared settings: `ai.autocomplete` switches the suggestions on. */\n settings: Store<EditorSettings>\n /** Pause after the last keystroke before a suggestion is requested (ms). */\n debounce: number\n /** Fetches a continuation of `before` (document text up to the caret); '' for none. Must honour `signal`. */\n fetchSuggestion: (before: string, signal: AbortSignal) => Promise<string>\n}\n\ninterface Suggestion { text: string, pos: number }\ninterface GhostState {\n suggestion: Suggestion | null\n /** Bumped by local edits: each bump starts a new pause, so at most one request follows it. */\n edits: number\n /** Bumped by local edits and caret moves: an in-flight request is stale once this changed. */\n activity: number\n}\ntype GhostMeta = { type: 'show', suggestion: Suggestion } | { type: 'clear' }\n\nexport const ghostAutocompleteKey = new PluginKey<GhostState>('ghostAutocomplete')\n\n/** Text of the caret's textblock, when the caret is an empty selection at the end of a non-empty, non-code textblock. */\nfunction blockTextAtEnd(state: EditorState): string | null {\n const { selection } = state\n if (!(selection instanceof TextSelection) || !selection.empty) return null\n const { $head } = selection\n const block = $head.parent\n if (!block.isTextblock || block.type.spec.code || $head.parentOffset !== block.content.size) return null\n const text = block.textContent\n return text.trim() ? text : null\n}\n\n/** Keeps a suggestion across a collaborator's change only if the caret block is untouched and the caret stayed with it. */\nfunction mapSuggestion(suggestion: Suggestion, tr: Transaction, oldState: EditorState, newState: EditorState): Suggestion | null {\n const mapped = tr.mapping.mapResult(suggestion.pos)\n if (mapped.deleted || newState.selection.head !== mapped.pos) return null\n const text = blockTextAtEnd(newState)\n if (text === null || text !== oldState.doc.resolve(suggestion.pos).parent.textContent) return null\n return { text: suggestion.text, pos: mapped.pos }\n}\n\n/** What the suggestion source needs of the app: its AI port and the model profile of a slot (`useModelProfiles`). */\nexport interface CompletionHost {\n /** `EditorContext.backend.ai`; without it there are no suggestions. */\n ai: EditorBackend['ai']\n modelRef: (slot: ModelSlot) => ModelRef\n}\n\n/** The suggestion source of the app behind `host`: `ai.complete` on the profile of the `complete` slot. */\nexport function completionFetcher(host: CompletionHost): GhostAutocompleteOptions['fetchSuggestion'] {\n return async (before, signal) => {\n if (!host.ai) return ''\n const { text } = await host.ai.complete({ before, model: host.modelRef('complete') }, signal)\n return text\n }\n}\n\n/**\n * Inline AI autocomplete: after a typing pause at the end of a non-empty textblock, a short continuation is shown as grey\n * ghost text after the caret. Tab accepts it as normal text, Esc dismisses it; typing, caret moves and blur clear it.\n * The suggestion is a local widget decoration — never part of the document, so collaborators never see it.\n * On while the editor setting `ai.autocomplete` is (see `useEditorSettings`); turning it off clears the ghost.\n * Client-only: not part of `schemaExtensions()`.\n */\nexport const ghostAutocomplete = (options: GhostAutocompleteOptions) => Extension.create({\n name: 'ghostAutocomplete',\n // Runs its Tab/Escape bindings before ListItem's Tab (sinkListItem); both return false without a suggestion.\n priority: 1000,\n\n addKeyboardShortcuts() {\n return {\n Tab: ({ editor }) => {\n const suggestion = ghostAutocompleteKey.getState(editor.state)?.suggestion\n const { selection } = editor.state\n if (!suggestion || !selection.empty || selection.head !== suggestion.pos) return false\n const end = suggestion.pos + suggestion.text.length\n const tr = editor.state.tr.insertText(suggestion.text, suggestion.pos)\n editor.view.dispatch(tr.setSelection(TextSelection.create(tr.doc, end)).scrollIntoView())\n return true\n },\n Escape: ({ editor }) => {\n if (!ghostAutocompleteKey.getState(editor.state)?.suggestion) return false\n editor.view.dispatch(editor.state.tr.setMeta(ghostAutocompleteKey, { type: 'clear' } satisfies GhostMeta))\n return true\n },\n }\n },\n\n addProseMirrorPlugins() {\n const { editor } = this\n const { settings } = options\n let timer: number | undefined\n let inFlight: AbortController | null = null\n\n function cancel() {\n clearTimeout(timer)\n timer = undefined\n inFlight?.abort()\n inFlight = null\n }\n\n function eligible(view: EditorView): boolean {\n const { state } = view\n if (!settings.get().ai.autocomplete || !editor.isEditable || !view.hasFocus() || view.composing || blockTextAtEnd(state) === null) return false\n if (!hasAbility(editor, ABILITY.aiAutocomplete)) return false\n // No ghost while an @tiptap/suggestion popup (e.g. `@` mentions) is open: its state is `{ active, query, … }`.\n return !state.plugins.some((plugin) => {\n const pluginState: unknown = plugin.getState(state)\n return typeof pluginState === 'object' && pluginState !== null && 'query' in pluginState && 'active' in pluginState && pluginState.active === true\n })\n }\n\n async function request(view: EditorView, activity: number) {\n timer = undefined\n const { state } = view\n if (ghostAutocompleteKey.getState(state)?.activity !== activity || !eligible(view)) return\n const pos = state.selection.head\n const blockText = blockTextAtEnd(state)\n // Positions count at least one per character, so twice the limit in positions covers the character limit.\n const before = state.doc.textBetween(Math.max(0, pos - AI_COMPLETE_LIMITS.before * 2), pos, '\\n\\n').slice(-AI_COMPLETE_LIMITS.before)\n const controller = new AbortController()\n inFlight = controller\n let text: string\n try {\n text = await options.fetchSuggestion(before, controller.signal)\n } catch {\n // Aborted, rate-limited (429) or failed: autocomplete is a silent nicety, so no ghost and no toast.\n return\n } finally {\n if (inFlight === controller) inFlight = null\n }\n if (controller.signal.aborted || !text || view.isDestroyed) return\n // Stale-response guard: no local edit or caret move since the request, and the caret block text is unchanged\n // (a collaborator's change elsewhere may have shifted the caret position).\n const now = view.state\n if (ghostAutocompleteKey.getState(now)?.activity !== activity || !eligible(view) || blockTextAtEnd(now) !== blockText) return\n view.dispatch(now.tr.setMeta(ghostAutocompleteKey, { type: 'show', suggestion: { text, pos: now.selection.head } } satisfies GhostMeta))\n }\n\n return [\n new Plugin<GhostState>({\n key: ghostAutocompleteKey,\n state: {\n init: () => ({ suggestion: null, edits: 0, activity: 0 }),\n apply(tr, prev, oldState, newState) {\n const meta = tr.getMeta(ghostAutocompleteKey) as GhostMeta | undefined\n // A collaborator's edit (the collaboration feature's resolver tells); the user's own undo/redo is not remote.\n const remote = anchorResolverOf(editor)?.isRemoteChange(tr) ?? false\n const edited = tr.docChanged && !remote\n const moved = tr.selectionSet && !remote\n let suggestion = prev.suggestion\n if (meta?.type === 'show') suggestion = meta.suggestion\n else if (meta?.type === 'clear' || edited || moved) suggestion = null\n else if (suggestion && tr.docChanged) suggestion = mapSuggestion(suggestion, tr, oldState, newState)\n if (suggestion === prev.suggestion && !edited && !moved) return prev\n return { suggestion, edits: prev.edits + (edited ? 1 : 0), activity: prev.activity + (edited || moved ? 1 : 0) }\n },\n },\n props: {\n decorations(state) {\n const suggestion = ghostAutocompleteKey.getState(state)?.suggestion\n if (!suggestion) return null\n const widget = Decoration.widget(suggestion.pos, () => {\n const span = document.createElement('span')\n span.className = 'ghost-autocomplete'\n span.contentEditable = 'false'\n span.setAttribute('aria-hidden', 'true')\n span.textContent = suggestion.text\n return span\n }, { side: 1, key: `ghost:${suggestion.text}`, ignoreSelection: true })\n return DecorationSet.create(state.doc, [widget])\n },\n handleDOMEvents: {\n blur(view) {\n cancel()\n if (ghostAutocompleteKey.getState(view.state)?.suggestion) {\n view.dispatch(view.state.tr.setMeta(ghostAutocompleteKey, { type: 'clear' } satisfies GhostMeta))\n }\n return false\n },\n },\n },\n view: (view) => {\n // Turned off (toolbar or settings): drop the pending request and the shown ghost right away.\n let last = settings.get().ai.autocomplete\n const stop = settings.subscribe((value) => {\n const enabled = value.ai.autocomplete\n if (enabled === last) return\n last = enabled\n if (enabled || view.isDestroyed) return\n cancel()\n if (ghostAutocompleteKey.getState(view.state)?.suggestion) {\n view.dispatch(view.state.tr.setMeta(ghostAutocompleteKey, { type: 'clear' } satisfies GhostMeta))\n }\n })\n return {\n update(view, prevState) {\n const next = ghostAutocompleteKey.getState(view.state)\n const prev = ghostAutocompleteKey.getState(prevState)\n if (!next || !prev) return\n if (!settings.get().ai.autocomplete) return cancel()\n if (next.activity === prev.activity) return\n // Typing or a caret move: drop the pending pause and abort the in-flight request.\n cancel()\n // Only a local edit starts a new pause; caret moves alone never request.\n if (next.edits === prev.edits || !eligible(view)) return\n const activity = next.activity\n timer = window.setTimeout(() => void request(view, activity), options.debounce)\n },\n destroy() {\n stop()\n cancel()\n },\n }\n },\n }),\n ]\n },\n})\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { ModelProfile, ModelRef } from "../../core/backend-types.js";
|
|
1
2
|
import { BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, TRUNCATED_LINE, blockLine, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, listLinePrefix, nestedLinePrefix, quoteLinePrefix, readBlockLines, singleBlockLine, stripBlockLineEcho, stripListMarkerEcho } from "./protocol/block-lines.js";
|
|
2
3
|
import { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, AgentOpType, AgentTextOptions, ModelAgentOp, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps } from "./protocol/agent-ops.js";
|
|
3
4
|
import { CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, DocumentChatMessage, citationBlockId, documentChatMessages } from "./protocol/chat.js";
|
|
4
5
|
import { AI_COMPLETE_LIMITS, AiCompleteMessage, cleanCompletion, completeMessages, completionReady } from "./protocol/complete.js";
|
|
5
|
-
import { AI_LANGUAGE_KEYS, MODEL_REF_LIMITS, MODEL_SLOT_KEYS,
|
|
6
|
+
import { AI_LANGUAGE_KEYS, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, UserModelProfile, modelRefSchema } from "./protocol/models.js";
|
|
6
7
|
import { AI_ID_PATTERN, aiBlockInputSchema, aiBlockSchema } from "./protocol/schemas.js";
|
|
7
8
|
import { AI_LIMITS, AI_TASKS, AI_TASK_IDS, AiApplyMode, AiBlockInput, AiInput, AiMessage, AiTask, AiTaskId, blockTasks, inBlockTasks, withHeading, withTaskHeading } from "./protocol/tasks.js";
|
|
8
9
|
import "./protocol/index.js";
|
|
@@ -14,7 +15,7 @@ import { AI_MENU_AT_CARET, AI_MENU_FOR_SELECTION, AiMenuAction, aiMenuActionsOf,
|
|
|
14
15
|
import { AiSelectionHighlight, aiSelectionHighlightKey, hideAiSelectionHighlight, showAiSelectionHighlight } from "./ai-selection-highlight.js";
|
|
15
16
|
import { AiSseOutcome, postAiSse } from "./ai-sse.js";
|
|
16
17
|
import { AiAssistant, AiAssistantOptions, AiAssistantState, AiRunBody, AiStatus, createAiAssistant } from "./assistant.js";
|
|
17
|
-
import { aiBlockAction,
|
|
18
|
+
import { aiBlockAction, aiCommand, blockTextRange } from "./block-actions.js";
|
|
18
19
|
import { documentChatText } from "./document-chat.js";
|
|
19
20
|
import { DocumentChat, DocumentChatGeneration, DocumentChatOptions, DocumentChatState, DocumentChatTurn, createDocumentChat } from "./document-chat-model.js";
|
|
20
21
|
import { AiUi, aiFeatureCore } from "./feature.js";
|
|
@@ -23,4 +24,4 @@ import { ModelProfilesModel, modelProfiles } from "./model-profiles.js";
|
|
|
23
24
|
import { isRateLimited, notifyAiRateLimited } from "./rate-limit.js";
|
|
24
25
|
import { AiSidebarFlow, AiSidebarFlowOptions, createAiSidebarFlow } from "./sidebar-flow.js";
|
|
25
26
|
import { AI_SELECTION_MENU_KEY, AiSelectionFlow, AiSelectionFlowOptions, AiSelectionMode, AiSelectionRequest, AiSelectionState, createAiSelectionFlow } from "./selection-flow.js";
|
|
26
|
-
export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_MENU_AT_CARET, AI_MENU_FOR_SELECTION, AI_SELECTION_MENU_KEY, AI_TASKS, AI_TASK_IDS, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, type AgentOpReview, type AgentOpReviewOptions, type AgentOpReviewState, type AgentOpState, AgentOpType, type AgentScope, AgentTextOptions, AiApplyMode, type AiApplyOutcome, type AiAssistant, type AiAssistantOptions, type AiAssistantState, AiBlockInput, AiCompleteMessage, AiInput, type AiMenuAction, AiMessage, type AiResultAction, AiReview, type AiReviewChange, type AiReviewChangeSummary, type AiReviewDecision, type AiReviewHandle, type AiReviewHunk, type AiReviewOptions, type AiReviewOutcome, type AiReviewSkipReason, type AiReviewState, type AiRunBody, type AiSelectionFlow, type AiSelectionFlowOptions, AiSelectionHighlight, type AiSelectionMode, type AiSelectionRequest, type AiSelectionState, type AiSidebarFlow, type AiSidebarFlowOptions, type AiSseOutcome, type AiStatus, type AiTarget, AiTask, AiTaskId, type AiUi, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, type DocumentChat, type DocumentChatGeneration, DocumentChatMessage, type DocumentChatOptions, type DocumentChatState, type DocumentChatTurn, type GhostAutocompleteOptions, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelAgentOp, ModelProfile, type ModelProfilesModel, ModelRef, TRUNCATED_LINE, UserModelProfile, acceptAiReview, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiApplyFailure, aiBlockAction, aiBlockInputSchema, aiBlockSchema, aiFeatureCore, aiMenuActionsOf, aiReviewKey, aiSelectionHighlightKey,
|
|
27
|
+
export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_MENU_AT_CARET, AI_MENU_FOR_SELECTION, AI_SELECTION_MENU_KEY, AI_TASKS, AI_TASK_IDS, AgentOp, AgentOpContext, AgentOpFeedback, AgentOpOutcome, AgentOpProblem, AgentOpProblemReason, type AgentOpReview, type AgentOpReviewOptions, type AgentOpReviewState, type AgentOpState, AgentOpType, type AgentScope, AgentTextOptions, AiApplyMode, type AiApplyOutcome, type AiAssistant, type AiAssistantOptions, type AiAssistantState, AiBlockInput, AiCompleteMessage, AiInput, type AiMenuAction, AiMessage, type AiResultAction, AiReview, type AiReviewChange, type AiReviewChangeSummary, type AiReviewDecision, type AiReviewHandle, type AiReviewHunk, type AiReviewOptions, type AiReviewOutcome, type AiReviewSkipReason, type AiReviewState, type AiRunBody, type AiSelectionFlow, type AiSelectionFlowOptions, AiSelectionHighlight, type AiSelectionMode, type AiSelectionRequest, type AiSelectionState, type AiSidebarFlow, type AiSidebarFlowOptions, type AiSseOutcome, type AiStatus, type AiTarget, AiTask, AiTaskId, type AiUi, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, BlockLines, CHAT_LIMITS, CITATION_HREF_PREFIX, ChatBlock, ChatBlockModel, ChatCompositeKind, type DocumentChat, type DocumentChatGeneration, DocumentChatMessage, type DocumentChatOptions, type DocumentChatState, type DocumentChatTurn, type GhostAutocompleteOptions, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, ModelAgentOp, type ModelProfile, type ModelProfilesModel, type ModelRef, TRUNCATED_LINE, UserModelProfile, acceptAiReview, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiApplyFailure, aiBlockAction, aiBlockInputSchema, aiBlockSchema, aiCommand, aiFeatureCore, aiMenuActionsOf, aiReviewKey, aiSelectionHighlightKey, applyAiResult, blockLine, blockTasks, blockTextRange, captureTarget, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionFetcher, completionReady, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, createAgentOpReview, createAiAssistant, createAiSelectionFlow, createAiSidebarFlow, createDocumentChat, documentChatMessages, documentChatText, documentMarkdown, formatTaskResult, ghostAutocomplete, ghostAutocompleteKey, hideAiSelectionHighlight, inBlockTasks, insertAtCaretLine, insertBlocksAtTarget, insertBlocksBelow, isRateLimited, listLinePrefix, modelProfiles, modelRefSchema, nestedLinePrefix, notifyAiRateLimited, openAiMenuAtCaret, openAiMenuForSelection, postAiSse, quoteLinePrefix, rangeMarkdown, readBlockLines, rejectAiReview, resultBlocks, selectionMarkdown, selectionScope, setActiveAiReviewHunk, showAiSelectionHighlight, singleBlockLine, startAiReview, stripBlockLineEcho, stripListMarkerEcho, withHeading, withTaskHeading };
|
|
@@ -5,21 +5,21 @@ import { AI_COMPLETE_LIMITS, cleanCompletion, completeMessages, completionReady
|
|
|
5
5
|
import { AI_LANGUAGE_KEYS, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, modelRefSchema } from "./protocol/models.js";
|
|
6
6
|
import { AI_LIMITS, AI_TASKS, AI_TASK_IDS, blockTasks, inBlockTasks, withHeading, withTaskHeading } from "./protocol/tasks.js";
|
|
7
7
|
import { AI_ID_PATTERN, aiBlockInputSchema, aiBlockSchema } from "./protocol/schemas.js";
|
|
8
|
-
import { selectionScope } from "./agent-ops.js";
|
|
9
8
|
import { aiApplyFailure, applyAiResult, captureTarget, documentMarkdown, formatTaskResult, insertAtCaretLine, insertBlocksAtTarget, insertBlocksBelow, rangeMarkdown, resultBlocks, selectionMarkdown } from "./ai-apply.js";
|
|
10
9
|
import { AiReview, acceptAiReview, aiReviewKey, rejectAiReview, setActiveAiReviewHunk, startAiReview } from "./ai-review.js";
|
|
11
|
-
import { createAgentOpReview } from "./agent-op-review.js";
|
|
12
|
-
import { AI_MENU_AT_CARET, AI_MENU_FOR_SELECTION, aiMenuActionsOf, openAiMenuAtCaret, openAiMenuForSelection } from "./ai-menu.js";
|
|
13
10
|
import { AiSelectionHighlight, aiSelectionHighlightKey, hideAiSelectionHighlight, showAiSelectionHighlight } from "./ai-selection-highlight.js";
|
|
11
|
+
import { AI_MENU_AT_CARET, AI_MENU_FOR_SELECTION, aiMenuActionsOf, openAiMenuAtCaret, openAiMenuForSelection } from "./ai-menu.js";
|
|
12
|
+
import { aiBlockAction, aiCommand, blockTextRange } from "./block-actions.js";
|
|
13
|
+
import { completionFetcher, ghostAutocomplete, ghostAutocompleteKey } from "./ghost-autocomplete.js";
|
|
14
|
+
import { modelProfiles } from "./model-profiles.js";
|
|
15
|
+
import { aiFeatureCore } from "./feature.js";
|
|
16
|
+
import { AI_SELECTION_MENU_KEY, createAiSelectionFlow } from "./selection-flow.js";
|
|
14
17
|
import { isRateLimited, notifyAiRateLimited } from "./rate-limit.js";
|
|
15
18
|
import { postAiSse } from "./ai-sse.js";
|
|
16
19
|
import { createAiAssistant } from "./assistant.js";
|
|
17
|
-
import {
|
|
20
|
+
import { selectionScope } from "./agent-ops.js";
|
|
21
|
+
import { createAiSidebarFlow } from "./sidebar-flow.js";
|
|
18
22
|
import { documentChatText } from "./document-chat.js";
|
|
23
|
+
import { createAgentOpReview } from "./agent-op-review.js";
|
|
19
24
|
import { createDocumentChat } from "./document-chat-model.js";
|
|
20
|
-
|
|
21
|
-
import { modelProfiles } from "./model-profiles.js";
|
|
22
|
-
import { aiFeatureCore } from "./feature.js";
|
|
23
|
-
import { createAiSidebarFlow } from "./sidebar-flow.js";
|
|
24
|
-
import { AI_SELECTION_MENU_KEY, createAiSelectionFlow } from "./selection-flow.js";
|
|
25
|
-
export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_MENU_AT_CARET, AI_MENU_FOR_SELECTION, AI_SELECTION_MENU_KEY, AI_TASKS, AI_TASK_IDS, AiReview, AiSelectionHighlight, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, CHAT_LIMITS, CITATION_HREF_PREFIX, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, TRUNCATED_LINE, acceptAiReview, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiApplyFailure, aiBlockAction, aiBlockInputSchema, aiBlockSchema, aiFeatureCore, aiMenuActionsOf, aiReviewKey, aiSelectionHighlightKey, aiSlashCommand, applyAiResult, blockLine, blockTasks, blockTextRange, captureTarget, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionFetcher, completionReady, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, createAgentOpReview, createAiAssistant, createAiSelectionFlow, createAiSidebarFlow, createDocumentChat, documentChatMessages, documentChatText, documentMarkdown, formatTaskResult, ghostAutocomplete, ghostAutocompleteKey, hideAiSelectionHighlight, inBlockTasks, insertAtCaretLine, insertBlocksAtTarget, insertBlocksBelow, isRateLimited, listLinePrefix, modelProfiles, modelRefSchema, nestedLinePrefix, notifyAiRateLimited, openAiMenuAtCaret, openAiMenuForSelection, postAiSse, quoteLinePrefix, rangeMarkdown, readBlockLines, rejectAiReview, resultBlocks, selectionMarkdown, selectionScope, setActiveAiReviewHunk, showAiSelectionHighlight, singleBlockLine, startAiReview, stripBlockLineEcho, stripListMarkerEcho, withHeading, withTaskHeading };
|
|
25
|
+
export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, AI_COMPLETE_LIMITS, AI_ID_PATTERN, AI_LANGUAGE_KEYS, AI_LIMITS, AI_MENU_AT_CARET, AI_MENU_FOR_SELECTION, AI_SELECTION_MENU_KEY, AI_TASKS, AI_TASK_IDS, AiReview, AiSelectionHighlight, BLOCK_LINES_LABEL, BLOCK_LINE_COMPOSITE_TEXT, BLOCK_LINE_ECHO_TEXT, BLOCK_LINE_EXAMPLE, BLOCK_LINE_EXAMPLE_ID, BLOCK_LINE_EXAMPLE_TEXT, BLOCK_LINE_HOST_TEXT, BLOCK_LINE_IDS_HINT, BLOCK_LINE_INTRO, BLOCK_LINE_LIST_TEXT, BLOCK_LINE_MARKERS, CHAT_LIMITS, CITATION_HREF_PREFIX, MODEL_REF_LIMITS, MODEL_SLOT_KEYS, TRUNCATED_LINE, acceptAiReview, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, aiApplyFailure, aiBlockAction, aiBlockInputSchema, aiBlockSchema, aiCommand, aiFeatureCore, aiMenuActionsOf, aiReviewKey, aiSelectionHighlightKey, applyAiResult, blockLine, blockTasks, blockTextRange, captureTarget, checkAgentOps, citationBlockId, cleanCompletion, completeMessages, completionFetcher, completionReady, compositeChromePart, compositeLineClose, compositeLineHeader, continuedLinePrefix, createAgentOpReview, createAiAssistant, createAiSelectionFlow, createAiSidebarFlow, createDocumentChat, documentChatMessages, documentChatText, documentMarkdown, formatTaskResult, ghostAutocomplete, ghostAutocompleteKey, hideAiSelectionHighlight, inBlockTasks, insertAtCaretLine, insertBlocksAtTarget, insertBlocksBelow, isRateLimited, listLinePrefix, modelProfiles, modelRefSchema, nestedLinePrefix, notifyAiRateLimited, openAiMenuAtCaret, openAiMenuForSelection, postAiSse, quoteLinePrefix, rangeMarkdown, readBlockLines, rejectAiReview, resultBlocks, selectionMarkdown, selectionScope, setActiveAiReviewHunk, showAiSelectionHighlight, singleBlockLine, startAiReview, stripBlockLineEcho, stripListMarkerEcho, withHeading, withTaskHeading };
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { ReadonlyStore, Store } from "../../core/store.js";
|
|
1
2
|
import { ModelSlot } from "../../settings/values.js";
|
|
2
3
|
import "../../settings/index.js";
|
|
3
|
-
import { ReadonlyStore, Store } from "../../core/store.js";
|
|
4
4
|
import { EditorContext } from "../../core/context.js";
|
|
5
|
-
import { ModelProfile, ModelRef
|
|
5
|
+
import { ModelProfile, ModelRef } from "../../core/backend-types.js";
|
|
6
|
+
import { UserModelProfile } from "./protocol/models.js";
|
|
6
7
|
import "./protocol/index.js";
|
|
7
8
|
//#region src/features/ai/model-profiles.d.ts
|
|
8
9
|
/** The model profiles of one app: see `modelProfiles`. */
|
|
9
10
|
export interface ModelProfilesModel {
|
|
10
|
-
/** Server presets (`
|
|
11
|
+
/** Server presets (`ai.models()`, without keys); empty until loaded. */
|
|
11
12
|
presets: ReadonlyStore<ModelProfile[]>;
|
|
12
13
|
/** This browser's own profiles (localStorage). Change them through `saveUserProfile`/`removeUserProfile`. */
|
|
13
14
|
userProfiles: Store<UserModelProfile[]>;
|
|
@@ -26,7 +27,7 @@ export interface ModelProfilesModel {
|
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* Model profiles the AI features run on, shared by all editors of the app (cached per `createEditorContext`, never
|
|
29
|
-
* process-global): server presets (`
|
|
30
|
+
* process-global): server presets (`backend.ai.models()`, requested once on first
|
|
30
31
|
* use, without keys) plus user profiles that live only in this browser's localStorage (their key goes with each
|
|
31
32
|
* request). Framework-free: `modelRef(slot)` is what AI requests send as `model`. Frameworks observe `presets`,
|
|
32
33
|
* `userProfiles` and `app.settings` (the slot assignment is `settings.ai.slots`).
|
|
@@ -35,7 +35,7 @@ function createModelProfiles(app) {
|
|
|
35
35
|
rewrite: "",
|
|
36
36
|
generate: ""
|
|
37
37
|
};
|
|
38
|
-
app.
|
|
38
|
+
app.backend.ai?.models().then((response) => {
|
|
39
39
|
serverSlots = response.slots;
|
|
40
40
|
presets.set(response.presets);
|
|
41
41
|
}).catch((error) => {
|
|
@@ -92,7 +92,7 @@ function createModelProfiles(app) {
|
|
|
92
92
|
const models = /* @__PURE__ */ new WeakMap();
|
|
93
93
|
/**
|
|
94
94
|
* Model profiles the AI features run on, shared by all editors of the app (cached per `createEditorContext`, never
|
|
95
|
-
* process-global): server presets (`
|
|
95
|
+
* process-global): server presets (`backend.ai.models()`, requested once on first
|
|
96
96
|
* use, without keys) plus user profiles that live only in this browser's localStorage (their key goes with each
|
|
97
97
|
* request). Framework-free: `modelRef(slot)` is what AI requests send as `model`. Frameworks observe `presets`,
|
|
98
98
|
* `userProfiles` and `app.settings` (the slot assignment is `settings.ai.slots`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-profiles.js","names":[],"sources":["../../../src/features/ai/model-profiles.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { EditorContext } from '../../core/context'\nimport { createStore, type ReadonlyStore, type Store } from '../../core/store'\nimport { MODEL_SLOTS, type ModelSlot } from '../../settings'\nimport type { ModelProfile, ModelRef, UserModelProfile } from './protocol'\n\nconst STORAGE_KEY = 'tiptap-editor:model-profiles'\n\n/** The model profiles of one app: see `modelProfiles`. */\nexport interface ModelProfilesModel {\n /** Server presets (`
|
|
1
|
+
{"version":3,"file":"model-profiles.js","names":[],"sources":["../../../src/features/ai/model-profiles.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { EditorContext } from '../../core/context'\nimport { createStore, type ReadonlyStore, type Store } from '../../core/store'\nimport { MODEL_SLOTS, type ModelSlot } from '../../settings'\nimport type { ModelProfile, ModelRef, UserModelProfile } from './protocol'\n\nconst STORAGE_KEY = 'tiptap-editor:model-profiles'\n\n/** The model profiles of one app: see `modelProfiles`. */\nexport interface ModelProfilesModel {\n /** Server presets (`ai.models()`, without keys); empty until loaded. */\n presets: ReadonlyStore<ModelProfile[]>\n /** This browser's own profiles (localStorage). Change them through `saveUserProfile`/`removeUserProfile`. */\n userProfiles: Store<UserModelProfile[]>\n /** Presets, then user profiles. Reads the stores: call it again after either changed. */\n profiles: () => ModelProfile[]\n /**\n * Profile a slot runs on: the one the settings assign to it, else the server's default preset for the slot, else the\n * first preset; none before the presets loaded (unless the settings point at a user profile).\n */\n slotProfile: (slot: ModelSlot) => ModelProfile | undefined\n /** What AI requests send as `model`; works before the presets loaded (the default slot preset of `createEditor({ ai })`). */\n modelRef: (slot: ModelSlot) => ModelRef\n saveUserProfile: (profile: UserModelProfile) => void\n /** Also resets every slot that ran on the profile to the server default. */\n removeUserProfile: (id: string) => void\n}\n\nconst storedProfileSchema = z.object({ id: z.string(), label: z.string(), endpoint: z.string(), model: z.string(), apiKey: z.string() })\n\nfunction storeUserProfiles(profiles: UserModelProfile[]) {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(profiles))\n }\n catch {\n // Storage unavailable (e.g. privacy mode): the profiles last for this session only.\n }\n}\n\nfunction loadUserProfiles(): UserModelProfile[] {\n try {\n const stored: unknown = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '[]')\n if (Array.isArray(stored)) {\n return stored.flatMap((item) => {\n const parsed = storedProfileSchema.safeParse(item)\n return parsed.success ? [parsed.data] : []\n })\n }\n }\n catch {\n // Unreadable or corrupt storage: no user profiles.\n }\n return []\n}\n\nfunction createModelProfiles(app: EditorContext): ModelProfilesModel {\n const presets = createStore<ModelProfile[]>([])\n const userProfiles = createStore<UserModelProfile[]>(loadUserProfiles())\n /** Default preset id per slot as the server reports it; until then the app's public AI configuration. */\n let serverSlots: Record<ModelSlot, string> | null = null\n const defaultSlots: Record<ModelSlot, string> = app.ai?.slots ?? { complete: '', rewrite: '', generate: '' }\n\n // A backend without `ai` has no server presets: requests keep using the default slot presets by id.\n app.backend.ai?.models()\n .then((response) => {\n serverSlots = response.slots\n presets.set(response.presets)\n })\n .catch((error) => {\n // Requests keep using the default slot presets by id.\n console.warn('tiptap-editor: Modell-Presets konnten nicht geladen werden', error)\n })\n\n const profiles = (): ModelProfile[] => [\n ...presets.get(),\n ...userProfiles.get().map(({ id, label, endpoint, model }) => ({ id, label, endpoint, model, source: 'user' as const })),\n ]\n\n const slotProfile = (slot: ModelSlot): ModelProfile | undefined => {\n const chosen = app.settings.get().ai.slots[slot]\n const defaultId = (serverSlots ?? defaultSlots)[slot]\n return profiles().find(p => p.id === chosen)\n ?? presets.get().find(p => p.id === defaultId)\n ?? presets.get()[0]\n }\n\n const modelRef = (slot: ModelSlot): ModelRef => {\n const profile = slotProfile(slot)\n if (!profile) return { preset: (serverSlots ?? defaultSlots)[slot] }\n const user = profile.source === 'user' ? userProfiles.get().find(p => p.id === profile.id) : undefined\n return user ? { endpoint: user.endpoint, model: user.model, apiKey: user.apiKey } : { preset: profile.id }\n }\n\n const saveUserProfile = (profile: UserModelProfile) => {\n const current = userProfiles.get()\n const index = current.findIndex(p => p.id === profile.id)\n const next = index < 0 ? [...current, profile] : current.map((p, i) => (i === index ? profile : p))\n userProfiles.set(next)\n storeUserProfiles(next)\n }\n\n const removeUserProfile = (id: string) => {\n const next = userProfiles.get().filter(p => p.id !== id)\n userProfiles.set(next)\n storeUserProfiles(next)\n const slots = app.settings.get().ai.slots\n if (MODEL_SLOTS.some(slot => slots[slot] === id)) {\n app.settings.update((s) => {\n for (const slot of MODEL_SLOTS) {\n if (s.ai.slots[slot] === id) delete s.ai.slots[slot]\n }\n })\n }\n }\n\n return { presets, userProfiles, profiles, slotProfile, modelRef, saveUserProfile, removeUserProfile }\n}\n\nconst models = new WeakMap<EditorContext, ModelProfilesModel>()\n\n/**\n * Model profiles the AI features run on, shared by all editors of the app (cached per `createEditorContext`, never\n * process-global): server presets (`backend.ai.models()`, requested once on first\n * use, without keys) plus user profiles that live only in this browser's localStorage (their key goes with each\n * request). Framework-free: `modelRef(slot)` is what AI requests send as `model`. Frameworks observe `presets`,\n * `userProfiles` and `app.settings` (the slot assignment is `settings.ai.slots`).\n */\nexport function modelProfiles(app: EditorContext): ModelProfilesModel {\n let model = models.get(app)\n if (!model) {\n model = createModelProfiles(app)\n models.set(app, model)\n }\n return model\n}\n"],"mappings":"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"}
|
|
@@ -166,7 +166,7 @@ const BLOCK_LINE_COMPOSITE_TEXT = `Im Dokument beginnt ein Baustein mit der Kopf
|
|
|
166
166
|
"Titel",
|
|
167
167
|
"Beschreibung",
|
|
168
168
|
"Tags: #a #b"
|
|
169
|
-
]))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose("")}“ ohne ID schließt ihn
|
|
169
|
+
]))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose("")}“ ohne ID schließt ihn. Das Feld „bib“ ist eine Literaturangabe als CSL-JSON, „derivation“ beschreibt, woraus der Baustein erzeugt wurde. Ein Verweis auf einen Baustein des Dokuments steht im Text als „[@<Block-ID>]“, mit Fundstelle als „[@<Block-ID>, S. 12]“.`;
|
|
170
170
|
/** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */
|
|
171
171
|
const BLOCK_LINE_ECHO_TEXT = "„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.";
|
|
172
172
|
/** What the retry message says about IDs. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-lines.js","names":[],"sources":["../../../../src/features/ai/protocol/block-lines.ts"],"sourcesContent":["// i18n-ignore-file: the document lines and the prompt texts describing them are model-facing (German; the model's language is not the editor's)\n/**\n * The line format of the document as the chat model sees it, in one place: how the lines are written (the primitives\n * `documentChatText` formats its lines with), how they are read back (the block IDs of a text and which blocks lie\n * inside which, ignoring fenced code), how an echoed line prefix is removed from the model's Markdown, and how the\n * prompt describes all of that. Framework-free and free of ProseMirror: the browser writes the text, the server reads it.\n *\n * A line is `[<block id>] <prefix><text>`. The prefix carries list items (`- `, `1. `), quotes (`> `) and the nesting\n * of a block inside a Baustein or host block (two spaces per level). A Baustein is a header line, its body lines and a\n * closing line `:::` without ID. Code blocks keep their own lines: the first one carries the ID, the others start at\n * the left edge, so their content can look like anything.\n */\n\n// ---------- Writing ----------------------------------------------------------\n\n/** One level of nesting inside a Baustein or host block. */\nconst INDENT = ' '\n\n/** The line shown instead of the rest of a document that did not fit. */\nexport const TRUNCATED_LINE = '[…] (Rest des Dokuments gekürzt)'\n\n/** A line of block `id` (none: an ID-less line), indented by `prefix`, holding `text`. */\nexport function blockLine(id: string | null, prefix: string, text: string): string {\n return `${id ? `[${id}] ` : ''}${prefix}${text}`\n}\n\n/** `markdown` on one line: hard breaks and wrapped lines would start new \"blocks\". */\nexport function singleBlockLine(markdown: string): string {\n return markdown.replace(/\\s*\\n\\s*/g, ' ')\n}\n\n/** The prefix of blocks inside the block whose lines carry `prefix` (the body of a Baustein or host block). */\nexport function nestedLinePrefix(prefix: string): string {\n return `${prefix}${INDENT}`\n}\n\n/** The prefix of the blocks of a quote. */\nexport function quoteLinePrefix(prefix: string): string {\n return `${prefix}> `\n}\n\n/** The prefix of the blocks of list item `number` (1-based; ignored in a bullet list). */\nexport function listLinePrefix(prefix: string, ordered: boolean, number: number): string {\n return `${prefix}${ordered ? `${number}. ` : '- '}`\n}\n\n/** The prefix of the blocks of a list item after its first: they continue under its marker instead of repeating it. */\nexport function continuedLinePrefix(prefix: string): string {\n return prefix.replace(/(?:- |\\d+\\. )$/, marker => ' '.repeat(marker.length))\n}\n\n/** One chrome row of a Baustein (`## title`, `> description`, `Tags: #a`) without its block marker, on one line. */\nexport function compositeChromePart(markdown: string): string {\n return singleBlockLine(markdown.replace(/^(?:#+|>) ?/, ''))\n}\n\n/**\n * The header text of a Baustein: `::: <kind> {meta}` (the meta as JSON only when it has entries), then every non-empty\n * chrome part (title, description, tags) separated by ` | `.\n */\nexport function compositeLineHeader(kind: string, meta: unknown, chrome: readonly string[]): string {\n const fields = meta && typeof meta === 'object' && Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : ''\n return [`::: ${kind}${fields}`, ...chrome.filter(Boolean)].join(' | ')\n}\n\n/** The closing line of a Baustein whose header line is indented by `prefix`: no ID. */\nexport function compositeLineClose(prefix: string): string {\n return `${nestedLinePrefix(prefix)}:::`\n}\n\n// ---------- Reading ----------------------------------------------------------\n\n/** The block IDs of a document text and which blocks lie inside which. */\nexport interface BlockLines {\n /** Every block ID that starts a line (lines inside fenced code do not count). */\n ids: ReadonlySet<string>\n /** The IDs of all blocks nested below block `id`: the lines indented under its header line (empty for a block without nested blocks). */\n below: (id: string) => ReadonlySet<string>\n}\n\nconst ID_PREFIX = /^\\[([^\\]\\s]+)\\] /\n/** An opening code fence after optional list/quote markers and indentation: three or more backticks, no backtick in the info string. */\nconst FENCE_OPEN = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*(`{3,})[^`]*$/\nconst FENCE_CLOSE = /^[ \\t]*(`{3,})[ \\t]*$/\n/** Indentation and list/quote markers at the start of a line's content. */\nconst LINE_PREFIX = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*/\n\n/**\n * The fence length after `line` when a fence of `fence` backticks was open before it (0: none). A line of at least as\n * many backticks closes the fence; `opens` says whether a line outside a fence may open one.\n */\nfunction fenceAfter(fence: number, line: string, opens: boolean): number {\n if (fence) {\n const closing = FENCE_CLOSE.exec(line)\n return closing && closing[1]!.length >= fence ? 0 : fence\n }\n const opening = opens ? FENCE_OPEN.exec(line) : null\n return opening ? opening[1]!.length : 0\n}\n\nconst NOTHING: ReadonlySet<string> = new Set()\n\n/**\n * Reads the lines of `text` (as `documentChatText` writes them). Lines inside a fenced code block are code, never\n * block lines, so a code line like `[ghost] text` does not become an ID; only a line that carries an ID opens a fence\n * (a stray line of backticks inside the code cannot swallow what follows).\n *\n * Nesting follows the indentation: the blocks on the lines under a header line that are indented further belong to its\n * block. Lines of list items (their marker, and what continues under it) never head a block of their own: a paragraph\n * and its nested list are siblings, nothing lies \"inside\" the paragraph. A block without lines of its own (a list, a\n * quote) has no nested blocks; a block's own ID on a nested line (a child without an ID of its own) is not \"inside\".\n */\nexport function readBlockLines(text: string): BlockLines {\n const ids = new Set<string>()\n const nested = new Map<string, Set<string>>()\n // The blocks whose lines enclose the current one, outermost first; `head`: the block can have nested blocks.\n const open: { id: string, indent: number, head: boolean }[] = []\n let fence = 0\n\n for (const line of text.split('\\n')) {\n const id = ID_PREFIX.exec(line)?.[1]\n if (fence) {\n fence = fenceAfter(fence, line, false)\n continue\n }\n // The truncation notice reads like a line with the ID `…`.\n if (!id || line === TRUNCATED_LINE) continue\n const content = line.slice(id.length + 3)\n fence = fenceAfter(0, content, true)\n ids.add(id)\n\n const indent = /^[ \\t]*/.exec(content)![0].length\n const listItem = /[-*+\\d]/.test(LINE_PREFIX.exec(content)![0])\n while (open.length && open.at(-1)!.indent >= indent) open.pop()\n for (const outer of open) {\n if (!outer.head || outer.id === id) continue\n const inside = nested.get(outer.id)\n if (inside) inside.add(id)\n else nested.set(outer.id, new Set([id]))\n }\n open.push({ id, indent, head: !listItem && (open.at(-1)?.head ?? true) })\n }\n return { ids, below: id => nested.get(id) ?? NOTHING }\n}\n\n/**\n * Removes what a model echoes from the document lines into its Markdown: a `[<id>] ` prefix of a known block at the\n * start of a line (not inside a fenced code block of its own Markdown, which keeps lines that look like one), and the\n * indentation all non-blank lines share (lines echoed from a Baustein's body carry its indent, which would turn deeper\n * nested lines into indented code).\n */\nexport function stripBlockLineEcho(markdown: string, known: (id: string) => boolean): string {\n let fence = 0\n const stripped = markdown.split('\\n').map((line) => {\n if (fence) {\n fence = fenceAfter(fence, line, false)\n return line\n }\n const bare = line.replace(/^\\[([^\\]\\s]+)\\] ?/, (prefix, id: string) => known(id) ? '' : prefix)\n fence = fenceAfter(0, bare, true)\n return bare\n })\n const indents = stripped.filter(line => line.trim()).map(line => /^[ \\t]*/.exec(line)![0].length)\n const common = indents.length ? Math.min(...indents) : 0\n return (common ? stripped.map(line => line.replace(new RegExp(`^[ \\\\t]{0,${common}}`), '')) : stripped).join('\\n')\n}\n\n/**\n * Removes the list and quote markers (`- `, `1. `, `> `) a model echoes from the lines of list items and quotes at the\n * start of each line of its Markdown.\n */\nexport function stripListMarkerEcho(markdown: string): string {\n return markdown.replace(/^(?:[ \\t]*(?:[-*+]|\\d+[.)])[ \\t]+|[ \\t]*>[ \\t]?)+/gm, '')\n}\n\n// ---------- Prompt -----------------------------------------------------------\n\n/** The block ID of the prompt's example line (also the target of its citation example). */\nexport const BLOCK_LINE_EXAMPLE_ID = '0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d'\n\n/** The text of the prompt's example line: a speaker line. */\nexport const BLOCK_LINE_EXAMPLE_TEXT = '**Anna:** Wir starten im Mai.'\n\n/** The example line of the prompt, written by `blockLine` like every line of the document. */\nexport const BLOCK_LINE_EXAMPLE = blockLine(BLOCK_LINE_EXAMPLE_ID, '', BLOCK_LINE_EXAMPLE_TEXT)\n\n/** The label of the document in the user message. */\nexport const BLOCK_LINES_LABEL = 'Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID)'\n\n/** What the first lines of the system prompt say about the document's lines. */\nexport const BLOCK_LINE_INTRO = `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \\`${BLOCK_LINE_EXAMPLE}\\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`\n\n/** The list and quote blocks with the characters their lines carry. */\nexport const BLOCK_LINE_MARKERS = `Aufzählung („${listLinePrefix('', false, 1)}“), nummerierte Liste („${listLinePrefix('', true, 1)}“), Zitat („${quoteLinePrefix('')}“)`\n\n/** What the prompt says about lists and quotes in the document. */\nexport const BLOCK_LINE_LIST_TEXT = 'Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.'\n\n/** What the prompt says about the lines of a host block with block content (a callout). */\nexport const BLOCK_LINE_HOST_TEXT = `Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „${blockLine('ID', '', '<erste Zeile seiner Markdown-Form>')}“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt.`\n\n/** What the prompt says about the lines of a Baustein. */\nexport const BLOCK_LINE_COMPOSITE_TEXT = `Im Dokument beginnt ein Baustein mit der Kopfzeile „${blockLine('ID', '', compositeLineHeader('Art', { feld: 'wert' }, ['Titel', 'Beschreibung', 'Tags: #a #b']))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose('')}“ ohne ID schließt ihn.`\n\n/** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */\nexport const BLOCK_LINE_ECHO_TEXT = '„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.'\n\n/** What the retry message says about IDs. */\nexport const BLOCK_LINE_IDS_HINT = 'Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"block-lines.js","names":[],"sources":["../../../../src/features/ai/protocol/block-lines.ts"],"sourcesContent":["// i18n-ignore-file: the document lines and the prompt texts describing them are model-facing (German; the model's language is not the editor's)\n/**\n * The line format of the document as the chat model sees it, in one place: how the lines are written (the primitives\n * `documentChatText` formats its lines with), how they are read back (the block IDs of a text and which blocks lie\n * inside which, ignoring fenced code), how an echoed line prefix is removed from the model's Markdown, and how the\n * prompt describes all of that. Framework-free and free of ProseMirror: the browser writes the text, the server reads it.\n *\n * A line is `[<block id>] <prefix><text>`. The prefix carries list items (`- `, `1. `), quotes (`> `) and the nesting\n * of a block inside a Baustein or host block (two spaces per level). A Baustein is a header line, its body lines and a\n * closing line `:::` without ID. Code blocks keep their own lines: the first one carries the ID, the others start at\n * the left edge, so their content can look like anything.\n */\n\n// ---------- Writing ----------------------------------------------------------\n\n/** One level of nesting inside a Baustein or host block. */\nconst INDENT = ' '\n\n/** The line shown instead of the rest of a document that did not fit. */\nexport const TRUNCATED_LINE = '[…] (Rest des Dokuments gekürzt)'\n\n/** A line of block `id` (none: an ID-less line), indented by `prefix`, holding `text`. */\nexport function blockLine(id: string | null, prefix: string, text: string): string {\n return `${id ? `[${id}] ` : ''}${prefix}${text}`\n}\n\n/** `markdown` on one line: hard breaks and wrapped lines would start new \"blocks\". */\nexport function singleBlockLine(markdown: string): string {\n return markdown.replace(/\\s*\\n\\s*/g, ' ')\n}\n\n/** The prefix of blocks inside the block whose lines carry `prefix` (the body of a Baustein or host block). */\nexport function nestedLinePrefix(prefix: string): string {\n return `${prefix}${INDENT}`\n}\n\n/** The prefix of the blocks of a quote. */\nexport function quoteLinePrefix(prefix: string): string {\n return `${prefix}> `\n}\n\n/** The prefix of the blocks of list item `number` (1-based; ignored in a bullet list). */\nexport function listLinePrefix(prefix: string, ordered: boolean, number: number): string {\n return `${prefix}${ordered ? `${number}. ` : '- '}`\n}\n\n/** The prefix of the blocks of a list item after its first: they continue under its marker instead of repeating it. */\nexport function continuedLinePrefix(prefix: string): string {\n return prefix.replace(/(?:- |\\d+\\. )$/, marker => ' '.repeat(marker.length))\n}\n\n/** One chrome row of a Baustein (`## title`, `> description`, `Tags: #a`) without its block marker, on one line. */\nexport function compositeChromePart(markdown: string): string {\n return singleBlockLine(markdown.replace(/^(?:#+|>) ?/, ''))\n}\n\n/**\n * The header text of a Baustein: `::: <kind> {meta}` (the meta as JSON only when it has entries), then every non-empty\n * chrome part (title, description, tags) separated by ` | `.\n */\nexport function compositeLineHeader(kind: string, meta: unknown, chrome: readonly string[]): string {\n const fields = meta && typeof meta === 'object' && Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : ''\n return [`::: ${kind}${fields}`, ...chrome.filter(Boolean)].join(' | ')\n}\n\n/** The closing line of a Baustein whose header line is indented by `prefix`: no ID. */\nexport function compositeLineClose(prefix: string): string {\n return `${nestedLinePrefix(prefix)}:::`\n}\n\n// ---------- Reading ----------------------------------------------------------\n\n/** The block IDs of a document text and which blocks lie inside which. */\nexport interface BlockLines {\n /** Every block ID that starts a line (lines inside fenced code do not count). */\n ids: ReadonlySet<string>\n /** The IDs of all blocks nested below block `id`: the lines indented under its header line (empty for a block without nested blocks). */\n below: (id: string) => ReadonlySet<string>\n}\n\nconst ID_PREFIX = /^\\[([^\\]\\s]+)\\] /\n/** An opening code fence after optional list/quote markers and indentation: three or more backticks, no backtick in the info string. */\nconst FENCE_OPEN = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*(`{3,})[^`]*$/\nconst FENCE_CLOSE = /^[ \\t]*(`{3,})[ \\t]*$/\n/** Indentation and list/quote markers at the start of a line's content. */\nconst LINE_PREFIX = /^(?:[ \\t]|>|[-*+][ \\t]|\\d+[.)][ \\t])*/\n\n/**\n * The fence length after `line` when a fence of `fence` backticks was open before it (0: none). A line of at least as\n * many backticks closes the fence; `opens` says whether a line outside a fence may open one.\n */\nfunction fenceAfter(fence: number, line: string, opens: boolean): number {\n if (fence) {\n const closing = FENCE_CLOSE.exec(line)\n return closing && closing[1]!.length >= fence ? 0 : fence\n }\n const opening = opens ? FENCE_OPEN.exec(line) : null\n return opening ? opening[1]!.length : 0\n}\n\nconst NOTHING: ReadonlySet<string> = new Set()\n\n/**\n * Reads the lines of `text` (as `documentChatText` writes them). Lines inside a fenced code block are code, never\n * block lines, so a code line like `[ghost] text` does not become an ID; only a line that carries an ID opens a fence\n * (a stray line of backticks inside the code cannot swallow what follows).\n *\n * Nesting follows the indentation: the blocks on the lines under a header line that are indented further belong to its\n * block. Lines of list items (their marker, and what continues under it) never head a block of their own: a paragraph\n * and its nested list are siblings, nothing lies \"inside\" the paragraph. A block without lines of its own (a list, a\n * quote) has no nested blocks; a block's own ID on a nested line (a child without an ID of its own) is not \"inside\".\n */\nexport function readBlockLines(text: string): BlockLines {\n const ids = new Set<string>()\n const nested = new Map<string, Set<string>>()\n // The blocks whose lines enclose the current one, outermost first; `head`: the block can have nested blocks.\n const open: { id: string, indent: number, head: boolean }[] = []\n let fence = 0\n\n for (const line of text.split('\\n')) {\n const id = ID_PREFIX.exec(line)?.[1]\n if (fence) {\n fence = fenceAfter(fence, line, false)\n continue\n }\n // The truncation notice reads like a line with the ID `…`.\n if (!id || line === TRUNCATED_LINE) continue\n const content = line.slice(id.length + 3)\n fence = fenceAfter(0, content, true)\n ids.add(id)\n\n const indent = /^[ \\t]*/.exec(content)![0].length\n const listItem = /[-*+\\d]/.test(LINE_PREFIX.exec(content)![0])\n while (open.length && open.at(-1)!.indent >= indent) open.pop()\n for (const outer of open) {\n if (!outer.head || outer.id === id) continue\n const inside = nested.get(outer.id)\n if (inside) inside.add(id)\n else nested.set(outer.id, new Set([id]))\n }\n open.push({ id, indent, head: !listItem && (open.at(-1)?.head ?? true) })\n }\n return { ids, below: id => nested.get(id) ?? NOTHING }\n}\n\n/**\n * Removes what a model echoes from the document lines into its Markdown: a `[<id>] ` prefix of a known block at the\n * start of a line (not inside a fenced code block of its own Markdown, which keeps lines that look like one), and the\n * indentation all non-blank lines share (lines echoed from a Baustein's body carry its indent, which would turn deeper\n * nested lines into indented code).\n */\nexport function stripBlockLineEcho(markdown: string, known: (id: string) => boolean): string {\n let fence = 0\n const stripped = markdown.split('\\n').map((line) => {\n if (fence) {\n fence = fenceAfter(fence, line, false)\n return line\n }\n const bare = line.replace(/^\\[([^\\]\\s]+)\\] ?/, (prefix, id: string) => known(id) ? '' : prefix)\n fence = fenceAfter(0, bare, true)\n return bare\n })\n const indents = stripped.filter(line => line.trim()).map(line => /^[ \\t]*/.exec(line)![0].length)\n const common = indents.length ? Math.min(...indents) : 0\n return (common ? stripped.map(line => line.replace(new RegExp(`^[ \\\\t]{0,${common}}`), '')) : stripped).join('\\n')\n}\n\n/**\n * Removes the list and quote markers (`- `, `1. `, `> `) a model echoes from the lines of list items and quotes at the\n * start of each line of its Markdown.\n */\nexport function stripListMarkerEcho(markdown: string): string {\n return markdown.replace(/^(?:[ \\t]*(?:[-*+]|\\d+[.)])[ \\t]+|[ \\t]*>[ \\t]?)+/gm, '')\n}\n\n// ---------- Prompt -----------------------------------------------------------\n\n/** The block ID of the prompt's example line (also the target of its citation example). */\nexport const BLOCK_LINE_EXAMPLE_ID = '0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d'\n\n/** The text of the prompt's example line: a speaker line. */\nexport const BLOCK_LINE_EXAMPLE_TEXT = '**Anna:** Wir starten im Mai.'\n\n/** The example line of the prompt, written by `blockLine` like every line of the document. */\nexport const BLOCK_LINE_EXAMPLE = blockLine(BLOCK_LINE_EXAMPLE_ID, '', BLOCK_LINE_EXAMPLE_TEXT)\n\n/** The label of the document in the user message. */\nexport const BLOCK_LINES_LABEL = 'Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID)'\n\n/** What the first lines of the system prompt say about the document's lines. */\nexport const BLOCK_LINE_INTRO = `Jede Zeile des Dokuments beginnt mit der ID ihres Blocks in eckigen Klammern, z. B. \\`${BLOCK_LINE_EXAMPLE}\\`. Redebeiträge beginnen nach der ID mit dem fett gesetzten Namen der Sprecherin bzw. des Sprechers und einem Doppelpunkt.`\n\n/** The list and quote blocks with the characters their lines carry. */\nexport const BLOCK_LINE_MARKERS = `Aufzählung („${listLinePrefix('', false, 1)}“), nummerierte Liste („${listLinePrefix('', true, 1)}“), Zitat („${quoteLinePrefix('')}“)`\n\n/** What the prompt says about lists and quotes in the document. */\nexport const BLOCK_LINE_LIST_TEXT = 'Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.'\n\n/** What the prompt says about the lines of a host block with block content (a callout). */\nexport const BLOCK_LINE_HOST_TEXT = `Im Dokument beginnt ein eigener Block mit Inhaltsblöcken mit der Zeile „${blockLine('ID', '', '<erste Zeile seiner Markdown-Form>')}“; seine Inhaltsblöcke folgen als eigene Zeilen mit IDs, um zwei Leerzeichen eingerückt.`\n\n/** What the prompt says about the lines of a Baustein. */\nexport const BLOCK_LINE_COMPOSITE_TEXT = `Im Dokument beginnt ein Baustein mit der Kopfzeile „${blockLine('ID', '', compositeLineHeader('Art', { feld: 'wert' }, ['Titel', 'Beschreibung', 'Tags: #a #b']))}“ (die Felder als JSON nur, wenn welche gesetzt sind; leere Teile fehlen samt ihrem „|“). Seine Inhaltsblöcke folgen als eigene Zeilen mit ihren IDs, um zwei Leerzeichen eingerückt; eine Zeile „${compositeLineClose('')}“ ohne ID schließt ihn. Das Feld „bib“ ist eine Literaturangabe als CSL-JSON, „derivation“ beschreibt, woraus der Baustein erzeugt wurde. Ein Verweis auf einen Baustein des Dokuments steht im Text als „[@<Block-ID>]“, mit Fundstelle als „[@<Block-ID>, S. 12]“.`\n\n/** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */\nexport const BLOCK_LINE_ECHO_TEXT = '„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.'\n\n/** What the retry message says about IDs. */\nexport const BLOCK_LINE_IDS_HINT = 'Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.'\n"],"mappings":"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"}
|