@ecsplico/editor 3.6.0 → 4.1.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 +73 -0
- package/dist/adapters/direct-ai.d.ts +4 -4
- package/dist/adapters/direct-ai.js +2 -2
- package/dist/adapters/direct-ai.js.map +1 -1
- package/dist/adapters/document-state.d.ts +1 -1
- package/dist/adapters/document-state.js.map +1 -1
- package/dist/adapters/forge.d.ts +2 -2
- package/dist/adapters/forge.js.map +1 -1
- package/dist/adapters/nitro.d.ts +11 -3
- package/dist/adapters/nitro.js +3 -1
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +6 -6
- package/dist/adapters/shared.js.map +1 -1
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/config/index.d.ts +14 -14
- package/dist/config/index.js +8 -8
- package/dist/config/index.js.map +1 -1
- package/dist/core/backend-types.d.ts +3 -3
- package/dist/core/backend-types.js.map +1 -1
- package/dist/core/backend.d.ts +4 -4
- package/dist/core/backend.js.map +1 -1
- package/dist/core/block-convert.d.ts +1 -1
- package/dist/core/block-convert.js +5 -5
- package/dist/core/block-convert.js.map +1 -1
- package/dist/core/block-transfer.d.ts +8 -4
- package/dist/core/block-transfer.js +23 -14
- package/dist/core/block-transfer.js.map +1 -1
- package/dist/core/capabilities.d.ts +113 -0
- package/dist/core/capabilities.js +155 -0
- package/dist/core/capabilities.js.map +1 -0
- package/dist/core/command-types.d.ts +1 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/convert.d.ts +5 -5
- package/dist/core/convert.js +5 -5
- package/dist/core/convert.js.map +1 -1
- package/dist/core/core-feature.js +1 -1
- package/dist/core/define-editor.d.ts +2 -2
- package/dist/core/define-editor.js +34 -6
- package/dist/core/define-editor.js.map +1 -1
- package/dist/core/derivation.d.ts +119 -0
- package/dist/core/derivation.js +187 -0
- package/dist/core/derivation.js.map +1 -0
- package/dist/core/derive.d.ts +61 -0
- package/dist/core/derive.js +69 -0
- package/dist/core/derive.js.map +1 -0
- package/dist/core/extension-points.d.ts +16 -0
- package/dist/core/feature-context.d.ts +5 -1
- package/dist/core/feature-context.js +10 -3
- package/dist/core/feature-context.js.map +1 -1
- package/dist/core/feature.d.ts +41 -19
- package/dist/core/feature.js.map +1 -1
- package/dist/core/fences.js +1 -1
- package/dist/core/fences.js.map +1 -1
- package/dist/core/index.d.ts +6 -2
- package/dist/core/index.js +5 -1
- package/dist/core/pipeline.d.ts +14 -5
- package/dist/core/pipeline.js +19 -7
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/properties.d.ts +22 -0
- package/dist/core/properties.js +17 -0
- package/dist/core/properties.js.map +1 -0
- package/dist/core/schema/block-ids.js +5 -5
- package/dist/core/schema/block-ids.js.map +1 -1
- package/dist/core/schema/index.d.ts +2 -1
- package/dist/core/schema/index.js +2 -1
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/schema/opaque-fence.d.ts +4 -4
- package/dist/core/schema/opaque-fence.js +7 -7
- package/dist/core/schema/opaque-fence.js.map +1 -1
- package/dist/core/schema/persisted-names.d.ts +58 -0
- package/dist/core/schema/persisted-names.js +96 -0
- package/dist/core/schema/persisted-names.js.map +1 -0
- package/dist/core/toolbar-items.js +1 -1
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/core/util.js +5 -1
- package/dist/core/util.js.map +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/agent-ops.js +1 -1
- package/dist/features/ai/ai-review.js +1 -1
- package/dist/features/ai/assistant.d.ts +4 -4
- package/dist/features/ai/assistant.js +1 -1
- package/dist/features/ai/assistant.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.js +7 -7
- package/dist/features/ai/components/ModelQuickSwitch.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.vue.d.ts +4 -4
- package/dist/features/ai/components/SettingsAiTab.js +8 -8
- package/dist/features/ai/components/SettingsAiTab.js.map +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/document-chat-model.d.ts +2 -2
- package/dist/features/ai/document-chat-model.js +5 -5
- package/dist/features/ai/document-chat-model.js.map +1 -1
- package/dist/features/ai/document-chat.js +2 -2
- package/dist/features/ai/document-chat.js.map +1 -1
- package/dist/features/ai/feature.d.ts +2 -2
- package/dist/features/ai/feature.js +2 -2
- package/dist/features/ai/feature.js.map +1 -1
- package/dist/features/ai/ghost-autocomplete.d.ts +4 -4
- package/dist/features/ai/ghost-autocomplete.js +1 -1
- package/dist/features/ai/ghost-autocomplete.js.map +1 -1
- package/dist/features/ai/index.d.ts +3 -3
- package/dist/features/ai/index.js +2 -2
- package/dist/features/ai/model-profiles.d.ts +9 -9
- package/dist/features/ai/model-profiles.js +13 -13
- package/dist/features/ai/model-profiles.js.map +1 -1
- package/dist/features/ai/protocol/agent-ops.d.ts +2 -2
- package/dist/features/ai/protocol/agent-ops.js +1 -1
- package/dist/features/ai/protocol/agent-ops.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.d.ts +7 -7
- package/dist/features/ai/protocol/block-lines.js +10 -10
- package/dist/features/ai/protocol/block-lines.js.map +1 -1
- package/dist/features/ai/protocol/chat.d.ts +5 -5
- package/dist/features/ai/protocol/chat.js +12 -12
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/generate.d.ts +1 -1
- package/dist/features/ai/protocol/generate.js +1 -1
- package/dist/features/ai/protocol/generate.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -3
- package/dist/features/ai/protocol/index.js +2 -2
- package/dist/features/ai/protocol/models.d.ts +3 -3
- package/dist/features/ai/protocol/models.js +3 -3
- package/dist/features/ai/protocol/models.js.map +1 -1
- package/dist/features/ai/protocol/schemas.d.ts +1 -1
- package/dist/features/ai/protocol/schemas.js +1 -1
- package/dist/features/ai/protocol/schemas.js.map +1 -1
- package/dist/features/ai/protocol/tasks.d.ts +5 -5
- package/dist/features/ai/protocol/tasks.js +12 -12
- package/dist/features/ai/protocol/tasks.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/react/ModelQuickSwitch.d.ts +4 -4
- package/dist/features/ai/react/ModelQuickSwitch.js +6 -6
- package/dist/features/ai/react/ModelQuickSwitch.js.map +1 -1
- package/dist/features/ai/react/SettingsAiTab.d.ts +1 -1
- package/dist/features/ai/react/SettingsAiTab.js +8 -8
- package/dist/features/ai/react/SettingsAiTab.js.map +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.d.ts +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/react/use-ai-assistant.js +1 -1
- package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/react/use-model-profiles.d.ts +5 -5
- package/dist/features/ai/react/use-model-profiles.js +2 -2
- package/dist/features/ai/react/use-model-profiles.js.map +1 -1
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-ai-assistant.js +1 -1
- package/dist/features/ai/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/use-model-profiles.d.ts +4 -4
- package/dist/features/ai/use-model-profiles.js +2 -2
- package/dist/features/ai/use-model-profiles.js.map +1 -1
- package/dist/features/block-ui/block-actions.d.ts +8 -4
- package/dist/features/block-ui/block-actions.js +38 -22
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/block-target.d.ts +29 -0
- package/dist/features/block-ui/block-target.js +63 -0
- package/dist/features/block-ui/block-target.js.map +1 -0
- package/dist/features/block-ui/components/BlockHandles.js +3 -3
- package/dist/features/block-ui/components/BlockHandles.js.map +1 -1
- package/dist/features/block-ui/components/BlockUi.js +11 -23
- package/dist/features/block-ui/components/BlockUi.js.map +1 -1
- package/dist/features/block-ui/feature.d.ts +3 -2
- package/dist/features/block-ui/feature.js +3 -2
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/handle-geometry.js +12 -23
- package/dist/features/block-ui/handle-geometry.js.map +1 -1
- package/dist/features/block-ui/index.d.ts +2 -1
- package/dist/features/block-ui/index.js +2 -1
- package/dist/features/block-ui/react/BlockHandles.d.ts +1 -1
- package/dist/features/block-ui/react/BlockHandles.js +4 -4
- package/dist/features/block-ui/react/BlockHandles.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.js +13 -25
- package/dist/features/block-ui/react/BlockUi.js.map +1 -1
- package/dist/features/comments/model.js +1 -1
- package/dist/features/composites/accepted-children.d.ts +5 -4
- package/dist/features/composites/accepted-children.js +26 -26
- package/dist/features/composites/accepted-children.js.map +1 -1
- package/dist/features/composites/ai-brief.d.ts +4 -4
- package/dist/features/composites/ai-brief.js +14 -12
- package/dist/features/composites/ai-brief.js.map +1 -1
- package/dist/features/composites/anchors.d.ts +1 -1
- package/dist/features/composites/anchors.js.map +1 -1
- package/dist/features/composites/baskets.d.ts +1 -1
- package/dist/features/composites/baskets.js.map +1 -1
- package/dist/features/composites/bibliography-command.d.ts +1 -1
- package/dist/features/composites/bibliography-form.js +1 -1
- package/dist/features/composites/bibliography-form.js.map +1 -1
- package/dist/features/composites/bibliography.js.map +1 -1
- package/dist/features/composites/block-types-json.d.ts +38 -0
- package/dist/features/composites/block-types-json.js +122 -0
- package/dist/features/composites/block-types-json.js.map +1 -0
- package/dist/features/composites/block-types.d.ts +119 -0
- package/dist/features/composites/block-types.js +343 -0
- package/dist/features/composites/block-types.js.map +1 -0
- package/dist/features/composites/capabilities.js +109 -0
- package/dist/features/composites/capabilities.js.map +1 -0
- package/dist/features/composites/components/BibliographyEditor.js +5 -5
- package/dist/features/composites/components/BibliographyEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeBasketMenu.js +2 -2
- package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -1
- package/dist/features/composites/components/CompositeChrome.js +18 -8
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeDetails.js +11 -10
- package/dist/features/composites/components/CompositeDetails.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeFieldEditor.js +7 -7
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeFieldEditor.vue.d.ts +5 -5
- package/dist/features/composites/components/CompositeInspector.js +136 -113
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/components/CompositeInspector.vue.d.ts +3 -3
- package/dist/features/composites/components/CompositeStatusMenu.js +2 -2
- package/dist/features/composites/components/CompositeStatusMenu.js.map +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeTable.js +2 -2
- package/dist/features/composites/components/CompositeTable.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeView.js +19 -17
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +15 -6
- package/dist/features/composites/components/DerivationBadge.js.map +1 -1
- package/dist/features/composites/components/FocusChip.js +1 -1
- package/dist/features/composites/components/FocusChip.js.map +1 -1
- package/dist/features/composites/components/FocusChip.vue.d.ts +1 -1
- package/dist/features/composites/components/ReferenceFieldSelect.js +3 -3
- package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -1
- package/dist/features/composites/components/ReferenceView.js +1 -1
- package/dist/features/composites/components/ReferenceView.js.map +1 -1
- package/dist/features/composites/components/SettingsEditingTab.js +18 -18
- package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/components/SettingsEditingTab.vue.d.ts +3 -3
- package/dist/features/composites/convert.d.ts +4 -4
- package/dist/features/composites/convert.js +27 -28
- package/dist/features/composites/convert.js.map +1 -1
- package/dist/features/composites/derived-chrome.js +34 -28
- package/dist/features/composites/derived-chrome.js.map +1 -1
- package/dist/features/composites/editing-settings-keys.js +6 -6
- package/dist/features/composites/editing-settings-keys.js.map +1 -1
- package/dist/features/composites/feature.d.ts +10 -10
- package/dist/features/composites/feature.js +58 -46
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/fences.d.ts +3 -3
- package/dist/features/composites/fences.js +18 -13
- package/dist/features/composites/fences.js.map +1 -1
- package/dist/features/composites/focus.d.ts +10 -10
- package/dist/features/composites/focus.js +11 -11
- package/dist/features/composites/focus.js.map +1 -1
- package/dist/features/composites/heading-composites.d.ts +5 -5
- package/dist/features/composites/heading-composites.js +13 -16
- package/dist/features/composites/heading-composites.js.map +1 -1
- package/dist/features/composites/index.d.ts +20 -18
- package/dist/features/composites/index.js +9 -7
- package/dist/features/composites/inspector-model.d.ts +45 -22
- package/dist/features/composites/inspector-model.js +53 -14
- package/dist/features/composites/inspector-model.js.map +1 -1
- package/dist/features/composites/legacy.d.ts +5 -4
- package/dist/features/composites/legacy.js +16 -23
- package/dist/features/composites/legacy.js.map +1 -1
- package/dist/features/composites/markdown-export.js +3 -3
- package/dist/features/composites/markdown-export.js.map +1 -1
- package/dist/features/composites/merge.d.ts +8 -8
- package/dist/features/composites/merge.js +23 -22
- package/dist/features/composites/merge.js.map +1 -1
- package/dist/features/composites/meta-display.js +5 -5
- package/dist/features/composites/meta-display.js.map +1 -1
- package/dist/features/composites/meta-keys.d.ts +5 -5
- package/dist/features/composites/meta-keys.js +8 -8
- package/dist/features/composites/meta-keys.js.map +1 -1
- package/dist/features/composites/node-views.d.ts +3 -3
- package/dist/features/composites/node-views.js +2 -2
- package/dist/features/composites/node-views.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +13 -13
- package/dist/features/composites/nodes.js +100 -77
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/outline.d.ts +15 -12
- package/dist/features/composites/outline.js +24 -21
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/properties.d.ts +51 -0
- package/dist/features/composites/properties.js +89 -0
- package/dist/features/composites/properties.js.map +1 -0
- package/dist/features/composites/react/BibliographyEditor.js +5 -5
- package/dist/features/composites/react/BibliographyEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeBasketMenu.js +2 -2
- package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -1
- package/dist/features/composites/react/CompositeChrome.d.ts +1 -1
- package/dist/features/composites/react/CompositeChrome.js +15 -6
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.d.ts +1 -1
- package/dist/features/composites/react/CompositeDetails.js +11 -10
- package/dist/features/composites/react/CompositeDetails.js.map +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.d.ts +5 -5
- package/dist/features/composites/react/CompositeFieldEditor.js +6 -6
- package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeInspector.d.ts +3 -3
- package/dist/features/composites/react/CompositeInspector.js +142 -128
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.js +2 -2
- package/dist/features/composites/react/CompositeStatusMenu.js.map +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeTable.d.ts +1 -1
- package/dist/features/composites/react/CompositeTable.js +3 -3
- package/dist/features/composites/react/CompositeTable.js.map +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeView.js +17 -15
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +13 -4
- package/dist/features/composites/react/DerivationBadge.js.map +1 -1
- package/dist/features/composites/react/FocusChip.d.ts +1 -1
- package/dist/features/composites/react/FocusChip.js +1 -1
- package/dist/features/composites/react/FocusChip.js.map +1 -1
- package/dist/features/composites/react/ReferenceFieldSelect.js +3 -3
- package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -1
- package/dist/features/composites/react/ReferenceView.d.ts +1 -1
- package/dist/features/composites/react/ReferenceView.js +1 -1
- package/dist/features/composites/react/ReferenceView.js.map +1 -1
- package/dist/features/composites/react/SettingsEditingTab.d.ts +4 -4
- package/dist/features/composites/react/SettingsEditingTab.js +20 -20
- package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/react.js +1 -1
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/reference-ai.js +1 -1
- package/dist/features/composites/reference-ai.js.map +1 -1
- package/dist/features/composites/reference-label.d.ts +3 -3
- package/dist/features/composites/reference-label.js +6 -6
- package/dist/features/composites/reference-label.js.map +1 -1
- package/dist/features/composites/reference-suggestion.d.ts +1 -1
- package/dist/features/composites/reference-suggestion.js +3 -3
- package/dist/features/composites/reference-suggestion.js.map +1 -1
- package/dist/features/composites/reference-text.d.ts +2 -2
- package/dist/features/composites/reference-text.js +4 -4
- package/dist/features/composites/reference-text.js.map +1 -1
- package/dist/features/composites/reference.d.ts +3 -3
- package/dist/features/composites/reference.js +1 -1
- package/dist/features/composites/reference.js.map +1 -1
- package/dist/features/composites/reveal-source.d.ts +3 -3
- package/dist/features/composites/reveal-source.js +6 -6
- package/dist/features/composites/reveal-source.js.map +1 -1
- package/dist/features/composites/rows-model.d.ts +2 -2
- package/dist/features/composites/rows-model.js +4 -3
- package/dist/features/composites/rows-model.js.map +1 -1
- package/dist/features/composites/rows.d.ts +16 -28
- package/dist/features/composites/rows.js +20 -54
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/features/composites/schema.d.ts +12 -11
- package/dist/features/composites/schema.js +10 -9
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/source.d.ts +20 -0
- package/dist/features/composites/source.js +40 -0
- package/dist/features/composites/source.js.map +1 -0
- package/dist/features/composites/split.d.ts +11 -11
- package/dist/features/composites/split.js +20 -20
- package/dist/features/composites/split.js.map +1 -1
- package/dist/features/composites/structure-mode.js +3 -3
- package/dist/features/composites/structure-mode.js.map +1 -1
- package/dist/features/composites/table-model.js +2 -2
- package/dist/features/composites/table-model.js.map +1 -1
- package/dist/features/composites/use-composite-inspector.js +3 -1
- package/dist/features/composites/use-composite-inspector.js.map +1 -1
- package/dist/features/composites/use-composite-rows.d.ts +2 -2
- package/dist/features/composites/use-composite-rows.js.map +1 -1
- package/dist/features/composites/vue.js +1 -1
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.js +150 -87
- package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +5 -1
- package/dist/features/image-studio/components/ImageStudioLibraryPanel.js +186 -0
- package/dist/features/image-studio/components/ImageStudioLibraryPanel.js.map +1 -0
- package/dist/features/image-studio/components/ImageStudioLibraryTile.js +134 -0
- package/dist/features/image-studio/components/ImageStudioLibraryTile.js.map +1 -0
- package/dist/features/image-studio/components/ImageStudioView.js.map +1 -1
- package/dist/features/image-studio/feature.d.ts +1 -1
- package/dist/features/image-studio/feature.js +1 -1
- package/dist/features/image-studio/feature.js.map +1 -1
- package/dist/features/image-studio/index.d.ts +3 -2
- package/dist/features/image-studio/index.js +2 -2
- package/dist/features/image-studio/library.d.ts +35 -0
- package/dist/features/image-studio/node-core.d.ts +3 -3
- package/dist/features/image-studio/node-core.js +1 -1
- package/dist/features/image-studio/node-core.js.map +1 -1
- package/dist/features/image-studio/node-store.d.ts +1 -1
- package/dist/features/image-studio/node-store.js +1 -1
- package/dist/features/image-studio/node-store.js.map +1 -1
- package/dist/features/image-studio/react/ImageStudioDialog.d.ts +1 -1
- package/dist/features/image-studio/react/ImageStudioDialog.js +46 -5
- package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/react/ImageStudioLibraryPanel.js +141 -0
- package/dist/features/image-studio/react/ImageStudioLibraryPanel.js.map +1 -0
- package/dist/features/image-studio/react/ImageStudioLibraryTile.js +105 -0
- package/dist/features/image-studio/react/ImageStudioLibraryTile.js.map +1 -0
- package/dist/features/image-studio/react/ImageStudioView.js +1 -1
- package/dist/features/image-studio/react/ImageStudioView.js.map +1 -1
- package/dist/features/image-studio/react/store.d.ts +8 -3
- package/dist/features/image-studio/react/store.js.map +1 -1
- package/dist/features/image-studio/react.d.ts +4 -3
- package/dist/features/image-studio/react.js +3 -3
- package/dist/features/image-studio/react.js.map +1 -1
- package/dist/features/image-studio/schema.d.ts +1 -1
- package/dist/features/image-studio/schema.js +1 -1
- package/dist/features/image-studio/schema.js.map +1 -1
- package/dist/features/image-studio/state.d.ts +8 -3
- package/dist/features/image-studio/state.js +11 -3
- package/dist/features/image-studio/state.js.map +1 -1
- package/dist/features/image-studio/store.d.ts +8 -3
- package/dist/features/image-studio/vue.d.ts +4 -3
- package/dist/features/image-studio/vue.js +3 -3
- package/dist/features/image-studio/vue.js.map +1 -1
- package/dist/features/mentions/tag.js +2 -1
- package/dist/features/mentions/tag.js.map +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/feature.d.ts +1 -1
- package/dist/features/themes/feature.js.map +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.d.ts +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/theme-runtime.js +1 -1
- package/dist/features/themes/theme-runtime.js.map +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/segment-node.js.map +1 -1
- package/dist/features/transfer/feature.js +1 -1
- package/dist/features/transfer/feature.js.map +1 -1
- package/dist/features/transfer/transforms.d.ts +4 -4
- package/dist/features/transfer/transforms.js +12 -12
- package/dist/features/transfer/transforms.js.map +1 -1
- package/dist/i18n/de/ai.js +4 -4
- package/dist/i18n/de/ai.js.map +1 -1
- package/dist/i18n/de/authors.js +1 -1
- package/dist/i18n/de/authors.js.map +1 -1
- package/dist/i18n/de/comments.js +1 -1
- package/dist/i18n/de/comments.js.map +1 -1
- package/dist/i18n/de/composites.js +62 -59
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/image-studio.js +11 -2
- package/dist/i18n/de/image-studio.js.map +1 -1
- package/dist/i18n/de/integrations.js +20 -20
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/markdown.js +1 -1
- package/dist/i18n/de/markdown.js.map +1 -1
- package/dist/i18n/de/palette.js +1 -1
- package/dist/i18n/de/palette.js.map +1 -1
- package/dist/i18n/de/themes.js +3 -3
- package/dist/i18n/de/themes.js.map +1 -1
- package/dist/i18n/de/transfer.js +2 -2
- package/dist/i18n/de/transfer.js.map +1 -1
- package/dist/i18n/de/tree.js +6 -6
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +110 -98
- package/dist/i18n/en/ai.js +2 -2
- package/dist/i18n/en/ai.js.map +1 -1
- package/dist/i18n/en/composites.js +64 -61
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/image-studio.js +9 -0
- package/dist/i18n/en/image-studio.js.map +1 -1
- package/dist/i18n/en/integrations.js +9 -9
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/markdown.js +1 -1
- package/dist/i18n/en/markdown.js.map +1 -1
- package/dist/i18n/en/palette.js +1 -1
- package/dist/i18n/en/palette.js.map +1 -1
- package/dist/i18n/en/themes.js +4 -4
- package/dist/i18n/en/themes.js.map +1 -1
- package/dist/i18n/en/tree.js +5 -5
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/integrations/composite-ai/bibliography-fetch.d.ts +6 -6
- package/dist/integrations/composite-ai/bibliography-fetch.js +19 -19
- package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -1
- package/dist/integrations/composite-ai/composite-fill.d.ts +4 -4
- package/dist/integrations/composite-ai/composite-fill.js +5 -5
- package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
- package/dist/integrations/composite-ai/composite-summary.d.ts +2 -2
- package/dist/integrations/composite-ai/composite-summary.js +5 -5
- package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +17 -17
- package/dist/integrations/composite-ai/derive.js +27 -26
- package/dist/integrations/composite-ai/derive.js.map +1 -1
- package/dist/integrations/composite-ai/index.d.ts +14 -15
- package/dist/integrations/composite-ai/index.js +98 -77
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.d.ts +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.js +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -1
- package/dist/integrations/composite-ai/rendition.d.ts +9 -9
- package/dist/integrations/composite-ai/rendition.js +27 -27
- package/dist/integrations/composite-ai/rendition.js.map +1 -1
- package/dist/integrations/composite-ai/split.d.ts +8 -8
- package/dist/integrations/composite-ai/split.js +12 -12
- package/dist/integrations/composite-ai/split.js.map +1 -1
- package/dist/integrations/composite-ai/transfer-transforms.d.ts +2 -2
- package/dist/integrations/composite-ai/transfer-transforms.js +11 -11
- package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -1
- package/dist/integrations/composite-media/index.d.ts +4 -3
- package/dist/integrations/composite-media/index.js +20 -14
- package/dist/integrations/composite-media/index.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
- package/dist/markdown/document.js +1 -1
- package/dist/markdown/document.js.map +1 -1
- package/dist/nuxt/module.d.ts +48 -24
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/server/plugins/editor-server.js +3 -2
- package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
- package/dist/presets/compose.d.ts +6 -6
- package/dist/presets/compose.js +7 -4
- package/dist/presets/compose.js.map +1 -1
- package/dist/react/AdvancedEditor.d.ts +2 -2
- package/dist/react/AdvancedEditor.js +1 -1
- package/dist/react/AdvancedEditor.js.map +1 -1
- package/dist/react/compose.d.ts +1 -1
- package/dist/react/compose.js +1 -1
- package/dist/react/compose.js.map +1 -1
- package/dist/react/composed-surface.d.ts +5 -5
- package/dist/react/composed-surface.js +2 -2
- package/dist/react/composed-surface.js.map +1 -1
- package/dist/server/ai.d.ts +7 -7
- package/dist/server/ai.js +1 -1
- package/dist/server/ai.js.map +1 -1
- package/dist/server/auth.d.ts +1 -1
- package/dist/server/collab-state.d.ts +1 -1
- package/dist/server/collab-state.js.map +1 -1
- package/dist/server/collab.d.ts +4 -4
- package/dist/server/collab.js +22 -17
- package/dist/server/collab.js.map +1 -1
- package/dist/server/handlers/ai.d.ts +9 -6
- package/dist/server/handlers/ai.js +13 -17
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +4 -0
- package/dist/server/handlers/context.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +33 -32
- package/dist/server/handlers/editor.js +13 -8
- package/dist/server/handlers/editor.js.map +1 -1
- package/dist/server/handlers/images.d.ts +1 -1
- package/dist/server/handlers/images.js +1 -1
- package/dist/server/handlers/images.js.map +1 -1
- package/dist/server/host.d.ts +4 -0
- package/dist/server/host.js +10 -2
- package/dist/server/host.js.map +1 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/ports.d.ts +8 -8
- package/dist/server/ports.js.map +1 -1
- package/dist/settings/index.d.ts +20 -19
- package/dist/settings/index.js +25 -24
- package/dist/settings/index.js.map +1 -1
- package/dist/settings/migrate.d.ts +18 -0
- package/dist/settings/migrate.js +46 -0
- package/dist/settings/migrate.js.map +1 -0
- package/dist/settings/values.d.ts +12 -12
- package/dist/settings/values.js +11 -11
- package/dist/settings/values.js.map +1 -1
- package/dist/styles/composites.css +10 -10
- package/dist/tree/DocumentTree.js +12 -5
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/drop-rules.js +5 -5
- package/dist/tree/drop-rules.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +14 -5
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +5 -5
- package/dist/tree/tree-host.js.map +1 -1
- package/dist/tree/tree-items.js +34 -28
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +16 -16
- package/dist/tree/tree-model.js.map +1 -1
- package/dist/tree/tree-rows.js +2 -2
- package/dist/tree/tree-rows.js.map +1 -1
- package/dist/tree/tree-view-model.d.ts +2 -2
- package/dist/tree/tree-view-model.js.map +1 -1
- package/dist/vue/AdvancedEditor.js +2 -2
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +10 -10
- package/dist/vue/SimpleEditor.vue.d.ts +4 -4
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/dist/vue/compose.d.ts +1 -1
- package/dist/vue/compose.js +1 -1
- package/dist/vue/compose.js.map +1 -1
- package/package.json +1 -1
- package/dist/features/composites/derivation.d.ts +0 -62
- package/dist/features/composites/derivation.js +0 -102
- package/dist/features/composites/derivation.js.map +0 -1
- package/dist/features/composites/kinds.d.ts +0 -89
- package/dist/features/composites/kinds.js +0 -285
- package/dist/features/composites/kinds.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outline-sanitize.js","names":[],"sources":["../../../src/integrations/composite-ai/outline-sanitize.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport type { Transaction } from '@tiptap/pm/state'\nimport type { EditorBackend } from '../../core/backend'\nimport type { Notifier } from '../../core/notify'\nimport type { Translate } from '../../i18n'\nimport { AI_LIMITS, type ModelRef, notifyAiRateLimited, postAiSse } from '../../features/ai'\nimport { headingGroupRange } from '../../features/composites/schema'\n\n/** A heading of the document as the model sees it, plus where it is: `i` is its index in document order, `chars` the text length of the blocks under it up to the next heading of the same or a higher level. */\nexport interface OutlineHeading { i: number, level: number, text: string, chars: number, pos: number }\n\n/** The model's answer for one heading: its level and text (empty: the heading is removed). */\nexport interface OutlineEntry { i: number, level: number, text: string }\n\n/** Every `heading` node of `doc` (any depth, document order). */\nexport function collectHeadings(doc: PMNode): OutlineHeading[] {\n const headings: OutlineHeading[] = []\n doc.descendants((node, pos, parent, index) => {\n if (node.type.name !== 'heading' || !parent) return\n const { to } = headingGroupRange(parent, index)\n let chars = 0\n for (let sibling = index + 1; sibling < to; sibling++) chars += parent.child(sibling).textContent.length\n headings.push({ i: headings.length, level: node.attrs.level as number, text: node.textContent, chars, pos })\n })\n return headings\n}\n\n/**\n * The model's answer as one entry per heading, or `null` when it is not exactly that: a JSON list (a Markdown code\n * fence around it is tolerated) of the same length as `headings`, `i` equal to the position, `level` an integer 1..6,\n * `text` a string.\n */\nexport function parseOutlineResponse(text: string, headings: readonly { i: number }[]): OutlineEntry[] | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(text.trim().replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```$/, ''))\n }\n catch {\n return null\n }\n if (!Array.isArray(parsed) || parsed.length !== headings.length) return null\n const entries: OutlineEntry[] = []\n for (const [index, item] of parsed.entries()) {\n const { i, level, text: value } = (item ?? {}) as Partial<Record<'i' | 'level' | 'text', unknown>>\n if (i !== index || typeof level !== 'number' || !Number.isInteger(level) || level < 1 || level > 6 || typeof value !== 'string') return null\n entries.push({ i, level, text: value.trim() })\n }\n return entries\n}\n\n/**\n * Writes `outline` into `tr` for the `headings` collected from `tr.doc` (so far unchanged): an empty text removes the\n * heading, otherwise its level and, when it differs, its text (as plain text) are set. Runs from the last heading to\n * the first, so the positions of the earlier ones stay valid. Returns how many headings changed.\n */\nexport function applyOutline(tr: Transaction, headings: readonly OutlineHeading[], outline: readonly OutlineEntry[]): number {\n let changed = 0\n for (let index = headings.length - 1; index >= 0; index--) {\n const { pos } = headings[index]!\n const { level, text } = outline[index]!\n const node = tr.doc.nodeAt(pos)\n if (node?.type.name !== 'heading') continue\n if (!text) {\n tr.delete(pos, pos + node.nodeSize)\n changed++\n continue\n }\n const retext = text !== node.textContent\n if (!retext && level === node.attrs.level) continue\n if (level !== node.attrs.level) tr.setNodeMarkup(pos, undefined, { ...node.attrs, level })\n if (retext) tr.replaceWith(pos + 1, pos + 1 + node.content.size, tr.doc.type.schema.text(text))\n changed++\n }\n return changed\n}\n\n/**\n * „Gliederung bereinigen (KI)“: sends the headings of the whole document (task `outline-sanitize`, `generate`
|
|
1
|
+
{"version":3,"file":"outline-sanitize.js","names":[],"sources":["../../../src/integrations/composite-ai/outline-sanitize.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport type { Node as PMNode } from '@tiptap/pm/model'\nimport type { Transaction } from '@tiptap/pm/state'\nimport type { EditorBackend } from '../../core/backend'\nimport type { Notifier } from '../../core/notify'\nimport type { Translate } from '../../i18n'\nimport { AI_LIMITS, type ModelRef, notifyAiRateLimited, postAiSse } from '../../features/ai'\nimport { headingGroupRange } from '../../features/composites/schema'\n\n/** A heading of the document as the model sees it, plus where it is: `i` is its index in document order, `chars` the text length of the blocks under it up to the next heading of the same or a higher level. */\nexport interface OutlineHeading { i: number, level: number, text: string, chars: number, pos: number }\n\n/** The model's answer for one heading: its level and text (empty: the heading is removed). */\nexport interface OutlineEntry { i: number, level: number, text: string }\n\n/** Every `heading` node of `doc` (any depth, document order). */\nexport function collectHeadings(doc: PMNode): OutlineHeading[] {\n const headings: OutlineHeading[] = []\n doc.descendants((node, pos, parent, index) => {\n if (node.type.name !== 'heading' || !parent) return\n const { to } = headingGroupRange(parent, index)\n let chars = 0\n for (let sibling = index + 1; sibling < to; sibling++) chars += parent.child(sibling).textContent.length\n headings.push({ i: headings.length, level: node.attrs.level as number, text: node.textContent, chars, pos })\n })\n return headings\n}\n\n/**\n * The model's answer as one entry per heading, or `null` when it is not exactly that: a JSON list (a Markdown code\n * fence around it is tolerated) of the same length as `headings`, `i` equal to the position, `level` an integer 1..6,\n * `text` a string.\n */\nexport function parseOutlineResponse(text: string, headings: readonly { i: number }[]): OutlineEntry[] | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(text.trim().replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```$/, ''))\n }\n catch {\n return null\n }\n if (!Array.isArray(parsed) || parsed.length !== headings.length) return null\n const entries: OutlineEntry[] = []\n for (const [index, item] of parsed.entries()) {\n const { i, level, text: value } = (item ?? {}) as Partial<Record<'i' | 'level' | 'text', unknown>>\n if (i !== index || typeof level !== 'number' || !Number.isInteger(level) || level < 1 || level > 6 || typeof value !== 'string') return null\n entries.push({ i, level, text: value.trim() })\n }\n return entries\n}\n\n/**\n * Writes `outline` into `tr` for the `headings` collected from `tr.doc` (so far unchanged): an empty text removes the\n * heading, otherwise its level and, when it differs, its text (as plain text) are set. Runs from the last heading to\n * the first, so the positions of the earlier ones stay valid. Returns how many headings changed.\n */\nexport function applyOutline(tr: Transaction, headings: readonly OutlineHeading[], outline: readonly OutlineEntry[]): number {\n let changed = 0\n for (let index = headings.length - 1; index >= 0; index--) {\n const { pos } = headings[index]!\n const { level, text } = outline[index]!\n const node = tr.doc.nodeAt(pos)\n if (node?.type.name !== 'heading') continue\n if (!text) {\n tr.delete(pos, pos + node.nodeSize)\n changed++\n continue\n }\n const retext = text !== node.textContent\n if (!retext && level === node.attrs.level) continue\n if (level !== node.attrs.level) tr.setNodeMarkup(pos, undefined, { ...node.attrs, level })\n if (retext) tr.replaceWith(pos + 1, pos + 1 + node.content.size, tr.doc.type.schema.text(text))\n changed++\n }\n return changed\n}\n\n/**\n * „Gliederung bereinigen (KI)“: sends the headings of the whole document (task `outline-sanitize`, `generate` Model role) and\n * applies the model's repaired outline (levels without gaps, numbering and trailing punctuation gone, empty headings\n * merged into the next) in one transaction. A malformed answer, or a document whose headings changed while the model\n * ran, writes nothing and shows a notice. Notices go through `notify`, in the language of `t`.\n */\nexport async function sanitizeOutline(editor: Editor, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate): Promise<void> {\n const headings = collectHeadings(editor.state.doc)\n if (!headings.length) {\n notify({ title: t('integrations.outlineSanitize.none'), kind: 'info' })\n return\n }\n const selection = JSON.stringify(headings.map(({ i, level, text, chars }) => ({ i, level, text, chars })))\n if (selection.length > AI_LIMITS.selection) {\n notify({ title: t('integrations.outlineSanitize.tooLong'), kind: 'error' })\n return\n }\n\n const toastId = 'outline-sanitize'\n const controller = new AbortController()\n const abort = () => controller.abort()\n editor.on('destroy', abort)\n notify({ id: toastId, title: t('integrations.outlineSanitize.running'), kind: 'info', icon: 'i-lucide-loader-circle', duration: 0 })\n let output = ''\n try {\n const outcome = await postAiSse(ai, 'generate', {\n task: 'outline-sanitize',\n prompt: '',\n selection,\n context: '',\n model: modelRef('generate'),\n }, { signal: controller.signal, t, onDelta: (text) => { output += text } })\n\n if (outcome.status === 'aborted' || editor.isDestroyed) return\n if (outcome.status === 'rate-limited') {\n notifyAiRateLimited(notify, t)\n return\n }\n if (outcome.status === 'error') {\n notify({ title: t('integrations.outlineSanitize.failed'), description: outcome.message, kind: 'error' })\n return\n }\n const outline = parseOutlineResponse(output, headings)\n if (!outline) {\n notify({ title: t('integrations.outlineSanitize.invalid'), kind: 'warning' })\n return\n }\n\n const current = collectHeadings(editor.state.doc)\n if (current.length !== headings.length || current.some((heading, index) => heading.pos !== headings[index]!.pos || heading.level !== headings[index]!.level || heading.text !== headings[index]!.text)) {\n notify({ title: t('integrations.outlineSanitize.gone'), kind: 'error' })\n return\n }\n const tr = editor.state.tr\n if (applyOutline(tr, current, outline)) editor.view.dispatch(tr)\n notify({ title: t('integrations.outlineSanitize.done'), kind: 'success', icon: 'i-lucide-list-tree' })\n }\n finally {\n notify.dismiss?.(toastId)\n editor.off('destroy', abort)\n }\n}\n"],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import { Node as Node$1 } from "@tiptap/pm/model";
|
|
|
8
8
|
//#region src/integrations/composite-ai/rendition.d.ts
|
|
9
9
|
export interface RenditionOptions {
|
|
10
10
|
/**
|
|
11
|
-
* Replace the rendition at this position (a child of the source-holding
|
|
11
|
+
* Replace the rendition at this position (a child of the source-holding Typed block) in place instead of adding a new one
|
|
12
12
|
* after the source: its chrome (title, description, tags) and block id stay, body and derivation are new.
|
|
13
13
|
*/
|
|
14
14
|
replace?: {
|
|
@@ -16,26 +16,26 @@ export interface RenditionOptions {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* „
|
|
19
|
+
* the „Create readable rendition“ action: extracts the readable content of the source of the Typed block at `pos` (`ai.extract`, method
|
|
20
20
|
* `auto`: transcript with timestamps for audio/video, full text or OCR for a PDF, a description for an image) and adds
|
|
21
|
-
* it as a `rendition`
|
|
22
|
-
* the stored raw output). Bibliographic data the server found fills an empty `meta.bib` of the
|
|
23
|
-
* title an empty
|
|
24
|
-
* The
|
|
21
|
+
* it as a `rendition` Typed block right after the source block, carrying `meta.derivation` (asset, method, model, hashes,
|
|
22
|
+
* the stored raw output). Bibliographic data the server found fills an empty `meta.bib` of the Typed block, and the work's
|
|
23
|
+
* title an empty Typed block title. With `options.replace` the existing rendition is replaced in place (same block id).
|
|
24
|
+
* The Typed block is anchored through its description when the request starts and found again after it ends, so edits
|
|
25
25
|
* while it runs do not misplace the result. Notices go through `notify`, in the language of `t`; the progress notice
|
|
26
26
|
* stays until the request ends.
|
|
27
27
|
*/
|
|
28
28
|
export declare function createRendition(editor: Editor, pos: number, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate, options?: RenditionOptions): Promise<void>;
|
|
29
29
|
/**
|
|
30
|
-
* Whether „
|
|
30
|
+
* Whether the „Create readable rendition“ action applies to `node`: a Typed block of a source-holding Block type whose source block was
|
|
31
31
|
* found. A recording needs speech-to-text (`transcription`: the app's public configuration enables it); everything else
|
|
32
32
|
* is extracted by the server.
|
|
33
33
|
*/
|
|
34
34
|
export declare function canCreateRendition(editor: Editor, node: Node$1, transcription: boolean): boolean;
|
|
35
|
-
/** Whether „Neu erzeugen (ersetzen)“ applies to the
|
|
35
|
+
/** Whether „Neu erzeugen (ersetzen)“ applies to the Typed block `node` at `pos`: an asset-derived rendition inside a Typed block that still has a source. */
|
|
36
36
|
export declare function canRegenerateRendition(editor: Editor, node: Node$1, pos: number, transcription: boolean): boolean;
|
|
37
37
|
/**
|
|
38
|
-
* „Neu erzeugen (ersetzen)“: makes the rendition at `pos` again from the source of its
|
|
38
|
+
* „Neu erzeugen (ersetzen)“: makes the rendition at `pos` again from the source of its Typed block and replaces it in place
|
|
39
39
|
* (`createRendition` with `replace`; the block id stays, so references keep resolving). A rendition whose body was
|
|
40
40
|
* edited since it was made is not replaced silently: a warning notice offers „Trotzdem ersetzen“, dismissing the notice
|
|
41
41
|
* cancels. The rendition is found again by its id when the button is pressed.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { parseBlocks, structureBlocks } from "../../core/pipeline.js";
|
|
2
1
|
import { blockIdOf, findBlockById, newBlockId } from "../../core/schema/block-ids.js";
|
|
2
|
+
import { capabilitiesOf } from "../../core/capabilities.js";
|
|
3
|
+
import { parseBlocks, structureBlocks } from "../../core/pipeline.js";
|
|
4
|
+
import { DERIVATION_KEY, derivationOf, derivationState } from "../../core/derivation.js";
|
|
3
5
|
import { captureRange, releaseRanges, resolveRange } from "../../core/doc-anchors.js";
|
|
4
6
|
import { fetchErrorMessage } from "../../core/fetch-error.js";
|
|
5
7
|
import { sha256Hex } from "../../core/hash.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { bodyMarkdown, derivationOf, derivationState, sourceOf } from "../../features/composites/derivation.js";
|
|
9
|
-
import { compositeJSON, compositeKindsOf } from "../../features/composites/nodes.js";
|
|
8
|
+
import { acceptsChild, blockTypeOf } from "../../features/composites/block-types.js";
|
|
9
|
+
import { blockTypesOf, compositeJSON } from "../../features/composites/nodes.js";
|
|
10
10
|
import { bibOf } from "../../features/composites/bibliography.js";
|
|
11
|
+
import { bodyMarkdown, sourceOf } from "../../features/composites/source.js";
|
|
11
12
|
import { isRateLimited, notifyAiRateLimited } from "../../features/ai/rate-limit.js";
|
|
12
13
|
import { Fragment } from "@tiptap/pm/model";
|
|
13
14
|
//#region src/integrations/composite-ai/rendition.ts
|
|
@@ -38,7 +39,7 @@ function withPageLabels(markdown, t) {
|
|
|
38
39
|
/**
|
|
39
40
|
* The body blocks of a rendition: one `transcriptSegment` per timed segment (speaker empty, `start`/`end` in seconds)
|
|
40
41
|
* when the editor has the transcript feature, else the Markdown parsed and brought into the preferred structure
|
|
41
|
-
* (chapters become nested `section`
|
|
42
|
+
* (chapters become nested `section` Typed blocks in composite structure; PDF page breaks become page label paragraphs).
|
|
42
43
|
*/
|
|
43
44
|
function renditionBody(editor, response, t) {
|
|
44
45
|
if (editor.schema.nodes.transcriptSegment && response.segments?.length) {
|
|
@@ -73,20 +74,20 @@ function sealed(editor, node, derivation) {
|
|
|
73
74
|
}, node.content, node.marks);
|
|
74
75
|
}
|
|
75
76
|
/**
|
|
76
|
-
* „
|
|
77
|
+
* the „Create readable rendition“ action: extracts the readable content of the source of the Typed block at `pos` (`ai.extract`, method
|
|
77
78
|
* `auto`: transcript with timestamps for audio/video, full text or OCR for a PDF, a description for an image) and adds
|
|
78
|
-
* it as a `rendition`
|
|
79
|
-
* the stored raw output). Bibliographic data the server found fills an empty `meta.bib` of the
|
|
80
|
-
* title an empty
|
|
81
|
-
* The
|
|
79
|
+
* it as a `rendition` Typed block right after the source block, carrying `meta.derivation` (asset, method, model, hashes,
|
|
80
|
+
* the stored raw output). Bibliographic data the server found fills an empty `meta.bib` of the Typed block, and the work's
|
|
81
|
+
* title an empty Typed block title. With `options.replace` the existing rendition is replaced in place (same block id).
|
|
82
|
+
* The Typed block is anchored through its description when the request starts and found again after it ends, so edits
|
|
82
83
|
* while it runs do not misplace the result. Notices go through `notify`, in the language of `t`; the progress notice
|
|
83
84
|
* stays until the request ends.
|
|
84
85
|
*/
|
|
85
86
|
async function createRendition(editor, pos, ai, modelRef, notify, t, options = {}) {
|
|
86
|
-
const kinds =
|
|
87
|
+
const kinds = blockTypesOf(editor);
|
|
87
88
|
const composite = editor.state.doc.nodeAt(pos);
|
|
88
89
|
const source = composite && sourceOf(composite, kinds);
|
|
89
|
-
if (composite?.type.name !== "
|
|
90
|
+
if (composite?.type.name !== "typedBlock" || !source) return;
|
|
90
91
|
const asset = source.asset;
|
|
91
92
|
if (!asset) {
|
|
92
93
|
notify({
|
|
@@ -95,7 +96,7 @@ async function createRendition(editor, pos, ai, modelRef, notify, t, options = {
|
|
|
95
96
|
});
|
|
96
97
|
return;
|
|
97
98
|
}
|
|
98
|
-
if (!
|
|
99
|
+
if (!acceptsChild(kinds, String(composite.attrs.blockType), "rendition")) {
|
|
99
100
|
notify({
|
|
100
101
|
title: t("integrations.rendition.notAccepted"),
|
|
101
102
|
kind: "error"
|
|
@@ -106,7 +107,7 @@ async function createRendition(editor, pos, ai, modelRef, notify, t, options = {
|
|
|
106
107
|
if (options.replace) {
|
|
107
108
|
const target = editor.state.doc.nodeAt(options.replace.pos);
|
|
108
109
|
replaceId = target && options.replace.pos > pos && options.replace.pos < pos + composite.nodeSize ? blockIdOf(target) : null;
|
|
109
|
-
if (target?.type.name !== "
|
|
110
|
+
if (target?.type.name !== "typedBlock" || !replaceId) return;
|
|
110
111
|
}
|
|
111
112
|
const descriptionPos = pos + 1 + composite.child(0).nodeSize;
|
|
112
113
|
const anchor = captureRange(editor, {
|
|
@@ -114,7 +115,7 @@ async function createRendition(editor, pos, ai, modelRef, notify, t, options = {
|
|
|
114
115
|
to: descriptionPos + 1 + composite.child(1).content.size
|
|
115
116
|
});
|
|
116
117
|
const toastId = `rendition-${blockIdOf(composite) ?? asset.name}`;
|
|
117
|
-
const wantsBibliography =
|
|
118
|
+
const wantsBibliography = capabilitiesOf(editor, composite).has("bibliography");
|
|
118
119
|
const model = modelRef("generate");
|
|
119
120
|
const controller = new AbortController();
|
|
120
121
|
const abort = () => controller.abort();
|
|
@@ -146,7 +147,7 @@ async function createRendition(editor, pos, ai, modelRef, notify, t, options = {
|
|
|
146
147
|
}
|
|
147
148
|
const range = resolveRange(editor.state, anchor);
|
|
148
149
|
const $from = range && editor.state.doc.resolve(range.from);
|
|
149
|
-
const parentPos = $from && $from.parent.type.name === "
|
|
150
|
+
const parentPos = $from && $from.parent.type.name === "typedBlockDescription" ? $from.before($from.depth - 1) : null;
|
|
150
151
|
const parent = parentPos === null ? null : editor.state.doc.nodeAt(parentPos);
|
|
151
152
|
const currentSource = parent && sourceOf(parent, kinds);
|
|
152
153
|
const replaced = replaceId ? findBlockById(editor.state.doc, replaceId) : null;
|
|
@@ -179,7 +180,7 @@ async function createRendition(editor, pos, ai, modelRef, notify, t, options = {
|
|
|
179
180
|
const titleKey = TITLED_METHODS.includes(response.method) ? `integrations.rendition.title.${response.method}` : null;
|
|
180
181
|
const json = compositeJSON("rendition", body);
|
|
181
182
|
const [title, ...rest] = json.content;
|
|
182
|
-
const text = titleKey ? t(titleKey) :
|
|
183
|
+
const text = titleKey ? t(titleKey) : blockTypeOf(kinds, "rendition").label;
|
|
183
184
|
node = sealed(editor, schema.nodeFromJSON({
|
|
184
185
|
...json,
|
|
185
186
|
attrs: {
|
|
@@ -250,34 +251,34 @@ async function createRendition(editor, pos, ai, modelRef, notify, t, options = {
|
|
|
250
251
|
}
|
|
251
252
|
}
|
|
252
253
|
/**
|
|
253
|
-
* Whether „
|
|
254
|
+
* Whether the „Create readable rendition“ action applies to `node`: a Typed block of a source-holding Block type whose source block was
|
|
254
255
|
* found. A recording needs speech-to-text (`transcription`: the app's public configuration enables it); everything else
|
|
255
256
|
* is extracted by the server.
|
|
256
257
|
*/
|
|
257
258
|
function canCreateRendition(editor, node, transcription) {
|
|
258
|
-
const source = sourceOf(node,
|
|
259
|
+
const source = sourceOf(node, blockTypesOf(editor));
|
|
259
260
|
if (!source) return false;
|
|
260
261
|
return transcription || source.node.type.name !== "audio" && source.node.type.name !== "video";
|
|
261
262
|
}
|
|
262
|
-
/** The source-holding
|
|
263
|
+
/** The source-holding Typed block around the asset-derived rendition at `pos`; null for anything else. */
|
|
263
264
|
function renditionParentOf(editor, pos) {
|
|
264
265
|
const node = editor.state.doc.nodeAt(pos);
|
|
265
266
|
const derivation = node && derivationOf(node);
|
|
266
267
|
if (!derivation || !("asset" in derivation.from)) return null;
|
|
267
268
|
const $pos = editor.state.doc.resolve(pos);
|
|
268
|
-
if ($pos.depth === 0 || $pos.parent.type.name !== "
|
|
269
|
+
if ($pos.depth === 0 || $pos.parent.type.name !== "typedBlock" || !sourceOf($pos.parent, blockTypesOf(editor))) return null;
|
|
269
270
|
return {
|
|
270
271
|
node: $pos.parent,
|
|
271
272
|
pos: $pos.before()
|
|
272
273
|
};
|
|
273
274
|
}
|
|
274
|
-
/** Whether „Neu erzeugen (ersetzen)“ applies to the
|
|
275
|
+
/** Whether „Neu erzeugen (ersetzen)“ applies to the Typed block `node` at `pos`: an asset-derived rendition inside a Typed block that still has a source. */
|
|
275
276
|
function canRegenerateRendition(editor, node, pos, transcription) {
|
|
276
|
-
const parent =
|
|
277
|
+
const parent = renditionParentOf(editor, pos);
|
|
277
278
|
return parent !== null && canCreateRendition(editor, parent.node, transcription);
|
|
278
279
|
}
|
|
279
280
|
/**
|
|
280
|
-
* „Neu erzeugen (ersetzen)“: makes the rendition at `pos` again from the source of its
|
|
281
|
+
* „Neu erzeugen (ersetzen)“: makes the rendition at `pos` again from the source of its Typed block and replaces it in place
|
|
281
282
|
* (`createRendition` with `replace`; the block id stays, so references keep resolving). A rendition whose body was
|
|
282
283
|
* edited since it was made is not replaced silently: a warning notice offers „Trotzdem ersetzen“, dismissing the notice
|
|
283
284
|
* cancels. The rendition is found again by its id when the button is pressed.
|
|
@@ -292,8 +293,7 @@ function regenerateRendition(editor, pos, ai, modelRef, notify, t) {
|
|
|
292
293
|
const current = found && renditionParentOf(editor, found.pos);
|
|
293
294
|
if (found && current) createRendition(editor, current.pos, ai, modelRef, notify, t, { replace: { pos: found.pos } });
|
|
294
295
|
};
|
|
295
|
-
|
|
296
|
-
if (derivationState(parent.node, node, () => sha256Hex(bodyMarkdown(editor, node)), kinds).edited !== true) {
|
|
296
|
+
if (derivationState(editor.state.doc, pos, () => sha256Hex(bodyMarkdown(editor, node)))?.edited !== true) {
|
|
297
297
|
replace();
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rendition.js","names":[],"sources":["../../../src/integrations/composite-ai/rendition.ts"],"sourcesContent":["import type { Editor, JSONContent } from '@tiptap/core'\nimport { Fragment, type Node as PMNode } from '@tiptap/pm/model'\nimport type { EditorBackend } from '../../core/backend'\nimport type { ExtractResponse } from '../../core/backend-types'\nimport { captureRange, fetchErrorMessage, parseBlocks, releaseRanges, resolveRange, sha256Hex, structureBlocks } from '../../core'\nimport type { MessageKey, Translate } from '../../i18n'\nimport type { Notifier } from '../../core/notify'\nimport { blockIdOf, findBlockById, newBlockId } from '../../core/schema'\nimport { isRateLimited, type ModelRef, notifyAiRateLimited } from '../../features/ai'\nimport {\n bibOf,\n bodyMarkdown,\n COMPOSITE_CHROME_COUNT,\n compositeJSON,\n compositeKind,\n compositeKindsOf,\n DERIVATION_KEY,\n derivationOf,\n derivationState,\n type Derivation,\n kindAccepts,\n sourceOf,\n} from '../../features/composites/schema'\n\nexport interface RenditionOptions {\n /**\n * Replace the rendition at this position (a child of the source-holding Baustein) in place instead of adding a new one\n * after the source: its chrome (title, description, tags) and block id stay, body and derivation are new.\n */\n replace?: { pos: number }\n}\n\n/** Methods with their own title of a fresh rendition (`integrations.rendition.title.<method>`). */\nconst TITLED_METHODS: readonly string[] = ['transcription', 'pdf-text', 'ocr', 'description']\n\n/** What the server's `warnings` codes say; an unknown code is shown as it is. */\nconst WARNING_KEYS: Readonly<Record<string, MessageKey>> = {\n 'ocr-unavailable': 'integrations.rendition.warning.ocr-unavailable',\n 'crossref-unavailable': 'integrations.rendition.warning.crossref-unavailable',\n 'pages-truncated': 'integrations.rendition.warning.pages-truncated',\n 'ocr-truncated': 'integrations.rendition.warning.ocr-truncated',\n 'bibliography-failed': 'integrations.rendition.warning.bibliography-failed',\n}\n\n/** The `<!-- page N -->` line the server puts at the start of every PDF page after the first (`joinPages`). */\nconst PAGE_MARKER = /^<!-- page (\\d+) -->[ \\t]*$/gm\n\n/**\n * `markdown` with every page marker line replaced by an emphasised page label paragraph (`*S. 2*`): parsed as it is, a\n * marker is an HTML comment and would leave an empty paragraph without the page. The stored raw output keeps the markers.\n */\nfunction withPageLabels(markdown: string, t: Translate): string {\n return markdown.replace(PAGE_MARKER, (_marker, page: string) => `*${t('integrations.rendition.page', { n: page })}*`)\n}\n\n/**\n * The body blocks of a rendition: one `transcriptSegment` per timed segment (speaker empty, `start`/`end` in seconds)\n * when the editor has the transcript feature, else the Markdown parsed and brought into the preferred structure\n * (chapters become nested `section` Bausteine in composite structure; PDF page breaks become page label paragraphs).\n */\nfunction renditionBody(editor: Editor, response: ExtractResponse, t: Translate): JSONContent[] {\n if (editor.schema.nodes.transcriptSegment && response.segments?.length) {\n const segments = response.segments\n .filter(segment => segment.text.trim())\n .map((segment): JSONContent => ({\n type: 'transcriptSegment',\n attrs: { speaker: '', start: segment.start, end: segment.end },\n content: [{ type: 'text', text: segment.text.trim() }],\n }))\n if (segments.length) return segments\n }\n return response.markdown.trim() ? structureBlocks(editor, parseBlocks(editor, withPageLabels(response.markdown, t)).blocks) : []\n}\n\n/** `node` with `derivation` in its meta, `outputHash` being the hash of the Markdown of its body (what `derivationState` compares later). */\nfunction sealed(editor: Editor, node: PMNode, derivation: Derivation): PMNode {\n const outputHash = sha256Hex(bodyMarkdown(editor, node))\n return node.type.create({ ...node.attrs, meta: { ...node.attrs.meta, [DERIVATION_KEY]: { ...derivation, outputHash } } }, node.content, node.marks)\n}\n\n/**\n * „Lesefassung erzeugen“: extracts the readable content of the source of the Baustein at `pos` (`ai.extract`, method\n * `auto`: transcript with timestamps for audio/video, full text or OCR for a PDF, a description for an image) and adds\n * it as a `rendition` Baustein right after the source block, carrying `meta.derivation` (asset, method, model, hashes,\n * the stored raw output). Bibliographic data the server found fills an empty `meta.bib` of the Baustein, and the work's\n * title an empty Baustein title. With `options.replace` the existing rendition is replaced in place (same block id).\n * The Baustein is anchored through its description when the request starts and found again after it ends, so edits\n * while it runs do not misplace the result. Notices go through `notify`, in the language of `t`; the progress notice\n * stays until the request ends.\n */\nexport async function createRendition(editor: Editor, pos: number, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate, options: RenditionOptions = {}): Promise<void> {\n const kinds = compositeKindsOf(editor)\n const composite = editor.state.doc.nodeAt(pos)\n const source = composite && sourceOf(composite, kinds)\n if (composite?.type.name !== 'composite' || !source) return\n const asset = source.asset\n if (!asset) {\n notify({ title: t('integrations.rendition.noSource'), kind: 'error' })\n return\n }\n if (!kindAccepts(kinds, String(composite.attrs.kind), 'rendition')) {\n notify({ title: t('integrations.rendition.notAccepted'), kind: 'error' })\n return\n }\n\n let replaceId: string | null = null\n if (options.replace) {\n const target = editor.state.doc.nodeAt(options.replace.pos)\n replaceId = target && options.replace.pos > pos && options.replace.pos < pos + composite.nodeSize ? blockIdOf(target) : null\n if (target?.type.name !== 'composite' || !replaceId) return\n }\n\n const descriptionPos = pos + 1 + composite.child(0).nodeSize\n const anchor = captureRange(editor, { from: descriptionPos + 1, to: descriptionPos + 1 + composite.child(1).content.size })\n const toastId = `rendition-${blockIdOf(composite) ?? asset.name}`\n const wantsBibliography = compositeKind(kinds, String(composite.attrs.kind)).fields.some(field => field.type === 'bibliography')\n const model = modelRef('generate')\n\n const controller = new AbortController()\n const abort = () => controller.abort()\n editor.on('destroy', abort)\n notify({ id: toastId, title: t('integrations.rendition.running'), kind: 'info', icon: 'i-lucide-loader-circle', duration: 0 })\n try {\n const response = await ai.extract({ documentId: asset.documentId, name: asset.name, method: 'auto', model, bibliography: wantsBibliography }, controller.signal)\n if (editor.isDestroyed) return\n const body = renditionBody(editor, response, t)\n if (!body.length) {\n notify({ title: t('integrations.rendition.empty.title'), description: t('integrations.rendition.empty.description'), kind: 'error' })\n return\n }\n\n // The Baustein, found again through its anchored description; its source may have changed meanwhile.\n const range = resolveRange(editor.state, anchor)\n const $from = range && editor.state.doc.resolve(range.from)\n const parentPos = $from && $from.parent.type.name === 'compositeDescription' ? $from.before($from.depth - 1) : null\n const parent = parentPos === null ? null : editor.state.doc.nodeAt(parentPos)\n const currentSource = parent && sourceOf(parent, kinds)\n const replaced = replaceId ? findBlockById(editor.state.doc, replaceId) : null\n if (parentPos === null || !parent || !currentSource || (replaceId !== null && (!replaced || replaced.pos <= parentPos || replaced.pos >= parentPos + parent.nodeSize))) {\n notify({ title: t('integrations.rendition.gone.title'), description: t('integrations.rendition.gone.description'), kind: 'error' })\n return\n }\n\n // The model that wrote the content: the speech-to-text model for a transcription, the chat model for OCR and an\n // image description, none for the PDF text layer.\n const usedModel = response.method === 'transcription' ? response.model : response.method === 'ocr' || response.method === 'description' ? ('preset' in model ? model.preset : model.model) : undefined\n const derivation: Derivation = {\n from: { asset: asset.name },\n method: response.method,\n ...(usedModel ? { model: usedModel } : {}),\n createdAt: new Date().toISOString(),\n inputHash: response.inputHash,\n outputHash: '',\n ...(response.output ? { output: response.output } : {}),\n }\n const { schema } = editor\n const bodyNodes = body.map(block => schema.nodeFromJSON(block))\n const id = replaceId ?? newBlockId()\n let node: PMNode\n if (replaced) {\n const chrome = Array.from({ length: COMPOSITE_CHROME_COUNT }, (_, index) => replaced.node.child(index))\n node = sealed(editor, replaced.node.type.create(replaced.node.attrs, Fragment.from([...chrome, ...bodyNodes])), derivation)\n }\n else {\n const titleKey = TITLED_METHODS.includes(response.method) ? `integrations.rendition.title.${response.method}` as MessageKey : null\n const json = compositeJSON('rendition', body)\n const [title, ...rest] = json.content!\n const text = titleKey ? t(titleKey) : compositeKind(kinds, 'rendition').label\n const fresh = schema.nodeFromJSON({ ...json, attrs: { ...json.attrs, id }, content: [{ ...title!, content: [{ type: 'text', text }] }, ...rest] })\n node = sealed(editor, fresh, derivation)\n }\n\n const tr = editor.state.tr\n if (replaced) tr.replaceWith(replaced.pos, replaced.pos + replaced.node.nodeSize, node)\n else {\n let insertPos = parentPos + 1\n for (let index = 0; index <= currentSource.index; index++) insertPos += parent.child(index).nodeSize\n if (!parent.canReplace(currentSource.index + 1, currentSource.index + 1, Fragment.from(node))) {\n notify({ title: t('integrations.rendition.notAccepted'), kind: 'error' })\n return\n }\n tr.insert(insertPos, node)\n }\n // Bibliographic data fills only what is empty; the title is never part of `meta.bib`.\n const { bib } = response\n const workTitle = bib?.title?.trim()\n if (workTitle && !parent.child(0).textContent.trim()) tr.insert(parentPos + 2, schema.text(workTitle))\n if (bib && bibOf(parent) === null) {\n const csl = { ...bib }\n delete csl.title\n tr.setNodeMarkup(parentPos, undefined, { ...parent.attrs, meta: { ...parent.attrs.meta, bib: csl } })\n }\n editor.view.dispatch(tr)\n editor.commands.revealBlock(id)\n notify({ title: t('integrations.rendition.done'), kind: 'success', icon: 'i-lucide-captions' })\n if (response.warnings.length) {\n notify({\n title: t('integrations.rendition.warnings.title'),\n description: response.warnings.map(code => (WARNING_KEYS[code] ? t(WARNING_KEYS[code]) : code)).join(' '),\n kind: 'info',\n })\n }\n }\n catch (err) {\n if (controller.signal.aborted || editor.isDestroyed) return\n if (isRateLimited(err)) notifyAiRateLimited(notify, t)\n else notify({ title: t('integrations.rendition.failed'), description: fetchErrorMessage(err), kind: 'error' })\n }\n finally {\n notify.dismiss?.(toastId)\n editor.off('destroy', abort)\n releaseRanges(editor, [anchor])\n }\n}\n\n/**\n * Whether „Lesefassung erzeugen“ applies to `node`: a Baustein of a source-holding kind whose source block was\n * found. A recording needs speech-to-text (`transcription`: the app's public configuration enables it); everything else\n * is extracted by the server.\n */\nexport function canCreateRendition(editor: Editor, node: PMNode, transcription: boolean): boolean {\n const source = sourceOf(node, compositeKindsOf(editor))\n if (!source) return false\n return transcription || (source.node.type.name !== 'audio' && source.node.type.name !== 'video')\n}\n\n/** The source-holding Baustein around the asset-derived rendition at `pos`; null for anything else. */\nfunction renditionParentOf(editor: Editor, pos: number): { node: PMNode, pos: number } | null {\n const node = editor.state.doc.nodeAt(pos)\n const derivation = node && derivationOf(node)\n if (!derivation || !('asset' in derivation.from)) return null\n const $pos = editor.state.doc.resolve(pos)\n if ($pos.depth === 0 || $pos.parent.type.name !== 'composite' || !sourceOf($pos.parent, compositeKindsOf(editor))) return null\n return { node: $pos.parent, pos: $pos.before() }\n}\n\n/** Whether „Neu erzeugen (ersetzen)“ applies to the Baustein `node` at `pos`: an asset-derived rendition inside a Baustein that still has a source. */\nexport function canRegenerateRendition(editor: Editor, node: PMNode, pos: number, transcription: boolean): boolean {\n const parent = node.type.name === 'composite' ? renditionParentOf(editor, pos) : null\n return parent !== null && canCreateRendition(editor, parent.node, transcription)\n}\n\n/**\n * „Neu erzeugen (ersetzen)“: makes the rendition at `pos` again from the source of its Baustein and replaces it in place\n * (`createRendition` with `replace`; the block id stays, so references keep resolving). A rendition whose body was\n * edited since it was made is not replaced silently: a warning notice offers „Trotzdem ersetzen“, dismissing the notice\n * cancels. The rendition is found again by its id when the button is pressed.\n */\nexport function regenerateRendition(editor: Editor, pos: number, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate): void {\n const node = editor.state.doc.nodeAt(pos)\n const id = node && blockIdOf(node)\n const parent = renditionParentOf(editor, pos)\n if (!node || !id || !parent) return\n\n const replace = () => {\n const found = findBlockById(editor.state.doc, id)\n const current = found && renditionParentOf(editor, found.pos)\n if (found && current) void createRendition(editor, current.pos, ai, modelRef, notify, t, { replace: { pos: found.pos } })\n }\n const kinds = compositeKindsOf(editor)\n if (derivationState(parent.node, node, () => sha256Hex(bodyMarkdown(editor, node)), kinds).edited !== true) {\n replace()\n return\n }\n notify({\n id: `rendition-edited-${id}`,\n title: t('integrations.rendition.editedWarning.title'),\n description: t('integrations.rendition.editedWarning.description'),\n kind: 'warning',\n action: { label: t('integrations.rendition.editedWarning.confirm'), onClick: replace },\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rendition.js","names":[],"sources":["../../../src/integrations/composite-ai/rendition.ts"],"sourcesContent":["import type { Editor, JSONContent } from '@tiptap/core'\nimport { Fragment, type Node as PMNode } from '@tiptap/pm/model'\nimport type { EditorBackend } from '../../core/backend'\nimport type { ExtractResponse } from '../../core/backend-types'\nimport { capabilitiesOf, captureRange, DERIVATION_KEY, type Derivation, derivationOf, derivationState, fetchErrorMessage, parseBlocks, releaseRanges, resolveRange, sha256Hex, structureBlocks } from '../../core'\nimport type { MessageKey, Translate } from '../../i18n'\nimport type { Notifier } from '../../core/notify'\nimport { blockIdOf, findBlockById, newBlockId } from '../../core/schema'\nimport { isRateLimited, type ModelRef, notifyAiRateLimited } from '../../features/ai'\nimport {\n acceptsChild,\n bibOf,\n blockTypeOf,\n blockTypesOf,\n bodyMarkdown,\n COMPOSITE_CHROME_COUNT,\n compositeJSON,\n sourceOf,\n} from '../../features/composites/schema'\n\nexport interface RenditionOptions {\n /**\n * Replace the rendition at this position (a child of the source-holding Typed block) in place instead of adding a new one\n * after the source: its chrome (title, description, tags) and block id stay, body and derivation are new.\n */\n replace?: { pos: number }\n}\n\n/** Methods with their own title of a fresh rendition (`integrations.rendition.title.<method>`). */\nconst TITLED_METHODS: readonly string[] = ['transcription', 'pdf-text', 'ocr', 'description']\n\n/** What the server's `warnings` codes say; an unknown code is shown as it is. */\nconst WARNING_KEYS: Readonly<Record<string, MessageKey>> = {\n 'ocr-unavailable': 'integrations.rendition.warning.ocr-unavailable',\n 'crossref-unavailable': 'integrations.rendition.warning.crossref-unavailable',\n 'pages-truncated': 'integrations.rendition.warning.pages-truncated',\n 'ocr-truncated': 'integrations.rendition.warning.ocr-truncated',\n 'bibliography-failed': 'integrations.rendition.warning.bibliography-failed',\n}\n\n/** The `<!-- page N -->` line the server puts at the start of every PDF page after the first (`joinPages`). */\nconst PAGE_MARKER = /^<!-- page (\\d+) -->[ \\t]*$/gm\n\n/**\n * `markdown` with every page marker line replaced by an emphasised page label paragraph (`*S. 2*`): parsed as it is, a\n * marker is an HTML comment and would leave an empty paragraph without the page. The stored raw output keeps the markers.\n */\nfunction withPageLabels(markdown: string, t: Translate): string {\n return markdown.replace(PAGE_MARKER, (_marker, page: string) => `*${t('integrations.rendition.page', { n: page })}*`)\n}\n\n/**\n * The body blocks of a rendition: one `transcriptSegment` per timed segment (speaker empty, `start`/`end` in seconds)\n * when the editor has the transcript feature, else the Markdown parsed and brought into the preferred structure\n * (chapters become nested `section` Typed blocks in composite structure; PDF page breaks become page label paragraphs).\n */\nfunction renditionBody(editor: Editor, response: ExtractResponse, t: Translate): JSONContent[] {\n if (editor.schema.nodes.transcriptSegment && response.segments?.length) {\n const segments = response.segments\n .filter(segment => segment.text.trim())\n .map((segment): JSONContent => ({\n type: 'transcriptSegment',\n attrs: { speaker: '', start: segment.start, end: segment.end },\n content: [{ type: 'text', text: segment.text.trim() }],\n }))\n if (segments.length) return segments\n }\n return response.markdown.trim() ? structureBlocks(editor, parseBlocks(editor, withPageLabels(response.markdown, t)).blocks) : []\n}\n\n/** `node` with `derivation` in its meta, `outputHash` being the hash of the Markdown of its body (what `derivationState` compares later). */\nfunction sealed(editor: Editor, node: PMNode, derivation: Derivation): PMNode {\n const outputHash = sha256Hex(bodyMarkdown(editor, node))\n return node.type.create({ ...node.attrs, meta: { ...node.attrs.meta, [DERIVATION_KEY]: { ...derivation, outputHash } } }, node.content, node.marks)\n}\n\n/**\n * the „Create readable rendition“ action: extracts the readable content of the source of the Typed block at `pos` (`ai.extract`, method\n * `auto`: transcript with timestamps for audio/video, full text or OCR for a PDF, a description for an image) and adds\n * it as a `rendition` Typed block right after the source block, carrying `meta.derivation` (asset, method, model, hashes,\n * the stored raw output). Bibliographic data the server found fills an empty `meta.bib` of the Typed block, and the work's\n * title an empty Typed block title. With `options.replace` the existing rendition is replaced in place (same block id).\n * The Typed block is anchored through its description when the request starts and found again after it ends, so edits\n * while it runs do not misplace the result. Notices go through `notify`, in the language of `t`; the progress notice\n * stays until the request ends.\n */\nexport async function createRendition(editor: Editor, pos: number, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate, options: RenditionOptions = {}): Promise<void> {\n const kinds = blockTypesOf(editor)\n const composite = editor.state.doc.nodeAt(pos)\n const source = composite && sourceOf(composite, kinds)\n if (composite?.type.name !== 'typedBlock' || !source) return\n const asset = source.asset\n if (!asset) {\n notify({ title: t('integrations.rendition.noSource'), kind: 'error' })\n return\n }\n if (!acceptsChild(kinds, String(composite.attrs.blockType), 'rendition')) {\n notify({ title: t('integrations.rendition.notAccepted'), kind: 'error' })\n return\n }\n\n let replaceId: string | null = null\n if (options.replace) {\n const target = editor.state.doc.nodeAt(options.replace.pos)\n replaceId = target && options.replace.pos > pos && options.replace.pos < pos + composite.nodeSize ? blockIdOf(target) : null\n if (target?.type.name !== 'typedBlock' || !replaceId) return\n }\n\n const descriptionPos = pos + 1 + composite.child(0).nodeSize\n const anchor = captureRange(editor, { from: descriptionPos + 1, to: descriptionPos + 1 + composite.child(1).content.size })\n const toastId = `rendition-${blockIdOf(composite) ?? asset.name}`\n const wantsBibliography = capabilitiesOf(editor, composite).has('bibliography')\n const model = modelRef('generate')\n\n const controller = new AbortController()\n const abort = () => controller.abort()\n editor.on('destroy', abort)\n notify({ id: toastId, title: t('integrations.rendition.running'), kind: 'info', icon: 'i-lucide-loader-circle', duration: 0 })\n try {\n const response = await ai.extract({ documentId: asset.documentId, name: asset.name, method: 'auto', model, bibliography: wantsBibliography }, controller.signal)\n if (editor.isDestroyed) return\n const body = renditionBody(editor, response, t)\n if (!body.length) {\n notify({ title: t('integrations.rendition.empty.title'), description: t('integrations.rendition.empty.description'), kind: 'error' })\n return\n }\n\n // The Typed block, found again through its anchored description; its source may have changed meanwhile.\n const range = resolveRange(editor.state, anchor)\n const $from = range && editor.state.doc.resolve(range.from)\n const parentPos = $from && $from.parent.type.name === 'typedBlockDescription' ? $from.before($from.depth - 1) : null\n const parent = parentPos === null ? null : editor.state.doc.nodeAt(parentPos)\n const currentSource = parent && sourceOf(parent, kinds)\n const replaced = replaceId ? findBlockById(editor.state.doc, replaceId) : null\n if (parentPos === null || !parent || !currentSource || (replaceId !== null && (!replaced || replaced.pos <= parentPos || replaced.pos >= parentPos + parent.nodeSize))) {\n notify({ title: t('integrations.rendition.gone.title'), description: t('integrations.rendition.gone.description'), kind: 'error' })\n return\n }\n\n // The model that wrote the content: the speech-to-text model for a transcription, the chat model for OCR and an\n // image description, none for the PDF text layer.\n const usedModel = response.method === 'transcription' ? response.model : response.method === 'ocr' || response.method === 'description' ? ('preset' in model ? model.preset : model.model) : undefined\n const derivation: Derivation = {\n from: { asset: asset.name },\n method: response.method,\n ...(usedModel ? { model: usedModel } : {}),\n createdAt: new Date().toISOString(),\n inputHash: response.inputHash,\n outputHash: '',\n ...(response.output ? { output: response.output } : {}),\n }\n const { schema } = editor\n const bodyNodes = body.map(block => schema.nodeFromJSON(block))\n const id = replaceId ?? newBlockId()\n let node: PMNode\n if (replaced) {\n const chrome = Array.from({ length: COMPOSITE_CHROME_COUNT }, (_, index) => replaced.node.child(index))\n node = sealed(editor, replaced.node.type.create(replaced.node.attrs, Fragment.from([...chrome, ...bodyNodes])), derivation)\n }\n else {\n const titleKey = TITLED_METHODS.includes(response.method) ? `integrations.rendition.title.${response.method}` as MessageKey : null\n const json = compositeJSON('rendition', body)\n const [title, ...rest] = json.content!\n const text = titleKey ? t(titleKey) : blockTypeOf(kinds, 'rendition').label\n const fresh = schema.nodeFromJSON({ ...json, attrs: { ...json.attrs, id }, content: [{ ...title!, content: [{ type: 'text', text }] }, ...rest] })\n node = sealed(editor, fresh, derivation)\n }\n\n const tr = editor.state.tr\n if (replaced) tr.replaceWith(replaced.pos, replaced.pos + replaced.node.nodeSize, node)\n else {\n let insertPos = parentPos + 1\n for (let index = 0; index <= currentSource.index; index++) insertPos += parent.child(index).nodeSize\n if (!parent.canReplace(currentSource.index + 1, currentSource.index + 1, Fragment.from(node))) {\n notify({ title: t('integrations.rendition.notAccepted'), kind: 'error' })\n return\n }\n tr.insert(insertPos, node)\n }\n // Bibliographic data fills only what is empty; the title is never part of `meta.bib`.\n const { bib } = response\n const workTitle = bib?.title?.trim()\n if (workTitle && !parent.child(0).textContent.trim()) tr.insert(parentPos + 2, schema.text(workTitle))\n if (bib && bibOf(parent) === null) {\n const csl = { ...bib }\n delete csl.title\n tr.setNodeMarkup(parentPos, undefined, { ...parent.attrs, meta: { ...parent.attrs.meta, bib: csl } })\n }\n editor.view.dispatch(tr)\n editor.commands.revealBlock(id)\n notify({ title: t('integrations.rendition.done'), kind: 'success', icon: 'i-lucide-captions' })\n if (response.warnings.length) {\n notify({\n title: t('integrations.rendition.warnings.title'),\n description: response.warnings.map(code => (WARNING_KEYS[code] ? t(WARNING_KEYS[code]) : code)).join(' '),\n kind: 'info',\n })\n }\n }\n catch (err) {\n if (controller.signal.aborted || editor.isDestroyed) return\n if (isRateLimited(err)) notifyAiRateLimited(notify, t)\n else notify({ title: t('integrations.rendition.failed'), description: fetchErrorMessage(err), kind: 'error' })\n }\n finally {\n notify.dismiss?.(toastId)\n editor.off('destroy', abort)\n releaseRanges(editor, [anchor])\n }\n}\n\n/**\n * Whether the „Create readable rendition“ action applies to `node`: a Typed block of a source-holding Block type whose source block was\n * found. A recording needs speech-to-text (`transcription`: the app's public configuration enables it); everything else\n * is extracted by the server.\n */\nexport function canCreateRendition(editor: Editor, node: PMNode, transcription: boolean): boolean {\n const source = sourceOf(node, blockTypesOf(editor))\n if (!source) return false\n return transcription || (source.node.type.name !== 'audio' && source.node.type.name !== 'video')\n}\n\n/** The source-holding Typed block around the asset-derived rendition at `pos`; null for anything else. */\nfunction renditionParentOf(editor: Editor, pos: number): { node: PMNode, pos: number } | null {\n const node = editor.state.doc.nodeAt(pos)\n const derivation = node && derivationOf(node)\n if (!derivation || !('asset' in derivation.from)) return null\n const $pos = editor.state.doc.resolve(pos)\n if ($pos.depth === 0 || $pos.parent.type.name !== 'typedBlock' || !sourceOf($pos.parent, blockTypesOf(editor))) return null\n return { node: $pos.parent, pos: $pos.before() }\n}\n\n/** Whether „Neu erzeugen (ersetzen)“ applies to the Typed block `node` at `pos`: an asset-derived rendition inside a Typed block that still has a source. */\nexport function canRegenerateRendition(editor: Editor, node: PMNode, pos: number, transcription: boolean): boolean {\n const parent = renditionParentOf(editor, pos)\n return parent !== null && canCreateRendition(editor, parent.node, transcription)\n}\n\n/**\n * „Neu erzeugen (ersetzen)“: makes the rendition at `pos` again from the source of its Typed block and replaces it in place\n * (`createRendition` with `replace`; the block id stays, so references keep resolving). A rendition whose body was\n * edited since it was made is not replaced silently: a warning notice offers „Trotzdem ersetzen“, dismissing the notice\n * cancels. The rendition is found again by its id when the button is pressed.\n */\nexport function regenerateRendition(editor: Editor, pos: number, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate): void {\n const node = editor.state.doc.nodeAt(pos)\n const id = node && blockIdOf(node)\n const parent = renditionParentOf(editor, pos)\n if (!node || !id || !parent) return\n\n const replace = () => {\n const found = findBlockById(editor.state.doc, id)\n const current = found && renditionParentOf(editor, found.pos)\n if (found && current) void createRendition(editor, current.pos, ai, modelRef, notify, t, { replace: { pos: found.pos } })\n }\n if (derivationState(editor.state.doc, pos, () => sha256Hex(bodyMarkdown(editor, node)))?.edited !== true) {\n replace()\n return\n }\n notify({\n id: `rendition-edited-${id}`,\n title: t('integrations.rendition.editedWarning.title'),\n description: t('integrations.rendition.editedWarning.description'),\n kind: 'warning',\n action: { label: t('integrations.rendition.editedWarning.confirm'), onClick: replace },\n })\n}\n"],"mappings":"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"}
|
|
@@ -18,27 +18,27 @@ export declare function bodyUnchanged(original: string, result: string): boolean
|
|
|
18
18
|
* inserted) is split at those headings, or `null` when it yields fewer than two parts. Every part gets the model's
|
|
19
19
|
* heading as a hidden title (`meta.titleHidden`), no block id.
|
|
20
20
|
*
|
|
21
|
-
* - `children`: one node, the composite with the blocks before the first heading followed by one
|
|
21
|
+
* - `children`: one node, the composite with the blocks before the first heading followed by one nested Typed block of
|
|
22
22
|
* `kind` per heading.
|
|
23
23
|
* - `siblings`: the composite keeps the blocks before the first heading (without any, the body of the first heading
|
|
24
|
-
* group, its heading is dropped); every further heading group becomes a following sibling of the composite's own
|
|
24
|
+
* group, its heading is dropped); every further heading group becomes a following sibling of the composite's own Block type.
|
|
25
25
|
*
|
|
26
26
|
* The headings of the result are level 2, so only the lowest level present is grouped (`groupHeadingsIntoComposites`,
|
|
27
27
|
* not recursive).
|
|
28
28
|
*/
|
|
29
29
|
export declare function splitByInsertedHeadings(schema: Schema, composite: Node$1, blocks: readonly Node$1[], mode: SplitMode, kind: string): Node$1[] | null;
|
|
30
30
|
/**
|
|
31
|
-
* „Mit KI teilen“: asks the model to insert `##` headings into the body of the
|
|
32
|
-
* `targetChars` as the wanted section size, `generate`
|
|
33
|
-
* `children` makes
|
|
31
|
+
* „Mit KI teilen“: asks the model to insert `##` headings into the body of the Typed block at `pos` (task `split-block`,
|
|
32
|
+
* `targetChars` as the wanted section size, `generate` Model role) and splits the body at them, in `mode`:
|
|
33
|
+
* `children` makes nested Typed blocks, `siblings` makes following Typed blocks of the same Block type. The model may only insert
|
|
34
34
|
* headings: when the text minus those headings is not the original body (`bodyUnchanged`) nothing is written and a
|
|
35
|
-
* warning is shown. The
|
|
36
|
-
* The write is one transaction. `
|
|
35
|
+
* warning is shown. The Typed block is anchored while the model runs; edits to its body meanwhile discard the result.
|
|
36
|
+
* The write is one transaction. `defaultBlockType` is the setting `blocks.defaultBlockType` (see `childKindFor`). Notices go
|
|
37
37
|
* through `notify`, in the language of `t`.
|
|
38
38
|
*/
|
|
39
39
|
export declare function splitCompositeWithAi(editor: Editor, pos: number, mode: SplitMode, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate, options: {
|
|
40
40
|
targetChars: number;
|
|
41
|
-
|
|
41
|
+
defaultBlockType: string;
|
|
42
42
|
}): Promise<void>;
|
|
43
43
|
//#endregion
|
|
44
44
|
//# sourceMappingURL=split.d.ts.map
|
|
@@ -2,9 +2,9 @@ import { AI_LIMITS } from "../../features/ai/protocol/tasks.js";
|
|
|
2
2
|
import { parseBlocks } from "../../core/pipeline.js";
|
|
3
3
|
import { captureRange, releaseRanges, resolveRange } from "../../core/doc-anchors.js";
|
|
4
4
|
import { TITLE_HIDDEN_KEY } from "../../features/composites/meta-keys.js";
|
|
5
|
-
import { bodyMarkdown } from "../../features/composites/derivation.js";
|
|
6
5
|
import { groupHeadingsIntoComposites } from "../../features/composites/heading-composites.js";
|
|
7
|
-
import {
|
|
6
|
+
import { blockTypesOf } from "../../features/composites/nodes.js";
|
|
7
|
+
import { bodyMarkdown } from "../../features/composites/source.js";
|
|
8
8
|
import { notifyAiRateLimited } from "../../features/ai/rate-limit.js";
|
|
9
9
|
import { postAiSse } from "../../features/ai/ai-sse.js";
|
|
10
10
|
import { childKindFor } from "../../features/composites/split.js";
|
|
@@ -39,10 +39,10 @@ function bodyUnchanged(original, result) {
|
|
|
39
39
|
* inserted) is split at those headings, or `null` when it yields fewer than two parts. Every part gets the model's
|
|
40
40
|
* heading as a hidden title (`meta.titleHidden`), no block id.
|
|
41
41
|
*
|
|
42
|
-
* - `children`: one node, the composite with the blocks before the first heading followed by one
|
|
42
|
+
* - `children`: one node, the composite with the blocks before the first heading followed by one nested Typed block of
|
|
43
43
|
* `kind` per heading.
|
|
44
44
|
* - `siblings`: the composite keeps the blocks before the first heading (without any, the body of the first heading
|
|
45
|
-
* group, its heading is dropped); every further heading group becomes a following sibling of the composite's own
|
|
45
|
+
* group, its heading is dropped); every further heading group becomes a following sibling of the composite's own Block type.
|
|
46
46
|
*
|
|
47
47
|
* The headings of the result are level 2, so only the lowest level present is grouped (`groupHeadingsIntoComposites`,
|
|
48
48
|
* not recursive).
|
|
@@ -78,20 +78,20 @@ function splitByInsertedHeadings(schema, composite, blocks, mode, kind) {
|
|
|
78
78
|
return [withBody(firstBody), ...rest];
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
|
-
* „Mit KI teilen“: asks the model to insert `##` headings into the body of the
|
|
82
|
-
* `targetChars` as the wanted section size, `generate`
|
|
83
|
-
* `children` makes
|
|
81
|
+
* „Mit KI teilen“: asks the model to insert `##` headings into the body of the Typed block at `pos` (task `split-block`,
|
|
82
|
+
* `targetChars` as the wanted section size, `generate` Model role) and splits the body at them, in `mode`:
|
|
83
|
+
* `children` makes nested Typed blocks, `siblings` makes following Typed blocks of the same Block type. The model may only insert
|
|
84
84
|
* headings: when the text minus those headings is not the original body (`bodyUnchanged`) nothing is written and a
|
|
85
|
-
* warning is shown. The
|
|
86
|
-
* The write is one transaction. `
|
|
85
|
+
* warning is shown. The Typed block is anchored while the model runs; edits to its body meanwhile discard the result.
|
|
86
|
+
* The write is one transaction. `defaultBlockType` is the setting `blocks.defaultBlockType` (see `childKindFor`). Notices go
|
|
87
87
|
* through `notify`, in the language of `t`.
|
|
88
88
|
*/
|
|
89
89
|
async function splitCompositeWithAi(editor, pos, mode, ai, modelRef, notify, t, options) {
|
|
90
90
|
const composite = editor.state.doc.nodeAt(pos);
|
|
91
|
-
if (composite?.type.name !== "
|
|
91
|
+
if (composite?.type.name !== "typedBlock") return;
|
|
92
92
|
const original = bodyMarkdown(editor, composite);
|
|
93
93
|
if (!original.trim()) return;
|
|
94
|
-
const kind = mode === "children" ? childKindFor(
|
|
94
|
+
const kind = mode === "children" ? childKindFor(blockTypesOf(editor), String(composite.attrs.blockType), options.defaultBlockType) : String(composite.attrs.blockType);
|
|
95
95
|
if (!kind) {
|
|
96
96
|
notify({
|
|
97
97
|
title: t("integrations.compositeSplit.notPossible"),
|
|
@@ -158,7 +158,7 @@ async function splitCompositeWithAi(editor, pos, mode, ai, modelRef, notify, t,
|
|
|
158
158
|
}
|
|
159
159
|
const range = resolveRange(editor.state, anchor);
|
|
160
160
|
const current = range && editor.state.doc.nodeAt(range.from);
|
|
161
|
-
if (!range || current?.type.name !== "
|
|
161
|
+
if (!range || current?.type.name !== "typedBlock" || range.from + current.nodeSize !== range.to || bodyMarkdown(editor, current) !== original) {
|
|
162
162
|
notify({
|
|
163
163
|
title: t("integrations.compositeSplit.gone"),
|
|
164
164
|
kind: "error"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split.js","names":[],"sources":["../../../src/integrations/composite-ai/split.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Fragment, type Node as PMNode, type Schema } from '@tiptap/pm/model'\nimport type { EditorBackend } from '../../core/backend'\nimport { captureRange, parseBlocks, releaseRanges, resolveRange } from '../../core'\nimport type { Notifier } from '../../core/notify'\nimport type { Translate } from '../../i18n'\nimport { AI_LIMITS, type ModelRef, notifyAiRateLimited, postAiSse } from '../../features/ai'\nimport { bodyMarkdown, COMPOSITE_CHROME_COUNT, compositeKindsOf, groupHeadingsIntoComposites, TITLE_HIDDEN_KEY } from '../../features/composites/schema'\nimport { childKindFor, type SplitMode } from '../../features/composites/split'\n\nconst HEADING_LINE = /^#{1,6}\\s/\nconst collapse = (text: string) => text.replace(/\\s+/g, ' ').trim()\n\n/**\n * Whether the model only inserted headings: `result` without its heading lines equals `original` with whitespace\n * collapsed (so line breaks and blank lines may differ, words may not). Heading lines the original already has stay\n * part of the comparison, so the model cannot drop or rewrite them.\n */\nexport function bodyUnchanged(original: string, result: string): boolean {\n const own = new Map<string, number>()\n for (const line of original.split('\\n')) {\n if (!HEADING_LINE.test(line)) continue\n const key = collapse(line)\n own.set(key, (own.get(key) ?? 0) + 1)\n }\n const kept = result.split('\\n').filter((line) => {\n if (!HEADING_LINE.test(line)) return true\n const key = collapse(line)\n const left = own.get(key) ?? 0\n if (!left) return false\n own.set(key, left - 1)\n return true\n })\n return collapse(kept.join('\\n')) === collapse(original)\n}\n\n/**\n * The nodes that replace `composite` when the model's result (`blocks`, the result Markdown as blocks, with headings\n * inserted) is split at those headings, or `null` when it yields fewer than two parts. Every part gets the model's\n * heading as a hidden title (`meta.titleHidden`), no block id.\n *\n * - `children`: one node, the composite with the blocks before the first heading followed by one sub-Baustein of\n * `kind` per heading.\n * - `siblings`: the composite keeps the blocks before the first heading (without any, the body of the first heading\n * group, its heading is dropped); every further heading group becomes a following sibling of the composite's own kind.\n *\n * The headings of the result are level 2, so only the lowest level present is grouped (`groupHeadingsIntoComposites`,\n * not recursive).\n */\nexport function splitByInsertedHeadings(schema: Schema, composite: PMNode, blocks: readonly PMNode[], mode: SplitMode, kind: string): PMNode[] | null {\n let minLevel = Infinity\n for (const block of blocks) if (block.type.name === 'heading') minLevel = Math.min(minLevel, block.attrs.level as number)\n if (minLevel === Infinity) return null\n const preambleLength = blocks.findIndex(block => block.type.name === 'heading' && (block.attrs.level as number) === minLevel)\n const grouped = groupHeadingsIntoComposites(schema, blocks, false, kind)\n if (!grouped) return null\n const preamble = grouped.slice(0, preambleLength)\n const parts = grouped.slice(preambleLength).map(part => part.type.create({ ...part.attrs, meta: { ...(part.attrs.meta as Record<string, unknown> | null), [TITLE_HIDDEN_KEY]: true } }, part.content))\n if ((preamble.length ? 1 : 0) + parts.length < 2) return null\n\n const chrome: PMNode[] = []\n composite.forEach((child, _offset, index) => {\n if (index < COMPOSITE_CHROME_COUNT) chrome.push(child)\n })\n const withBody = (body: readonly PMNode[]) => composite.copy(Fragment.from([...chrome, ...body]))\n if (mode === 'children') return [withBody([...preamble, ...parts])]\n\n if (preamble.length) return [withBody(preamble), ...parts]\n const [first, ...rest] = parts\n const firstBody: PMNode[] = []\n first!.forEach((child, _offset, index) => {\n if (index >= COMPOSITE_CHROME_COUNT) firstBody.push(child)\n })\n return [withBody(firstBody), ...rest]\n}\n\n/**\n * „Mit KI teilen“: asks the model to insert `##` headings into the body of the Baustein at `pos` (task `split-block`,\n * `targetChars` as the wanted section size, `generate` slot) and splits the body at them, in `mode`:\n * `children` makes sub-Bausteine, `siblings` makes following Bausteine of the same kind. The model may only insert\n * headings: when the text minus those headings is not the original body (`bodyUnchanged`) nothing is written and a\n * warning is shown. The Baustein is anchored while the model runs; edits to its body meanwhile discard the result.\n * The write is one transaction. `defaultKind` is the setting `blocks.defaultKind` (see `childKindFor`). Notices go\n * through `notify`, in the language of `t`.\n */\nexport async function splitCompositeWithAi(editor: Editor, pos: number, mode: SplitMode, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate, options: { targetChars: number, defaultKind: string }): Promise<void> {\n const composite = editor.state.doc.nodeAt(pos)\n if (composite?.type.name !== 'composite') return\n const original = bodyMarkdown(editor, composite)\n if (!original.trim()) return\n const kind = mode === 'children' ? childKindFor(compositeKindsOf(editor), String(composite.attrs.kind), options.defaultKind) : String(composite.attrs.kind)\n if (!kind) {\n notify({ title: t('integrations.compositeSplit.notPossible'), kind: 'error' })\n return\n }\n if (original.length > AI_LIMITS.selection) {\n notify({ title: t('integrations.compositeSplit.tooLong'), kind: 'error' })\n return\n }\n\n const anchor = captureRange(editor, { from: pos, to: pos + composite.nodeSize }, { startInside: true })\n const toastId = `split-${anchor.id}`\n const controller = new AbortController()\n const abort = () => controller.abort()\n editor.on('destroy', abort)\n notify({ id: toastId, title: t('integrations.compositeSplit.running'), kind: 'info', icon: 'i-lucide-loader-circle', duration: 0 })\n let output = ''\n try {\n const outcome = await postAiSse(ai, 'generate', {\n task: 'split-block',\n prompt: String(options.targetChars),\n selection: original,\n context: '',\n model: modelRef('generate'),\n }, { signal: controller.signal, t, onDelta: (text) => { output += text } })\n\n if (outcome.status === 'aborted' || editor.isDestroyed) return\n if (outcome.status === 'rate-limited') {\n notifyAiRateLimited(notify, t)\n return\n }\n if (outcome.status === 'error') {\n notify({ title: t('integrations.compositeSplit.failed'), description: outcome.message, kind: 'error' })\n return\n }\n if (!bodyUnchanged(original, output)) {\n notify({ title: t('integrations.compositeSplit.changed'), kind: 'warning' })\n return\n }\n\n const range = resolveRange(editor.state, anchor)\n const current = range && editor.state.doc.nodeAt(range.from)\n if (!range || current?.type.name !== 'composite' || range.from + current.nodeSize !== range.to || bodyMarkdown(editor, current) !== original) {\n notify({ title: t('integrations.compositeSplit.gone'), kind: 'error' })\n return\n }\n const { schema } = editor\n const replacement = splitByInsertedHeadings(schema, current, parseBlocks(editor, output).blocks.map(block => schema.nodeFromJSON(block)), mode, kind)\n if (!replacement) {\n notify({ title: t('integrations.compositeSplit.notPossible'), kind: 'error' })\n return\n }\n editor.view.dispatch(editor.state.tr.replaceWith(range.from, range.to, replacement))\n }\n finally {\n notify.dismiss?.(toastId)\n editor.off('destroy', abort)\n releaseRanges(editor, [anchor])\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"split.js","names":[],"sources":["../../../src/integrations/composite-ai/split.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Fragment, type Node as PMNode, type Schema } from '@tiptap/pm/model'\nimport type { EditorBackend } from '../../core/backend'\nimport { captureRange, parseBlocks, releaseRanges, resolveRange } from '../../core'\nimport type { Notifier } from '../../core/notify'\nimport type { Translate } from '../../i18n'\nimport { AI_LIMITS, type ModelRef, notifyAiRateLimited, postAiSse } from '../../features/ai'\nimport { blockTypesOf, bodyMarkdown, COMPOSITE_CHROME_COUNT, groupHeadingsIntoComposites, TITLE_HIDDEN_KEY } from '../../features/composites/schema'\nimport { childKindFor, type SplitMode } from '../../features/composites/split'\n\nconst HEADING_LINE = /^#{1,6}\\s/\nconst collapse = (text: string) => text.replace(/\\s+/g, ' ').trim()\n\n/**\n * Whether the model only inserted headings: `result` without its heading lines equals `original` with whitespace\n * collapsed (so line breaks and blank lines may differ, words may not). Heading lines the original already has stay\n * part of the comparison, so the model cannot drop or rewrite them.\n */\nexport function bodyUnchanged(original: string, result: string): boolean {\n const own = new Map<string, number>()\n for (const line of original.split('\\n')) {\n if (!HEADING_LINE.test(line)) continue\n const key = collapse(line)\n own.set(key, (own.get(key) ?? 0) + 1)\n }\n const kept = result.split('\\n').filter((line) => {\n if (!HEADING_LINE.test(line)) return true\n const key = collapse(line)\n const left = own.get(key) ?? 0\n if (!left) return false\n own.set(key, left - 1)\n return true\n })\n return collapse(kept.join('\\n')) === collapse(original)\n}\n\n/**\n * The nodes that replace `composite` when the model's result (`blocks`, the result Markdown as blocks, with headings\n * inserted) is split at those headings, or `null` when it yields fewer than two parts. Every part gets the model's\n * heading as a hidden title (`meta.titleHidden`), no block id.\n *\n * - `children`: one node, the composite with the blocks before the first heading followed by one nested Typed block of\n * `kind` per heading.\n * - `siblings`: the composite keeps the blocks before the first heading (without any, the body of the first heading\n * group, its heading is dropped); every further heading group becomes a following sibling of the composite's own Block type.\n *\n * The headings of the result are level 2, so only the lowest level present is grouped (`groupHeadingsIntoComposites`,\n * not recursive).\n */\nexport function splitByInsertedHeadings(schema: Schema, composite: PMNode, blocks: readonly PMNode[], mode: SplitMode, kind: string): PMNode[] | null {\n let minLevel = Infinity\n for (const block of blocks) if (block.type.name === 'heading') minLevel = Math.min(minLevel, block.attrs.level as number)\n if (minLevel === Infinity) return null\n const preambleLength = blocks.findIndex(block => block.type.name === 'heading' && (block.attrs.level as number) === minLevel)\n const grouped = groupHeadingsIntoComposites(schema, blocks, false, kind)\n if (!grouped) return null\n const preamble = grouped.slice(0, preambleLength)\n const parts = grouped.slice(preambleLength).map(part => part.type.create({ ...part.attrs, meta: { ...(part.attrs.meta as Record<string, unknown> | null), [TITLE_HIDDEN_KEY]: true } }, part.content))\n if ((preamble.length ? 1 : 0) + parts.length < 2) return null\n\n const chrome: PMNode[] = []\n composite.forEach((child, _offset, index) => {\n if (index < COMPOSITE_CHROME_COUNT) chrome.push(child)\n })\n const withBody = (body: readonly PMNode[]) => composite.copy(Fragment.from([...chrome, ...body]))\n if (mode === 'children') return [withBody([...preamble, ...parts])]\n\n if (preamble.length) return [withBody(preamble), ...parts]\n const [first, ...rest] = parts\n const firstBody: PMNode[] = []\n first!.forEach((child, _offset, index) => {\n if (index >= COMPOSITE_CHROME_COUNT) firstBody.push(child)\n })\n return [withBody(firstBody), ...rest]\n}\n\n/**\n * „Mit KI teilen“: asks the model to insert `##` headings into the body of the Typed block at `pos` (task `split-block`,\n * `targetChars` as the wanted section size, `generate` Model role) and splits the body at them, in `mode`:\n * `children` makes nested Typed blocks, `siblings` makes following Typed blocks of the same Block type. The model may only insert\n * headings: when the text minus those headings is not the original body (`bodyUnchanged`) nothing is written and a\n * warning is shown. The Typed block is anchored while the model runs; edits to its body meanwhile discard the result.\n * The write is one transaction. `defaultBlockType` is the setting `blocks.defaultBlockType` (see `childKindFor`). Notices go\n * through `notify`, in the language of `t`.\n */\nexport async function splitCompositeWithAi(editor: Editor, pos: number, mode: SplitMode, ai: NonNullable<EditorBackend['ai']>, modelRef: (slot: 'generate') => ModelRef, notify: Notifier, t: Translate, options: { targetChars: number, defaultBlockType: string }): Promise<void> {\n const composite = editor.state.doc.nodeAt(pos)\n if (composite?.type.name !== 'typedBlock') return\n const original = bodyMarkdown(editor, composite)\n if (!original.trim()) return\n const kind = mode === 'children' ? childKindFor(blockTypesOf(editor), String(composite.attrs.blockType), options.defaultBlockType) : String(composite.attrs.blockType)\n if (!kind) {\n notify({ title: t('integrations.compositeSplit.notPossible'), kind: 'error' })\n return\n }\n if (original.length > AI_LIMITS.selection) {\n notify({ title: t('integrations.compositeSplit.tooLong'), kind: 'error' })\n return\n }\n\n const anchor = captureRange(editor, { from: pos, to: pos + composite.nodeSize }, { startInside: true })\n const toastId = `split-${anchor.id}`\n const controller = new AbortController()\n const abort = () => controller.abort()\n editor.on('destroy', abort)\n notify({ id: toastId, title: t('integrations.compositeSplit.running'), kind: 'info', icon: 'i-lucide-loader-circle', duration: 0 })\n let output = ''\n try {\n const outcome = await postAiSse(ai, 'generate', {\n task: 'split-block',\n prompt: String(options.targetChars),\n selection: original,\n context: '',\n model: modelRef('generate'),\n }, { signal: controller.signal, t, onDelta: (text) => { output += text } })\n\n if (outcome.status === 'aborted' || editor.isDestroyed) return\n if (outcome.status === 'rate-limited') {\n notifyAiRateLimited(notify, t)\n return\n }\n if (outcome.status === 'error') {\n notify({ title: t('integrations.compositeSplit.failed'), description: outcome.message, kind: 'error' })\n return\n }\n if (!bodyUnchanged(original, output)) {\n notify({ title: t('integrations.compositeSplit.changed'), kind: 'warning' })\n return\n }\n\n const range = resolveRange(editor.state, anchor)\n const current = range && editor.state.doc.nodeAt(range.from)\n if (!range || current?.type.name !== 'typedBlock' || range.from + current.nodeSize !== range.to || bodyMarkdown(editor, current) !== original) {\n notify({ title: t('integrations.compositeSplit.gone'), kind: 'error' })\n return\n }\n const { schema } = editor\n const replacement = splitByInsertedHeadings(schema, current, parseBlocks(editor, output).blocks.map(block => schema.nodeFromJSON(block)), mode, kind)\n if (!replacement) {\n notify({ title: t('integrations.compositeSplit.notPossible'), kind: 'error' })\n return\n }\n editor.view.dispatch(editor.state.tr.replaceWith(range.from, range.to, replacement))\n }\n finally {\n notify.dismiss?.(toastId)\n editor.off('destroy', abort)\n releaseRanges(editor, [anchor])\n }\n}\n"],"mappings":"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"}
|
|
@@ -18,9 +18,9 @@ export declare function transferRequest(payload: BlockTransfer, blocks: readonly
|
|
|
18
18
|
blocks: readonly AiBlock[];
|
|
19
19
|
model: ModelRef;
|
|
20
20
|
};
|
|
21
|
-
/** „Zusammenfassung einfügen“: a new
|
|
21
|
+
/** „Zusammenfassung einfügen“: a new Typed block with a model-written summary of the dragged block (needs an AI backend). */
|
|
22
22
|
export declare const summaryTransform: BlockTransform;
|
|
23
|
-
/** „Stichpunkte einfügen“: a new
|
|
23
|
+
/** „Stichpunkte einfügen“: a new Typed block with the dragged block's key statements as a list (needs an AI backend). */
|
|
24
24
|
export declare const bulletsTransform: BlockTransform;
|
|
25
25
|
//#endregion
|
|
26
26
|
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { de } from "../../i18n/de.js";
|
|
2
2
|
import { AI_LIMITS } from "../../features/ai/protocol/tasks.js";
|
|
3
|
-
import { aiContextOf } from "../../core/pipeline.js";
|
|
4
3
|
import { newBlockId } from "../../core/schema/block-ids.js";
|
|
5
|
-
import "../../
|
|
4
|
+
import { aiContextOf } from "../../core/pipeline.js";
|
|
5
|
+
import "../../core/derivation.js";
|
|
6
6
|
import { compositeJSON } from "../../features/composites/nodes.js";
|
|
7
7
|
import { transferKindOf, transferSourceHash } from "../../features/transfer/transforms.js";
|
|
8
8
|
import { modelProfiles } from "../../features/ai/model-profiles.js";
|
|
@@ -21,17 +21,17 @@ function transferRequest(payload, blocks, model, task) {
|
|
|
21
21
|
model
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
/** The `Creates` of the
|
|
24
|
+
/** The `Creates` of the Typed block the AI transforms insert (`transferKindOf`). */
|
|
25
25
|
const compositeCreates = (ctx) => ({
|
|
26
|
-
type: "
|
|
27
|
-
attrs: {
|
|
26
|
+
type: "typedBlock",
|
|
27
|
+
attrs: { blockType: transferKindOf(ctx.editor) }
|
|
28
28
|
});
|
|
29
29
|
/** Whether the AI transforms can run for this drop: an AI backend, the source's Markdown and the composite node in the schema. */
|
|
30
|
-
const aiAvailable = (ctx) => !!ctx.ai && ctx.payload.markdown.trim().length > 0 && ctx.editor.schema.nodes.
|
|
30
|
+
const aiAvailable = (ctx) => !!ctx.ai && ctx.payload.markdown.trim().length > 0 && ctx.editor.schema.nodes.typedBlock !== void 0;
|
|
31
31
|
/**
|
|
32
|
-
* An AI transform: inserts an empty
|
|
33
|
-
* source block and the transform), streams the model's answer (`transferRequest`) and writes it into the
|
|
34
|
-
* write-back of „Ableiten (KI)“ (`writeDerivedBody`). A failed, aborted or empty run removes the
|
|
32
|
+
* An AI transform: inserts an empty Typed block (Block type `transferKindOf`, titled after the source, `meta.derivation` naming the
|
|
33
|
+
* source block and the transform), streams the model's answer (`transferRequest`) and writes it into the Typed block with the
|
|
34
|
+
* write-back of „Ableiten (KI)“ (`writeDerivedBody`). A failed, aborted or empty run removes the Typed block again.
|
|
35
35
|
*/
|
|
36
36
|
function aiTransform(spec) {
|
|
37
37
|
return {
|
|
@@ -127,7 +127,7 @@ function aiTransform(spec) {
|
|
|
127
127
|
}
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
-
/** „Zusammenfassung einfügen“: a new
|
|
130
|
+
/** „Zusammenfassung einfügen“: a new Typed block with a model-written summary of the dragged block (needs an AI backend). */
|
|
131
131
|
const summaryTransform = aiTransform({
|
|
132
132
|
id: "summary",
|
|
133
133
|
label: de["transfer.summary"],
|
|
@@ -137,7 +137,7 @@ const summaryTransform = aiTransform({
|
|
|
137
137
|
method: "transform:summary",
|
|
138
138
|
titleKey: "transfer.summaryTitle"
|
|
139
139
|
});
|
|
140
|
-
/** „Stichpunkte einfügen“: a new
|
|
140
|
+
/** „Stichpunkte einfügen“: a new Typed block with the dragged block's key statements as a list (needs an AI backend). */
|
|
141
141
|
const bulletsTransform = aiTransform({
|
|
142
142
|
id: "bullets",
|
|
143
143
|
label: de["transfer.bullets"],
|