@ecsplico/editor 2.1.2 → 3.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 +78 -1
- package/README.md +39 -156
- package/dist/adapters/comments/drizzle.d.ts +1 -1
- package/dist/adapters/comments/record.d.ts +15 -0
- package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
- package/dist/adapters/comments/rest-model.js.map +1 -0
- package/dist/adapters/comments/server.d.ts +2 -1
- package/dist/adapters/comments/server.js +2 -1
- package/dist/adapters/comments/server.js.map +1 -1
- package/dist/adapters/comments/types.d.ts +2 -2
- package/dist/adapters/document-state.d.ts +23 -0
- package/dist/adapters/document-state.js +66 -0
- package/dist/adapters/document-state.js.map +1 -0
- package/dist/adapters/documents.js +14 -11
- package/dist/adapters/documents.js.map +1 -1
- package/dist/adapters/drizzle.d.ts +2 -2
- package/dist/adapters/drizzle.js +97 -88
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/adapters/forge.d.ts +12 -7
- package/dist/adapters/forge.js +38 -45
- package/dist/adapters/forge.js.map +1 -1
- package/dist/adapters/http.d.ts +20 -0
- package/dist/adapters/http.js +68 -0
- package/dist/adapters/http.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/json.d.ts +3 -3
- package/dist/adapters/json.js +58 -49
- package/dist/adapters/json.js.map +1 -1
- package/dist/adapters/memory.d.ts +21 -0
- package/dist/adapters/memory.js +53 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/nitro.d.ts +27 -20
- package/dist/adapters/nitro.js +59 -163
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/react.d.ts +55 -0
- package/dist/adapters/react.js +209 -0
- package/dist/adapters/react.js.map +1 -0
- package/dist/adapters/shared.d.ts +15 -21
- package/dist/adapters/shared.js +10 -8
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/vue.d.ts +15 -30
- package/dist/adapters/vue.js +36 -87
- package/dist/adapters/vue.js.map +1 -1
- package/dist/code/CodeSurface.vue.d.ts +2 -2
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.js +95 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/abilities.js +1 -0
- package/dist/core/abilities.js.map +1 -1
- package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
- package/dist/core/api-routes.js.map +1 -0
- package/dist/core/backend-capability.d.ts +7 -0
- package/dist/core/backend-capability.js +17 -0
- package/dist/core/backend-capability.js.map +1 -0
- package/dist/core/backend-types.d.ts +245 -0
- package/dist/core/backend-types.js +17 -0
- package/dist/core/backend-types.js.map +1 -0
- package/dist/core/backend.d.ts +108 -0
- package/dist/core/backend.js +14 -0
- package/dist/core/backend.js.map +1 -0
- package/dist/core/block-convert.d.ts +7 -5
- package/dist/core/block-convert.js +28 -5
- package/dist/core/block-convert.js.map +1 -1
- package/dist/core/command-types.d.ts +96 -0
- package/dist/core/commands.d.ts +40 -0
- package/dist/core/commands.js +151 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/context.d.ts +17 -20
- package/dist/core/context.js +24 -21
- package/dist/core/context.js.map +1 -1
- package/dist/core/core-feature.d.ts +22 -7
- package/dist/core/core-feature.js +51 -18
- package/dist/core/core-feature.js.map +1 -1
- package/dist/core/define-editor.d.ts +4 -3
- package/dist/core/define-editor.js +14 -7
- package/dist/core/define-editor.js.map +1 -1
- package/dist/core/document-markdown.js +12 -3
- package/dist/core/document-markdown.js.map +1 -1
- package/dist/core/editor-ui.d.ts +8 -1
- package/dist/core/editor-ui.js +9 -8
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/extension-points.d.ts +3 -15
- package/dist/core/feature-context.d.ts +8 -7
- package/dist/core/feature-context.js +12 -11
- package/dist/core/feature-context.js.map +1 -1
- package/dist/core/feature.d.ts +41 -19
- package/dist/core/feature.js.map +1 -1
- package/dist/core/fences.d.ts +2 -1
- package/dist/core/fences.js +3 -2
- package/dist/core/fences.js.map +1 -1
- package/dist/core/fetch-error.d.ts +1 -1
- package/dist/core/fetch-error.js +3 -1
- package/dist/core/fetch-error.js.map +1 -1
- package/dist/core/hash.d.ts +5 -0
- package/dist/core/hash.js +11 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/http-backend.d.ts +11 -0
- package/dist/core/http-backend.js +195 -0
- package/dist/core/http-backend.js.map +1 -0
- package/dist/core/index.d.ts +15 -9
- package/dist/core/index.js +13 -9
- package/dist/core/palette.d.ts +48 -0
- package/dist/core/palette.js +95 -0
- package/dist/core/palette.js.map +1 -0
- package/dist/core/schema/abilities.d.ts +2 -1
- package/dist/core/schema/abilities.js +5 -1
- package/dist/core/schema/abilities.js.map +1 -1
- package/dist/core/schema/index.d.ts +11 -5
- package/dist/core/schema/index.js +13 -8
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/schema/mark-flags.d.ts +8 -1
- package/dist/core/schema/of-features.d.ts +13 -0
- package/dist/core/schema/of-features.js +14 -0
- package/dist/core/schema/of-features.js.map +1 -0
- package/dist/core/slash.d.ts +8 -6
- package/dist/core/slash.js +24 -16
- package/dist/core/slash.js.map +1 -1
- package/dist/core/surface-session.js +15 -5
- package/dist/core/surface-session.js.map +1 -1
- package/dist/core/toolbar-items.d.ts +10 -4
- package/dist/core/toolbar-items.js +269 -195
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/core/transaction-guards.d.ts +21 -0
- package/dist/core/transaction-guards.js +49 -0
- package/dist/core/transaction-guards.js.map +1 -0
- package/dist/core/util.js +11 -0
- package/dist/core/util.js.map +1 -0
- package/dist/document/DocumentSurface.d.ts +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +2 -2
- package/dist/features/ai/agent-op-review.d.ts +1 -1
- package/dist/features/ai/agent-op-review.js +1 -1
- package/dist/features/ai/agent-ops.js +1 -1
- package/dist/features/ai/ai-apply.d.ts +2 -2
- package/dist/features/ai/ai-apply.js +1 -1
- package/dist/features/ai/ai-review.js +1 -1
- package/dist/features/ai/ai-sse.d.ts +6 -6
- package/dist/features/ai/ai-sse.js +20 -63
- package/dist/features/ai/ai-sse.js.map +1 -1
- package/dist/features/ai/assistant.d.ts +8 -7
- package/dist/features/ai/assistant.js +3 -3
- package/dist/features/ai/assistant.js.map +1 -1
- package/dist/features/ai/block-actions.d.ts +3 -2
- package/dist/features/ai/block-actions.js +3 -2
- package/dist/features/ai/block-actions.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +1 -1
- package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
- package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/document-chat-model.d.ts +8 -7
- package/dist/features/ai/document-chat-model.js +5 -5
- package/dist/features/ai/document-chat-model.js.map +1 -1
- package/dist/features/ai/document-chat.js +1 -1
- package/dist/features/ai/feature.js +27 -14
- package/dist/features/ai/feature.js.map +1 -1
- package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
- package/dist/features/ai/ghost-autocomplete.js +6 -9
- package/dist/features/ai/ghost-autocomplete.js.map +1 -1
- package/dist/features/ai/index.d.ts +4 -3
- package/dist/features/ai/index.js +10 -10
- package/dist/features/ai/model-profiles.d.ts +5 -4
- package/dist/features/ai/model-profiles.js +2 -2
- package/dist/features/ai/model-profiles.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.js +1 -1
- package/dist/features/ai/protocol/block-lines.js.map +1 -1
- package/dist/features/ai/protocol/chat.js +6 -2
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -2
- package/dist/features/ai/protocol/models.d.ts +2 -16
- package/dist/features/ai/protocol/models.js.map +1 -1
- package/dist/features/ai/rate-limit.d.ts +1 -1
- package/dist/features/ai/rate-limit.js +3 -1
- package/dist/features/ai/rate-limit.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js +2 -2
- package/dist/features/ai/react/AiSidebar.js +4 -4
- package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
- package/dist/features/ai/react/DocumentChatOps.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
- package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/react/use-ai-assistant.js +3 -3
- package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/react/use-document-chat.js +2 -2
- package/dist/features/ai/react/use-document-chat.js.map +1 -1
- package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
- package/dist/features/ai/selection-flow.d.ts +1 -1
- package/dist/features/ai/selection-flow.js +1 -1
- package/dist/features/ai/sidebar-flow.d.ts +1 -1
- package/dist/features/ai/sidebar-flow.js +1 -1
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-ai-assistant.js +3 -3
- package/dist/features/ai/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/use-document-chat.js +2 -2
- package/dist/features/ai/use-document-chat.js.map +1 -1
- package/dist/features/ai/use-model-profiles.d.ts +2 -1
- package/dist/features/authors/attribution.d.ts +1 -1
- package/dist/features/authors/feature.js +12 -11
- package/dist/features/authors/feature.js.map +1 -1
- package/dist/features/block-ui/block-actions.d.ts +1 -1
- package/dist/features/block-ui/block-actions.js +1 -1
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/components/BlockUi.js +1 -1
- package/dist/features/block-ui/feature.js +1 -0
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.js +1 -1
- package/dist/features/collab/anchor-resolver.js +1 -1
- package/dist/features/collab/feature.js +2 -0
- package/dist/features/collab/feature.js.map +1 -1
- package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
- package/dist/features/comments/feature.js +12 -11
- package/dist/features/comments/feature.js.map +1 -1
- package/dist/features/comments/index.d.ts +3 -2
- package/dist/features/comments/index.js +1 -1
- package/dist/features/comments/margin-layout.d.ts +3 -2
- package/dist/features/comments/model.d.ts +3 -67
- package/dist/features/comments/model.js +2 -2
- package/dist/features/comments/model.js.map +1 -1
- package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
- package/dist/features/comments/react/use-comments.d.ts +2 -1
- package/dist/features/comments/react.d.ts +1 -1
- package/dist/features/comments/schema.d.ts +2 -52
- package/dist/features/comments/schema.js.map +1 -1
- package/dist/features/comments/snapshot-equality.js.map +1 -1
- package/dist/features/comments/use-comments.d.ts +3 -2
- package/dist/features/comments/vue.d.ts +1 -1
- package/dist/features/composites/accepted-children.d.ts +1 -0
- package/dist/features/composites/accepted-children.js +16 -1
- package/dist/features/composites/accepted-children.js.map +1 -1
- package/dist/features/composites/ai-brief.js +39 -0
- package/dist/features/composites/ai-brief.js.map +1 -1
- package/dist/features/composites/bibliography-command.d.ts +11 -0
- package/dist/features/composites/bibliography-form.js +90 -0
- package/dist/features/composites/bibliography-form.js.map +1 -0
- package/dist/features/composites/bibliography.d.ts +33 -0
- package/dist/features/composites/bibliography.js +120 -0
- package/dist/features/composites/bibliography.js.map +1 -0
- package/dist/features/composites/components/BibliographyEditor.js +261 -0
- package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/components/CompositeChrome.js +78 -29
- 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 +46 -11
- package/dist/features/composites/components/CompositeDetails.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
- package/dist/features/composites/components/CompositeFieldEditor.js +64 -5
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeInspector.js +20 -7
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/components/CompositeTable.js +13 -4
- package/dist/features/composites/components/CompositeTable.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/components/CompositeView.js +11 -1
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +103 -0
- package/dist/features/composites/components/DerivationBadge.js.map +1 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.js +122 -0
- package/dist/features/composites/components/ReferenceView.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
- package/dist/features/composites/components/SettingsEditingTab.js +1 -1
- package/dist/features/composites/convert.js +2 -2
- package/dist/features/composites/convert.js.map +1 -1
- package/dist/features/composites/derivation.d.ts +61 -0
- package/dist/features/composites/derivation.js +98 -0
- package/dist/features/composites/derivation.js.map +1 -0
- package/dist/features/composites/derived-chrome.js +69 -0
- package/dist/features/composites/derived-chrome.js.map +1 -0
- package/dist/features/composites/feature.js +159 -109
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/fences.d.ts +4 -3
- package/dist/features/composites/fences.js +51 -7
- package/dist/features/composites/fences.js.map +1 -1
- package/dist/features/composites/index.d.ts +12 -3
- package/dist/features/composites/index.js +15 -6
- package/dist/features/composites/inspector-model.d.ts +4 -1
- package/dist/features/composites/inspector-model.js +8 -4
- package/dist/features/composites/inspector-model.js.map +1 -1
- package/dist/features/composites/kinds.d.ts +16 -2
- package/dist/features/composites/kinds.js +145 -6
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/legacy.d.ts +4 -1
- package/dist/features/composites/legacy.js +56 -1
- package/dist/features/composites/legacy.js.map +1 -1
- package/dist/features/composites/meta-display.js +45 -2
- package/dist/features/composites/meta-display.js.map +1 -1
- package/dist/features/composites/meta-keys.d.ts +7 -0
- package/dist/features/composites/meta-keys.js +21 -0
- package/dist/features/composites/meta-keys.js.map +1 -0
- package/dist/features/composites/node-views.d.ts +6 -3
- package/dist/features/composites/node-views.js +7 -3
- package/dist/features/composites/node-views.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +6 -1
- package/dist/features/composites/nodes.js +33 -7
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/react/BibliographyEditor.js +229 -0
- package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/react/CompositeChrome.js +67 -19
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
- package/dist/features/composites/react/CompositeDetails.js +45 -6
- package/dist/features/composites/react/CompositeDetails.js.map +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.js +43 -1
- package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeInspector.js +12 -6
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/react/CompositeTable.js +16 -2
- package/dist/features/composites/react/CompositeTable.js.map +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
- package/dist/features/composites/react/CompositeView.js +6 -1
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +116 -0
- package/dist/features/composites/react/DerivationBadge.js.map +1 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/react/ReferenceView.d.ts +11 -0
- package/dist/features/composites/react/ReferenceView.js +115 -0
- package/dist/features/composites/react/ReferenceView.js.map +1 -0
- package/dist/features/composites/react/SettingsEditingTab.js +2 -2
- package/dist/features/composites/react.d.ts +2 -1
- package/dist/features/composites/react.js +7 -3
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/reference-ai.js +16 -0
- package/dist/features/composites/reference-ai.js.map +1 -0
- package/dist/features/composites/reference-label.d.ts +19 -0
- package/dist/features/composites/reference-label.js +58 -0
- package/dist/features/composites/reference-label.js.map +1 -0
- package/dist/features/composites/reference-suggestion.d.ts +10 -0
- package/dist/features/composites/reference-suggestion.js +41 -0
- package/dist/features/composites/reference-suggestion.js.map +1 -0
- package/dist/features/composites/reference-text.d.ts +18 -0
- package/dist/features/composites/reference-text.js +89 -0
- package/dist/features/composites/reference-text.js.map +1 -0
- package/dist/features/composites/reference.d.ts +44 -0
- package/dist/features/composites/reference.js +148 -0
- package/dist/features/composites/reference.js.map +1 -0
- package/dist/features/composites/reveal-source.d.ts +32 -0
- package/dist/features/composites/reveal-source.js +89 -0
- package/dist/features/composites/reveal-source.js.map +1 -0
- package/dist/features/composites/rows.d.ts +25 -3
- package/dist/features/composites/rows.js +72 -10
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/features/composites/schema.d.ts +9 -3
- package/dist/features/composites/schema.js +11 -4
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/table-model.js +12 -21
- package/dist/features/composites/table-model.js.map +1 -1
- package/dist/features/composites/vue.d.ts +3 -2
- package/dist/features/composites/vue.js +8 -4
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/media/assets.d.ts +2 -0
- package/dist/features/media/assets.js +3 -2
- package/dist/features/media/assets.js.map +1 -1
- package/dist/features/media/components/FileView.js +13 -4
- package/dist/features/media/components/FileView.js.map +1 -1
- package/dist/features/media/feature.d.ts +3 -3
- package/dist/features/media/feature.js +21 -28
- package/dist/features/media/feature.js.map +1 -1
- package/dist/features/media/file-import.js +2 -2
- package/dist/features/media/file-import.js.map +1 -1
- package/dist/features/media/index.d.ts +2 -2
- package/dist/features/media/index.js +3 -3
- package/dist/features/media/react/AudioRecorderModal.js +1 -1
- package/dist/features/media/react/FileView.js +19 -2
- package/dist/features/media/react/FileView.js.map +1 -1
- package/dist/features/media/react.js +1 -1
- package/dist/features/media/schema.d.ts +8 -0
- package/dist/features/media/schema.js +126 -11
- package/dist/features/media/schema.js.map +1 -1
- package/dist/features/media/upload.d.ts +16 -14
- package/dist/features/media/upload.js +13 -19
- package/dist/features/media/upload.js.map +1 -1
- package/dist/features/mentions/index.d.ts +4 -3
- package/dist/features/mentions/index.js +4 -1
- package/dist/features/mentions/index.js.map +1 -1
- package/dist/features/mentions/mention-text.d.ts +1 -1
- package/dist/features/mentions/mention-text.js.map +1 -1
- package/dist/features/mentions/mention.d.ts +0 -6
- package/dist/features/mentions/mention.js.map +1 -1
- package/dist/features/mentions/metadata-defaults.d.ts +2 -2
- package/dist/features/mentions/metadata-defaults.js +3 -9
- package/dist/features/mentions/metadata-defaults.js.map +1 -1
- package/dist/features/mentions/schema.d.ts +2 -1
- package/dist/features/mentions/schema.js.map +1 -1
- package/dist/features/tables/feature.d.ts +2 -1
- package/dist/features/tables/feature.js +90 -70
- package/dist/features/tables/feature.js.map +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
- package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/feature.js +2 -0
- package/dist/features/themes/feature.js.map +1 -1
- package/dist/features/themes/index.d.ts +2 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
- package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/schema.d.ts +2 -6
- package/dist/features/themes/schema.js.map +1 -1
- package/dist/features/themes/theme-runtime.d.ts +1 -1
- package/dist/features/themes/theme-runtime.js +4 -2
- package/dist/features/themes/theme-runtime.js.map +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js +48 -28
- package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/feature.js +21 -19
- package/dist/features/transcript/feature.js.map +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
- package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/segment-node.d.ts +2 -0
- package/dist/features/transcript/segment-node.js +35 -7
- package/dist/features/transcript/segment-node.js.map +1 -1
- package/dist/i18n/de/composites.js +91 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/integrations.js +49 -8
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/media.js +0 -1
- package/dist/i18n/de/media.js.map +1 -1
- package/dist/i18n/de/palette.js +22 -0
- package/dist/i18n/de/palette.js.map +1 -0
- package/dist/i18n/de/tables.js +0 -1
- package/dist/i18n/de/tables.js.map +1 -1
- package/dist/i18n/de/transcript.js +2 -1
- package/dist/i18n/de/transcript.js.map +1 -1
- package/dist/i18n/de/tree.js +3 -0
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +157 -11
- package/dist/i18n/de.js +3 -1
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en/composites.js +89 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/integrations.js +49 -8
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/media.js +0 -1
- package/dist/i18n/en/media.js.map +1 -1
- package/dist/i18n/en/palette.js +22 -0
- package/dist/i18n/en/palette.js.map +1 -0
- package/dist/i18n/en/tables.js +0 -1
- package/dist/i18n/en/tables.js.map +1 -1
- package/dist/i18n/en/transcript.js +2 -1
- package/dist/i18n/en/transcript.js.map +1 -1
- package/dist/i18n/en/tree.js +3 -0
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/i18n/en.js +3 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
- package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
- package/dist/integrations/composite-ai/composite-fill.js +97 -56
- package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
- package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
- package/dist/integrations/composite-ai/composite-summary.js +7 -26
- package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +29 -0
- package/dist/integrations/composite-ai/derive.js +173 -0
- package/dist/integrations/composite-ai/derive.js.map +1 -0
- package/dist/integrations/composite-ai/index.d.ts +11 -7
- package/dist/integrations/composite-ai/index.js +72 -14
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/rendition.d.ts +45 -0
- package/dist/integrations/composite-ai/rendition.js +314 -0
- package/dist/integrations/composite-ai/rendition.js.map +1 -0
- package/dist/integrations/composite-media/index.d.ts +7 -5
- package/dist/integrations/composite-media/index.js +86 -13
- package/dist/integrations/composite-media/index.js.map +1 -1
- package/dist/integrations/transcript-ai/index.d.ts +1 -1
- package/dist/integrations/transcript-ai/index.js +5 -2
- package/dist/integrations/transcript-ai/index.js.map +1 -1
- package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
- package/dist/integrations/transcript-ai/protocol.js +0 -2
- package/dist/integrations/transcript-ai/protocol.js.map +1 -1
- package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
- package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
- package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +12 -11
- package/dist/markdown/MarkdownSurface2.js +3 -6
- package/dist/markdown/MarkdownSurface2.js.map +1 -1
- package/dist/markdown/document.js +1 -1
- package/dist/markdown/source-mode.d.ts +1 -1
- package/dist/nuxt/module.d.ts +1402 -54
- package/dist/nuxt/module.js +6 -49
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/install-context.d.ts +7 -3
- package/dist/nuxt/runtime/install-context.js +14 -8
- package/dist/nuxt/runtime/install-context.js.map +1 -1
- package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
- package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
- package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
- package/dist/presets/compose.d.ts +94 -0
- package/dist/presets/compose.js +164 -0
- package/dist/presets/compose.js.map +1 -0
- package/dist/react/AdvancedEditor.d.ts +15 -15
- package/dist/react/AdvancedEditor.js +15 -13
- package/dist/react/AdvancedEditor.js.map +1 -1
- package/dist/react/EditorCommandPalette.d.ts +13 -0
- package/dist/react/EditorCommandPalette.js +118 -0
- package/dist/react/EditorCommandPalette.js.map +1 -0
- package/dist/react/EditorSettingsModal.d.ts +5 -5
- package/dist/react/EditorSettingsModal.js +7 -4
- package/dist/react/EditorSettingsModal.js.map +1 -1
- package/dist/react/EditorSettingsPanel.d.ts +7 -7
- package/dist/react/EditorSettingsPanel.js +8 -5
- package/dist/react/EditorSettingsPanel.js.map +1 -1
- package/dist/react/EditorSurface.d.ts +7 -7
- package/dist/react/EditorSurface.js +10 -6
- package/dist/react/EditorSurface.js.map +1 -1
- package/dist/react/EditorToolbar.d.ts +4 -6
- package/dist/react/EditorToolbar.js +29 -37
- package/dist/react/EditorToolbar.js.map +1 -1
- package/dist/react/SimpleEditor.d.ts +22 -23
- package/dist/react/SimpleEditor.js +21 -7
- package/dist/react/SimpleEditor.js.map +1 -1
- package/dist/react/advanced.d.ts +4 -3
- package/dist/react/advanced.js +2 -2
- package/dist/react/compose.d.ts +20 -0
- package/dist/react/compose.js +49 -0
- package/dist/react/compose.js.map +1 -0
- package/dist/react/composed-surface.d.ts +39 -0
- package/dist/react/composed-surface.js +71 -0
- package/dist/react/composed-surface.js.map +1 -0
- package/dist/react/context.js +6 -2
- package/dist/react/context.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +2 -1
- package/dist/react/key-labels.js +15 -0
- package/dist/react/key-labels.js.map +1 -0
- package/dist/react/settings.d.ts +1 -1
- package/dist/react/simple.d.ts +2 -2
- package/dist/react/ui/command.js +36 -1
- package/dist/react/ui/command.js.map +1 -1
- package/dist/schema/advanced.d.ts +6 -6
- package/dist/schema/advanced.js +26 -29
- package/dist/schema/advanced.js.map +1 -1
- package/dist/server/ai.d.ts +5 -16
- package/dist/server/ai.js.map +1 -1
- package/dist/server/auth.d.ts +9 -0
- package/dist/server/collab-state.d.ts +1 -1
- package/dist/server/collab-state.js.map +1 -1
- package/dist/server/collab.d.ts +13 -13
- package/dist/server/collab.js +34 -18
- package/dist/server/collab.js.map +1 -1
- package/dist/server/crossref.js +78 -0
- package/dist/server/crossref.js.map +1 -0
- package/dist/server/errors.d.ts +1 -1
- package/dist/server/errors.js +1 -1
- package/dist/server/errors.js.map +1 -1
- package/dist/server/extract.js +308 -0
- package/dist/server/extract.js.map +1 -0
- package/dist/server/h3.d.ts +8 -0
- package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
- package/dist/server/h3.js.map +1 -0
- package/dist/server/handlers/ai.d.ts +32 -10
- package/dist/server/handlers/ai.js +172 -37
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +12 -5
- package/dist/server/handlers/context.js +10 -4
- package/dist/server/handlers/context.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +19 -13
- package/dist/server/handlers/editor.js +49 -38
- package/dist/server/handlers/editor.js.map +1 -1
- package/dist/server/handlers/index.d.ts +4 -4
- package/dist/server/handlers/index.js +4 -4
- package/dist/server/host.d.ts +16 -25
- package/dist/server/host.js +58 -41
- package/dist/server/host.js.map +1 -1
- package/dist/server/index.d.ts +6 -4
- package/dist/server/index.js +3 -2
- package/dist/server/pdf.js +97 -0
- package/dist/server/pdf.js.map +1 -0
- package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
- package/dist/server/ports.js +29 -0
- package/dist/server/ports.js.map +1 -0
- package/dist/server/routes.d.ts +22 -0
- package/dist/server/routes.js +144 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/styles/composites.css +25 -0
- package/dist/tree/DocumentTree.js +56 -29
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +23 -0
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +2 -2
- package/dist/tree/tree-items.js +1 -1
- package/dist/vue/AdvancedEditor.js +30 -9
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +32 -12
- package/dist/vue/SimpleEditor.js +62 -16
- package/dist/vue/SimpleEditor.js.map +1 -1
- package/dist/vue/SimpleEditor.vue.d.ts +39 -9
- package/dist/vue/advanced.d.ts +3 -2
- package/dist/vue/advanced.js +2 -2
- package/dist/vue/components/EditorCommandPalette.js +91 -0
- package/dist/vue/components/EditorCommandPalette.js.map +1 -0
- package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
- package/dist/vue/components/EditorSettingsModal.js +10 -6
- package/dist/vue/components/EditorSettingsModal.js.map +1 -1
- package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
- package/dist/vue/components/EditorSettingsPanel.js +10 -6
- package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
- package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
- package/dist/vue/components/EditorSurface.js +11 -6
- package/dist/vue/components/EditorSurface.js.map +1 -1
- package/dist/vue/components/EditorSurface.vue.d.ts +11 -11
- package/dist/vue/components/EditorToolbar.js +41 -49
- package/dist/vue/components/EditorToolbar.js.map +1 -1
- package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
- package/dist/vue/compose.d.ts +17 -0
- package/dist/vue/compose.js +51 -0
- package/dist/vue/compose.js.map +1 -0
- package/dist/vue/context-key.d.ts +6 -0
- package/dist/vue/context-key.js +6 -0
- package/dist/vue/context-key.js.map +1 -0
- package/dist/vue/context.d.ts +8 -6
- package/dist/vue/context.js +6 -5
- package/dist/vue/context.js.map +1 -1
- package/dist/vue/index.d.ts +10 -8
- package/dist/vue/index.js +10 -8
- package/dist/vue/kit/index.d.ts +2 -2
- package/dist/vue/kit/nuxt-ui.js +23 -0
- package/dist/vue/kit/nuxt-ui.js.map +1 -1
- package/dist/vue/kit/types.d.ts +40 -1
- package/package.json +30 -16
- package/dist/adapters/comments/client.d.ts +0 -64
- package/dist/adapters/comments/client.js.map +0 -1
- package/dist/adapters/routes.js.map +0 -1
- package/dist/core/toolbar.d.ts +0 -74
- package/dist/core/toolbar.js +0 -45
- package/dist/core/toolbar.js.map +0 -1
- package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
- package/dist/integrations/composite-ai/document-transcribe.js +0 -89
- package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
- package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
- package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
- package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
- package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
- package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
- package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
- package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
- package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
- package/dist/presets/advanced.d.ts +0 -33
- package/dist/presets/advanced.js +0 -50
- package/dist/presets/advanced.js.map +0 -1
- package/dist/presets/simple.d.ts +0 -9
- package/dist/presets/simple.js +0 -12
- package/dist/presets/simple.js.map +0 -1
- package/dist/react/advanced-preset.d.ts +0 -11
- package/dist/react/advanced-preset.js +0 -31
- package/dist/react/advanced-preset.js.map +0 -1
- package/dist/vue/advanced-preset.d.ts +0 -13
- package/dist/vue/advanced-preset.js +0 -33
- package/dist/vue/advanced-preset.js.map +0 -1
- package/docs/extending.md +0 -538
- package/docs/integration.md +0 -632
- package/docs/llms.md +0 -383
- package/docs/usage.md +0 -1076
|
@@ -19,14 +19,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
19
19
|
__name: "SettingsAppearanceTab",
|
|
20
20
|
setup(__props) {
|
|
21
21
|
const { settings } = useEditorSettings();
|
|
22
|
-
const {
|
|
22
|
+
const { backend } = useEditorContext();
|
|
23
23
|
const { Badge: UBadge, Button: UButton, FormField: UFormField, Input: UInput, Select: USelect, notifier } = useUiKit();
|
|
24
24
|
const notify = notifier();
|
|
25
25
|
const { t } = useEditorI18n();
|
|
26
26
|
/** USelect has no `null` value: „Standard“ travels as this sentinel (no theme id can contain underscores). */
|
|
27
27
|
const themes = shallowRef([]);
|
|
28
28
|
async function loadThemes() {
|
|
29
|
-
themes.value = await
|
|
29
|
+
themes.value = await backend.themes?.list() ?? [];
|
|
30
30
|
}
|
|
31
31
|
onMounted(async () => {
|
|
32
32
|
try {
|
|
@@ -59,14 +59,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
59
59
|
async function upload() {
|
|
60
60
|
const css = cssFile.value;
|
|
61
61
|
if (!css || uploading.value) return;
|
|
62
|
-
const body = new FormData();
|
|
63
|
-
body.append("css", css);
|
|
64
|
-
if (manifestFile.value) body.append("manifest", manifestFile.value);
|
|
65
62
|
uploading.value = true;
|
|
66
63
|
try {
|
|
67
|
-
const record = await
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
const record = await backend.themes.store({
|
|
65
|
+
css,
|
|
66
|
+
...manifestFile.value && { manifest: manifestFile.value }
|
|
70
67
|
});
|
|
71
68
|
cssFile.value = null;
|
|
72
69
|
manifestFile.value = null;
|
|
@@ -96,7 +93,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
96
93
|
if (removing.value) return;
|
|
97
94
|
removing.value = record.id;
|
|
98
95
|
try {
|
|
99
|
-
await
|
|
96
|
+
await backend.themes.remove(record.id);
|
|
100
97
|
if (settings.value.appearance.theme === record.id) settings.value.appearance.theme = null;
|
|
101
98
|
notify({
|
|
102
99
|
title: t("themes.delete.done", { name: record.name }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsAppearanceTab.js","names":["_openBlock","_createElementBlock","_createVNode","SettingsSection","_unref","_withCtx","_createElementVNode","_withModifiers","_createBlock","_Fragment","_renderList","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/themes/components/SettingsAppearanceTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, ref, shallowRef } from 'vue'\nimport SettingsSection from '../../../vue/components/SettingsSection.vue'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../vue/context'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the Nuxt UI look. */\nconst { settings } = useEditorSettings()\nconst { fetch } = useEditorContext()\nconst { Badge: UBadge, Button: UButton, FormField: UFormField, Input: UInput, Select: USelect, notifier } = useUiKit()\nconst notify = notifier()\nconst { t } = useEditorI18n()\n\n/** USelect has no `null` value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\nconst themes = shallowRef<ThemeRecord[]>([])\n\nasync function loadThemes() {\n themes.value = await fetch<ThemeRecord[]>('/api/editor/themes')\n}\n\nonMounted(async () => {\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n})\n\nconst items = computed(() => [\n { label: t('themes.standard'), value: STANDARD },\n ...themes.value.map(({ id, name, author }) => ({ label: author ? `${name} – ${author}` : name, value: id })),\n])\n\nconst theme = computed({\n get: () => settings.value.appearance.theme ?? STANDARD,\n set: (value: string) => {\n settings.value.appearance.theme = value === STANDARD ? null : value\n },\n})\n\nconst cssFile = shallowRef<File | null>(null)\nconst manifestFile = shallowRef<File | null>(null)\nconst uploading = ref(false)\n/** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its model). */\nconst inputKey = ref(0)\n\nfunction pickedFile(event: Event): File | null {\n return (event.target as HTMLInputElement).files?.[0] ?? null\n}\n\nasync function upload() {\n const css = cssFile.value\n if (!css || uploading.value) return\n const body = new FormData()\n body.append('css', css)\n if (manifestFile.value) body.append('manifest', manifestFile.value)\n uploading.value = true\n try {\n const record = await fetch<ThemeRecord>('/api/editor/themes', { method: 'POST', body })\n cssFile.value = null\n manifestFile.value = null\n inputKey.value++\n settings.value.appearance.theme = record.id\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n uploading.value = false\n }\n}\n\nconst removing = ref<string | null>(null)\n\nasync function remove(record: ThemeRecord) {\n if (removing.value) return\n removing.value = record.id\n try {\n await fetch(`/api/editor/themes/${encodeURIComponent(record.id)}`, { method: 'DELETE' })\n if (settings.value.appearance.theme === record.id) settings.value.appearance.theme = null\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n removing.value = null\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-8\">\n <SettingsSection :title=\"t('themes.title')\">\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.select.label')\"\n :description=\"t('themes.select.description')\"\n >\n <USelect v-model=\"theme\" :items=\"items\" class=\"w-64\" />\n </UFormField>\n\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.upload.label')\"\n :description=\"t('themes.upload.description')\"\n >\n <form class=\"flex w-64 flex-col gap-2\" @submit.prevent=\"upload\">\n <UFormField :label=\"t('themes.upload.css')\" required size=\"xs\">\n <UInput\n :key=\"`css-${inputKey}`\"\n type=\"file\"\n accept=\".css,text/css\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"cssFile = pickedFile($event)\"\n />\n </UFormField>\n <UFormField :label=\"t('themes.upload.manifest')\" size=\"xs\">\n <UInput\n :key=\"`manifest-${inputKey}`\"\n type=\"file\"\n accept=\".json,application/json\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"manifestFile = pickedFile($event)\"\n />\n </UFormField>\n <UButton\n type=\"submit\"\n icon=\"i-lucide-upload\"\n :label=\"t('themes.upload.submit')\"\n class=\"self-start\"\n :disabled=\"!cssFile\"\n :loading=\"uploading\"\n />\n </form>\n </UFormField>\n </SettingsSection>\n\n <SettingsSection v-if=\"themes.length\" :title=\"t('themes.library')\">\n <div v-for=\"record in themes\" :key=\"record.id\" class=\"flex items-center gap-3 py-2\">\n <div class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate text-sm font-medium text-[var(--sk-fg-highlighted)]\">{{ record.name }}</span>\n <span v-if=\"record.version || record.author\" class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ [record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ') }}\n </span>\n </div>\n <UBadge v-if=\"record.builtin\" color=\"neutral\" variant=\"subtle\" :label=\"t('themes.builtin')\" />\n <UButton\n v-else\n icon=\"i-lucide-trash-2\"\n variant=\"ghost\"\n color=\"error\"\n :aria-label=\"t('themes.delete.label', { name: record.name })\"\n :loading=\"removing === record.id\"\n :disabled=\"removing !== null\"\n @click=\"remove(record)\"\n />\n </div>\n </SettingsSection>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SettingsAppearanceTab.js","names":["_openBlock","_createElementBlock","_createVNode","SettingsSection","_unref","_withCtx","_createElementVNode","_withModifiers","_createBlock","_Fragment","_renderList","_toDisplayString","_createCommentVNode"],"sources":["../../../../src/features/themes/components/SettingsAppearanceTab.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, ref, shallowRef } from 'vue'\nimport SettingsSection from '../../../vue/components/SettingsSection.vue'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../vue/context'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the Nuxt UI look. */\nconst { settings } = useEditorSettings()\nconst { backend } = useEditorContext()\nconst { Badge: UBadge, Button: UButton, FormField: UFormField, Input: UInput, Select: USelect, notifier } = useUiKit()\nconst notify = notifier()\nconst { t } = useEditorI18n()\n\n/** USelect has no `null` value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\nconst themes = shallowRef<ThemeRecord[]>([])\n\nasync function loadThemes() {\n themes.value = await backend.themes?.list() ?? []\n}\n\nonMounted(async () => {\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n})\n\nconst items = computed(() => [\n { label: t('themes.standard'), value: STANDARD },\n ...themes.value.map(({ id, name, author }) => ({ label: author ? `${name} – ${author}` : name, value: id })),\n])\n\nconst theme = computed({\n get: () => settings.value.appearance.theme ?? STANDARD,\n set: (value: string) => {\n settings.value.appearance.theme = value === STANDARD ? null : value\n },\n})\n\nconst cssFile = shallowRef<File | null>(null)\nconst manifestFile = shallowRef<File | null>(null)\nconst uploading = ref(false)\n/** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its model). */\nconst inputKey = ref(0)\n\nfunction pickedFile(event: Event): File | null {\n return (event.target as HTMLInputElement).files?.[0] ?? null\n}\n\nasync function upload() {\n const css = cssFile.value\n if (!css || uploading.value) return\n uploading.value = true\n try {\n // The themes feature needs the `themes` capability, so the tab is only mounted where the backend has it.\n const record = await backend.themes!.store({ css, ...(manifestFile.value && { manifest: manifestFile.value }) })\n cssFile.value = null\n manifestFile.value = null\n inputKey.value++\n settings.value.appearance.theme = record.id\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n uploading.value = false\n }\n}\n\nconst removing = ref<string | null>(null)\n\nasync function remove(record: ThemeRecord) {\n if (removing.value) return\n removing.value = record.id\n try {\n await backend.themes!.remove(record.id)\n if (settings.value.appearance.theme === record.id) settings.value.appearance.theme = null\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n removing.value = null\n }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-8\">\n <SettingsSection :title=\"t('themes.title')\">\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.select.label')\"\n :description=\"t('themes.select.description')\"\n >\n <USelect v-model=\"theme\" :items=\"items\" class=\"w-64\" />\n </UFormField>\n\n <UFormField\n orientation=\"horizontal\"\n class=\"py-3\"\n :label=\"t('themes.upload.label')\"\n :description=\"t('themes.upload.description')\"\n >\n <form class=\"flex w-64 flex-col gap-2\" @submit.prevent=\"upload\">\n <UFormField :label=\"t('themes.upload.css')\" required size=\"xs\">\n <UInput\n :key=\"`css-${inputKey}`\"\n type=\"file\"\n accept=\".css,text/css\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"cssFile = pickedFile($event)\"\n />\n </UFormField>\n <UFormField :label=\"t('themes.upload.manifest')\" size=\"xs\">\n <UInput\n :key=\"`manifest-${inputKey}`\"\n type=\"file\"\n accept=\".json,application/json\"\n class=\"w-full\"\n :disabled=\"uploading\"\n @change=\"manifestFile = pickedFile($event)\"\n />\n </UFormField>\n <UButton\n type=\"submit\"\n icon=\"i-lucide-upload\"\n :label=\"t('themes.upload.submit')\"\n class=\"self-start\"\n :disabled=\"!cssFile\"\n :loading=\"uploading\"\n />\n </form>\n </UFormField>\n </SettingsSection>\n\n <SettingsSection v-if=\"themes.length\" :title=\"t('themes.library')\">\n <div v-for=\"record in themes\" :key=\"record.id\" class=\"flex items-center gap-3 py-2\">\n <div class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate text-sm font-medium text-[var(--sk-fg-highlighted)]\">{{ record.name }}</span>\n <span v-if=\"record.version || record.author\" class=\"truncate text-xs text-[var(--sk-muted)]\">\n {{ [record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ') }}\n </span>\n </div>\n <UBadge v-if=\"record.builtin\" color=\"neutral\" variant=\"subtle\" :label=\"t('themes.builtin')\" />\n <UButton\n v-else\n icon=\"i-lucide-trash-2\"\n variant=\"ghost\"\n color=\"error\"\n :aria-label=\"t('themes.delete.label', { name: record.name })\"\n :loading=\"removing === record.id\"\n :disabled=\"removing !== null\"\n @click=\"remove(record)\"\n />\n </div>\n </SettingsSection>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -10,6 +10,8 @@ import { startObsidianTheme } from "./theme-runtime.js";
|
|
|
10
10
|
function themesFeatureCore(ui) {
|
|
11
11
|
return {
|
|
12
12
|
id: "themes",
|
|
13
|
+
dimension: "interaction",
|
|
14
|
+
backend: ["themes"],
|
|
13
15
|
setup: (ctx) => ui.lifetime ? ui.lifetime(ctx, () => startObsidianTheme(ctx)) : startObsidianTheme(ctx),
|
|
14
16
|
settingsTabs: (ctx) => [{
|
|
15
17
|
id: "appearance",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/themes/feature.ts"],"sourcesContent":["import type { EditorFeature, FeatureContext } from '../../core'\nimport { startObsidianTheme } from './theme-runtime'\n\n/** What a framework binding supplies to the themes feature. */\nexport interface ThemesUi<C> {\n /** The „Darstellung“ settings tab component of the framework. */\n settingsTab: C\n /**\n * Decides how long the theme runtime lives. Receives the feature context and `start` (starts the shared, reference-counted\n * runtime and returns its idempotent release); its return value is the feature's `setup` result (the teardown run when\n * the surface unmounts). Default: `start()` itself, i.e. the runtime lives as long as the surface is mounted.\n */\n lifetime?: (ctx: FeatureContext, start: () => () => void) => void | (() => void)\n}\n\n/**\n * Site-wide Obsidian themes: the theme named by the setting `appearance.theme` (`null` = the standard look) is\n * fetched from `GET /api/editor/themes/:id`, scoped to `.obsidian-theme` and injected as one `<style data-tiptap-theme>`\n * in the document head. `<html>` carries `obsidian-theme theme-dark|theme-light` following the app's colour mode\n * (`html.dark`, else the system preference), so the bridge stylesheet `styles/themes.css` maps Obsidian's variables onto\n * every UI token (the whole app) and onto the editor content. Needs an adapter with `listThemes`/`loadTheme`.\n */\nexport function themesFeatureCore<C>(ui: ThemesUi<C>): EditorFeature<C> {\n return {\n id: 'themes',\n setup: ctx => (ui.lifetime ? ui.lifetime(ctx, () => startObsidianTheme(ctx)) : startObsidianTheme(ctx)),\n settingsTabs: ctx => [{ id: 'appearance', label: ctx.t('themes.tab'), icon: 'i-lucide-palette', component: ui.settingsTab }],\n }\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,kBAAqB,IAAmC;CACtE,OAAO;EACL,IAAI;EACJ,QAAO,QAAQ,GAAG,WAAW,GAAG,SAAS,WAAW,mBAAmB,GAAG,CAAC,IAAI,mBAAmB,GAAG;EACrG,eAAc,QAAO,CAAC;GAAE,IAAI;GAAc,OAAO,IAAI,EAAE,YAAY;GAAG,MAAM;GAAoB,WAAW,GAAG;EAAY,CAAC;CAC7H;AACF"}
|
|
1
|
+
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/themes/feature.ts"],"sourcesContent":["import type { EditorFeature, FeatureContext } from '../../core'\nimport { startObsidianTheme } from './theme-runtime'\n\n/** What a framework binding supplies to the themes feature. */\nexport interface ThemesUi<C> {\n /** The „Darstellung“ settings tab component of the framework. */\n settingsTab: C\n /**\n * Decides how long the theme runtime lives. Receives the feature context and `start` (starts the shared, reference-counted\n * runtime and returns its idempotent release); its return value is the feature's `setup` result (the teardown run when\n * the surface unmounts). Default: `start()` itself, i.e. the runtime lives as long as the surface is mounted.\n */\n lifetime?: (ctx: FeatureContext, start: () => () => void) => void | (() => void)\n}\n\n/**\n * Site-wide Obsidian themes: the theme named by the setting `appearance.theme` (`null` = the standard look) is\n * fetched from `GET /api/editor/themes/:id`, scoped to `.obsidian-theme` and injected as one `<style data-tiptap-theme>`\n * in the document head. `<html>` carries `obsidian-theme theme-dark|theme-light` following the app's colour mode\n * (`html.dark`, else the system preference), so the bridge stylesheet `styles/themes.css` maps Obsidian's variables onto\n * every UI token (the whole app) and onto the editor content. Needs an adapter with `listThemes`/`loadTheme`.\n */\nexport function themesFeatureCore<C>(ui: ThemesUi<C>): EditorFeature<C> {\n return {\n id: 'themes',\n dimension: 'interaction',\n backend: ['themes'],\n setup: ctx => (ui.lifetime ? ui.lifetime(ctx, () => startObsidianTheme(ctx)) : startObsidianTheme(ctx)),\n settingsTabs: ctx => [{ id: 'appearance', label: ctx.t('themes.tab'), icon: 'i-lucide-palette', component: ui.settingsTab }],\n }\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,kBAAqB,IAAmC;CACtE,OAAO;EACL,IAAI;EACJ,WAAW;EACX,SAAS,CAAC,QAAQ;EAClB,QAAO,QAAQ,GAAG,WAAW,GAAG,SAAS,WAAW,mBAAmB,GAAG,CAAC,IAAI,mBAAmB,GAAG;EACrG,eAAc,QAAO,CAAC;GAAE,IAAI;GAAc,OAAO,IAAI,EAAE,YAAY;GAAG,MAAM;GAAoB,WAAW,GAAG;EAAY,CAAC;CAC7H;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { THEME_ID } from "../../settings/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ThemeRecord } from "../../core/backend-types.js";
|
|
3
|
+
import { MAX_THEME_BYTES, ThemeManifest, themeManifestSchema } from "./schema.js";
|
|
3
4
|
import { ThemesUi, themesFeatureCore } from "./feature.js";
|
|
4
5
|
import { scopeThemeCss } from "./scope-css.js";
|
|
5
6
|
import { THEME_SCOPE_CLASS, startObsidianTheme } from "./theme-runtime.js";
|
|
@@ -36,12 +36,12 @@ function FormField({ label, description, required, small, horizontal, children }
|
|
|
36
36
|
/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the normal look. */
|
|
37
37
|
function SettingsAppearanceTab() {
|
|
38
38
|
const { settings, update } = useEditorSettings();
|
|
39
|
-
const {
|
|
39
|
+
const { backend, notify } = useEditorContext();
|
|
40
40
|
const { t } = useEditorI18n();
|
|
41
41
|
const [themes, setThemes] = useState([]);
|
|
42
42
|
const loadThemes = useCallback(async () => {
|
|
43
|
-
setThemes(await
|
|
44
|
-
}, [
|
|
43
|
+
setThemes(await backend.themes?.list() ?? []);
|
|
44
|
+
}, [backend]);
|
|
45
45
|
useEffect(() => {
|
|
46
46
|
loadThemes().catch((error) => {
|
|
47
47
|
console.warn("tiptap-editor: Die Themes konnten nicht geladen werden", error);
|
|
@@ -57,14 +57,11 @@ function SettingsAppearanceTab() {
|
|
|
57
57
|
const [inputKey, setInputKey] = useState(0);
|
|
58
58
|
async function upload() {
|
|
59
59
|
if (!cssFile || uploading) return;
|
|
60
|
-
const body = new FormData();
|
|
61
|
-
body.append("css", cssFile);
|
|
62
|
-
if (manifestFile) body.append("manifest", manifestFile);
|
|
63
60
|
setUploading(true);
|
|
64
61
|
try {
|
|
65
|
-
const record = await
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
const record = await backend.themes.store({
|
|
63
|
+
css: cssFile,
|
|
64
|
+
...manifestFile && { manifest: manifestFile }
|
|
68
65
|
});
|
|
69
66
|
setCssFile(null);
|
|
70
67
|
setManifestFile(null);
|
|
@@ -94,7 +91,7 @@ function SettingsAppearanceTab() {
|
|
|
94
91
|
if (removing) return;
|
|
95
92
|
setRemoving(record.id);
|
|
96
93
|
try {
|
|
97
|
-
await
|
|
94
|
+
await backend.themes.remove(record.id);
|
|
98
95
|
if (settings.appearance.theme === record.id) setTheme(null);
|
|
99
96
|
notify({
|
|
100
97
|
title: t("themes.delete.done", { name: record.name }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsAppearanceTab.js","names":[],"sources":["../../../../src/features/themes/react/SettingsAppearanceTab.tsx"],"sourcesContent":["import { type ReactNode, useCallback, useEffect, useState } from 'react'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../react/context'\nimport { Icon } from '../../../react/Icon'\nimport { useEditorSettings } from '../../../react/settings'\nimport { SettingsSection } from '../../../react/SettingsSection'\nimport { Badge } from '../../../react/ui/badge'\nimport { Button } from '../../../react/ui/button'\nimport { Input } from '../../../react/ui/input'\nimport { Label } from '../../../react/ui/label'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../react/ui/select'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** Radix Select has no `null`/empty value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\n/** A label (+ description) next to or above its control; the counterpart of Nuxt UI's `UFormField`. */\nfunction FormField({ label, description, required, small, horizontal, children }: {\n label: string\n description?: string\n required?: boolean\n small?: boolean\n horizontal?: boolean\n children: ReactNode\n}) {\n return (\n <div className={horizontal ? 'flex items-start justify-between gap-4 py-3' : 'flex flex-col gap-1.5'}>\n <div className={horizontal ? 'min-w-0 flex-1' : undefined}>\n <Label className={small ? 'text-xs font-medium text-foreground' : 'text-sm font-medium text-foreground'}>\n {label}\n {required && <span className=\"ms-0.5 text-destructive\">*</span>}\n </Label>\n {description && <p className=\"text-sm text-muted-foreground\">{description}</p>}\n </div>\n {children}\n </div>\n )\n}\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the normal look. */\nexport function SettingsAppearanceTab() {\n const { settings, update } = useEditorSettings()\n const { fetch, notify } = useEditorContext()\n const { t } = useEditorI18n()\n\n const [themes, setThemes] = useState<ThemeRecord[]>([])\n\n const loadThemes = useCallback(async () => {\n setThemes(await fetch<ThemeRecord[]>('/api/editor/themes'))\n }, [fetch])\n\n useEffect(() => {\n loadThemes().catch((error: unknown) => {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n })\n }, [loadThemes])\n\n const setTheme = (value: string | null) => update((draft) => { draft.appearance.theme = value })\n\n const [cssFile, setCssFile] = useState<File | null>(null)\n const [manifestFile, setManifestFile] = useState<File | null>(null)\n const [uploading, setUploading] = useState(false)\n /** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its value). */\n const [inputKey, setInputKey] = useState(0)\n\n async function upload() {\n if (!cssFile || uploading) return\n const body = new FormData()\n body.append('css', cssFile)\n if (manifestFile) body.append('manifest', manifestFile)\n setUploading(true)\n try {\n const record = await fetch<ThemeRecord>('/api/editor/themes', { method: 'POST', body })\n setCssFile(null)\n setManifestFile(null)\n setInputKey(key => key + 1)\n setTheme(record.id)\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setUploading(false)\n }\n }\n\n const [removing, setRemoving] = useState<string | null>(null)\n\n async function remove(record: ThemeRecord) {\n if (removing) return\n setRemoving(record.id)\n try {\n await fetch(`/api/editor/themes/${encodeURIComponent(record.id)}`, { method: 'DELETE' })\n if (settings.appearance.theme === record.id) setTheme(null)\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setRemoving(null)\n }\n }\n\n return (\n <div className=\"flex flex-col gap-8\">\n <SettingsSection title={t('themes.title')}>\n <FormField\n horizontal\n label={t('themes.select.label')}\n description={t('themes.select.description')}\n >\n <Select value={settings.appearance.theme ?? STANDARD} onValueChange={value => setTheme(value === STANDARD ? null : value)}>\n <SelectTrigger className=\"w-64\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={STANDARD}>{t('themes.standard')}</SelectItem>\n {themes.map(({ id, name, author }) => (\n <SelectItem key={id} value={id}>{author ? `${name} – ${author}` : name}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormField>\n\n <FormField\n horizontal\n label={t('themes.upload.label')}\n description={t('themes.upload.description')}\n >\n <form\n className=\"flex w-64 flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault()\n void upload()\n }}\n >\n <FormField label={t('themes.upload.css')} required small>\n <Input\n key={`css-${inputKey}`}\n type=\"file\"\n accept=\".css,text/css\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setCssFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <FormField label={t('themes.upload.manifest')} small>\n <Input\n key={`manifest-${inputKey}`}\n type=\"file\"\n accept=\".json,application/json\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setManifestFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <Button type=\"submit\" className=\"self-start\" disabled={!cssFile || uploading}>\n <Icon name={uploading ? 'i-lucide-loader-circle' : 'i-lucide-upload'} className={uploading ? 'animate-spin' : undefined} />\n {t('themes.upload.submit')}\n </Button>\n </form>\n </FormField>\n </SettingsSection>\n\n {themes.length > 0 && (\n <SettingsSection title={t('themes.library')}>\n {themes.map(record => (\n <div key={record.id} className=\"flex items-center gap-3 py-2\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium text-foreground\">{record.name}</span>\n {(record.version || record.author) && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {[record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n {record.builtin\n ? <Badge variant=\"secondary\">{t('themes.builtin')}</Badge>\n : (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-destructive\"\n aria-label={t('themes.delete.label', { name: record.name })}\n disabled={removing !== null}\n onClick={() => void remove(record)}\n >\n <Icon name={removing === record.id ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={removing === record.id ? 'animate-spin' : undefined} />\n </Button>\n )}\n </div>\n ))}\n </SettingsSection>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SettingsAppearanceTab.js","names":[],"sources":["../../../../src/features/themes/react/SettingsAppearanceTab.tsx"],"sourcesContent":["import { type ReactNode, useCallback, useEffect, useState } from 'react'\nimport { fetchErrorMessage } from '../../../core/fetch-error'\nimport { useEditorContext } from '../../../react/context'\nimport { Icon } from '../../../react/Icon'\nimport { useEditorSettings } from '../../../react/settings'\nimport { SettingsSection } from '../../../react/SettingsSection'\nimport { Badge } from '../../../react/ui/badge'\nimport { Button } from '../../../react/ui/button'\nimport { Input } from '../../../react/ui/input'\nimport { Label } from '../../../react/ui/label'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../react/ui/select'\nimport { useEditorI18n } from '../../../react/use-editor-i18n'\nimport type { ThemeRecord } from '../schema'\n\n/** Radix Select has no `null`/empty value: „Standard“ travels as this sentinel (no theme id can contain underscores). */\nconst STANDARD = '__standard__'\n\n/** A label (+ description) next to or above its control; the counterpart of Nuxt UI's `UFormField`. */\nfunction FormField({ label, description, required, small, horizontal, children }: {\n label: string\n description?: string\n required?: boolean\n small?: boolean\n horizontal?: boolean\n children: ReactNode\n}) {\n return (\n <div className={horizontal ? 'flex items-start justify-between gap-4 py-3' : 'flex flex-col gap-1.5'}>\n <div className={horizontal ? 'min-w-0 flex-1' : undefined}>\n <Label className={small ? 'text-xs font-medium text-foreground' : 'text-sm font-medium text-foreground'}>\n {label}\n {required && <span className=\"ms-0.5 text-destructive\">*</span>}\n </Label>\n {description && <p className=\"text-sm text-muted-foreground\">{description}</p>}\n </div>\n {children}\n </div>\n )\n}\n\n/** „Darstellung“: the Obsidian theme of the whole app (`appearance.theme`); „Standard“ is the normal look. */\nexport function SettingsAppearanceTab() {\n const { settings, update } = useEditorSettings()\n const { backend, notify } = useEditorContext()\n const { t } = useEditorI18n()\n\n const [themes, setThemes] = useState<ThemeRecord[]>([])\n\n const loadThemes = useCallback(async () => {\n setThemes(await backend.themes?.list() ?? [])\n }, [backend])\n\n useEffect(() => {\n loadThemes().catch((error: unknown) => {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n })\n }, [loadThemes])\n\n const setTheme = (value: string | null) => update((draft) => { draft.appearance.theme = value })\n\n const [cssFile, setCssFile] = useState<File | null>(null)\n const [manifestFile, setManifestFile] = useState<File | null>(null)\n const [uploading, setUploading] = useState(false)\n /** Bumped after an upload to remount the file inputs (a file input cannot be cleared through its value). */\n const [inputKey, setInputKey] = useState(0)\n\n async function upload() {\n if (!cssFile || uploading) return\n setUploading(true)\n try {\n // The themes feature needs the `themes` capability, so the tab is only mounted where the backend has it.\n const record = await backend.themes!.store({ css: cssFile, ...(manifestFile && { manifest: manifestFile }) })\n setCssFile(null)\n setManifestFile(null)\n setInputKey(key => key + 1)\n setTheme(record.id)\n notify({ title: t('themes.upload.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.upload.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setUploading(false)\n }\n }\n\n const [removing, setRemoving] = useState<string | null>(null)\n\n async function remove(record: ThemeRecord) {\n if (removing) return\n setRemoving(record.id)\n try {\n await backend.themes!.remove(record.id)\n if (settings.appearance.theme === record.id) setTheme(null)\n notify({ title: t('themes.delete.done', { name: record.name }), kind: 'success' })\n try {\n await loadThemes()\n }\n catch (error) {\n console.warn('tiptap-editor: Die Themes konnten nicht geladen werden', error)\n }\n }\n catch (error) {\n notify({ title: t('themes.delete.failed'), description: fetchErrorMessage(error), kind: 'error' })\n }\n finally {\n setRemoving(null)\n }\n }\n\n return (\n <div className=\"flex flex-col gap-8\">\n <SettingsSection title={t('themes.title')}>\n <FormField\n horizontal\n label={t('themes.select.label')}\n description={t('themes.select.description')}\n >\n <Select value={settings.appearance.theme ?? STANDARD} onValueChange={value => setTheme(value === STANDARD ? null : value)}>\n <SelectTrigger className=\"w-64\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={STANDARD}>{t('themes.standard')}</SelectItem>\n {themes.map(({ id, name, author }) => (\n <SelectItem key={id} value={id}>{author ? `${name} – ${author}` : name}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormField>\n\n <FormField\n horizontal\n label={t('themes.upload.label')}\n description={t('themes.upload.description')}\n >\n <form\n className=\"flex w-64 flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault()\n void upload()\n }}\n >\n <FormField label={t('themes.upload.css')} required small>\n <Input\n key={`css-${inputKey}`}\n type=\"file\"\n accept=\".css,text/css\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setCssFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <FormField label={t('themes.upload.manifest')} small>\n <Input\n key={`manifest-${inputKey}`}\n type=\"file\"\n accept=\".json,application/json\"\n className=\"w-full\"\n disabled={uploading}\n onChange={event => setManifestFile(event.target.files?.[0] ?? null)}\n />\n </FormField>\n <Button type=\"submit\" className=\"self-start\" disabled={!cssFile || uploading}>\n <Icon name={uploading ? 'i-lucide-loader-circle' : 'i-lucide-upload'} className={uploading ? 'animate-spin' : undefined} />\n {t('themes.upload.submit')}\n </Button>\n </form>\n </FormField>\n </SettingsSection>\n\n {themes.length > 0 && (\n <SettingsSection title={t('themes.library')}>\n {themes.map(record => (\n <div key={record.id} className=\"flex items-center gap-3 py-2\">\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium text-foreground\">{record.name}</span>\n {(record.version || record.author) && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {[record.version && t('themes.version', { version: record.version }), record.author && t('themes.byAuthor', { author: record.author })].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n {record.builtin\n ? <Badge variant=\"secondary\">{t('themes.builtin')}</Badge>\n : (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"text-destructive\"\n aria-label={t('themes.delete.label', { name: record.name })}\n disabled={removing !== null}\n onClick={() => void remove(record)}\n >\n <Icon name={removing === record.id ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={removing === record.id ? 'animate-spin' : undefined} />\n </Button>\n )}\n </div>\n ))}\n </SettingsSection>\n )}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { THEME_ID } from "../../settings/index.js";
|
|
2
|
+
import { ThemeRecord } from "../../core/backend-types.js";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
//#region src/features/themes/schema.d.ts
|
|
4
5
|
/** Upper bound for a theme stylesheet (bytes). */
|
|
@@ -11,11 +12,6 @@ export declare const themeManifestSchema: z.ZodObject<{
|
|
|
11
12
|
authorUrl: z.ZodOptional<z.ZodString>;
|
|
12
13
|
}, z.core.$strip>;
|
|
13
14
|
export type ThemeManifest = z.infer<typeof themeManifestSchema>;
|
|
14
|
-
/** A theme in the shared library; `builtin` themes are seeded by the host and cannot be deleted. */
|
|
15
|
-
export interface ThemeRecord extends ThemeManifest {
|
|
16
|
-
id: string;
|
|
17
|
-
builtin: boolean;
|
|
18
|
-
}
|
|
19
15
|
//#endregion
|
|
20
|
-
export { THEME_ID };
|
|
16
|
+
export { THEME_ID, type ThemeRecord };
|
|
21
17
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/themes/schema.ts"],"sourcesContent":["import { z } from 'zod'\nimport { THEME_ID } from '../../settings'\n\n/** Valid theme id (pattern defined in `settings`, which validates `appearance.theme` with it). */\nexport { THEME_ID }\n\n/** Upper bound for a theme stylesheet (bytes). */\nexport const MAX_THEME_BYTES = 2 * 1024 * 1024\n\n/** The subset of an Obsidian `manifest.json` the editor keeps. */\nexport const themeManifestSchema = z.object({\n name: z.string().min(1).max(120),\n version: z.string().max(40).optional(),\n author: z.string().max(120).optional(),\n authorUrl: z.string().max(400).optional(),\n})\nexport type ThemeManifest = z.infer<typeof themeManifestSchema>\n\n/** A theme in the shared library
|
|
1
|
+
{"version":3,"file":"schema.js","names":[],"sources":["../../../src/features/themes/schema.ts"],"sourcesContent":["import { z } from 'zod'\nimport { THEME_ID } from '../../settings'\n\n/** Valid theme id (pattern defined in `settings`, which validates `appearance.theme` with it). */\nexport { THEME_ID }\n\n/** Upper bound for a theme stylesheet (bytes). */\nexport const MAX_THEME_BYTES = 2 * 1024 * 1024\n\n/** The subset of an Obsidian `manifest.json` the editor keeps. */\nexport const themeManifestSchema = z.object({\n name: z.string().min(1).max(120),\n version: z.string().max(40).optional(),\n author: z.string().max(120).optional(),\n authorUrl: z.string().max(400).optional(),\n})\nexport type ThemeManifest = z.infer<typeof themeManifestSchema>\n\n/** A theme in the shared library (`ThemeManifest` + `id` + `builtin`); defined in core because `EditorBackend` lists them. */\nexport type { ThemeRecord } from '../../core/backend-types'\n"],"mappings":";;;;AAOA,MAAa,kBAAkB;;AAG/B,MAAa,sBAAsB,EAAE,OAAO;CAC1C,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CAC/B,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS;CACrC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;CACrC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;AAC1C,CAAC"}
|
|
@@ -4,7 +4,7 @@ import { FeatureContext } from "../../core/feature.js";
|
|
|
4
4
|
export declare const THEME_SCOPE_CLASS = "obsidian-theme";
|
|
5
5
|
/**
|
|
6
6
|
* The theme runtime of the app `ctx` belongs to: applies `settings.appearance.theme` (fetches the stylesheet through
|
|
7
|
-
* `ctx.
|
|
7
|
+
* `ctx.backend.themes`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`
|
|
8
8
|
* on `<html>`) and follows the app's colour mode.
|
|
9
9
|
* Reference-counted per app: the first call starts the runtime, every call returns an idempotent release, the last release
|
|
10
10
|
* stops it (removing the style element and the root classes). Surfaces of one app share a single runtime.
|
|
@@ -51,7 +51,9 @@ function createRuntime(ctx) {
|
|
|
51
51
|
async function load(id) {
|
|
52
52
|
const cached = cache.get(id);
|
|
53
53
|
if (cached !== void 0) return cached;
|
|
54
|
-
const
|
|
54
|
+
const themes = ctx.backend.themes;
|
|
55
|
+
if (!themes) throw new Error(`@ecsplico/editor: backend "${ctx.backend.name}" has no themes capability`);
|
|
56
|
+
const css = await themes.css(id);
|
|
55
57
|
const scoped = scopeThemeCss(css, `.${THEME_SCOPE_CLASS}`);
|
|
56
58
|
cache.set(id, scoped);
|
|
57
59
|
return scoped;
|
|
@@ -115,7 +117,7 @@ function createRuntime(ctx) {
|
|
|
115
117
|
}
|
|
116
118
|
/**
|
|
117
119
|
* The theme runtime of the app `ctx` belongs to: applies `settings.appearance.theme` (fetches the stylesheet through
|
|
118
|
-
* `ctx.
|
|
120
|
+
* `ctx.backend.themes`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`
|
|
119
121
|
* on `<html>`) and follows the app's colour mode.
|
|
120
122
|
* Reference-counted per app: the first call starts the runtime, every call returns an idempotent release, the last release
|
|
121
123
|
* stops it (removing the style element and the root classes). Surfaces of one app share a single runtime.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-runtime.js","names":[],"sources":["../../../src/features/themes/theme-runtime.ts"],"sourcesContent":["import type { FeatureContext } from '../../core/feature'\nimport { scopeThemeCss } from './scope-css'\n\n/** Class that scopes a theme's rules (`scopeThemeCss`) and the bridge stylesheet; the runtime puts it on `<html>`. */\nexport const THEME_SCOPE_CLASS = 'obsidian-theme'\n\n/** Marks the single `<style>` element in `document.head` that carries the active theme. */\nconst STYLE_ATTRIBUTE = 'data-tiptap-theme'\n\n// One runtime per app (keyed by the app's settings store): every surface of the page shares the style element and the\n// cache. Reference-counted: the last release stops it.\nconst runtimes = new Map<object, { count: number, stop: () => void }>()\n\nconst PREFERS_DARK = '(prefers-color-scheme: dark)'\n\n/** Whether the app is in dark mode: `html.dark` (Nuxt colour mode), `html.light`, else the system preference. */\nfunction readDark(): boolean {\n if (typeof document === 'undefined') return false\n const classes = document.documentElement.classList\n if (classes.contains('dark')) return true\n if (classes.contains('light')) return false\n return typeof matchMedia === 'function' && matchMedia(PREFERS_DARK).matches\n}\n\nfunction createRuntime(ctx: FeatureContext): () => void {\n let dark = readDark()\n /** The theme whose style is in the document; null = standard look. */\n let applied: string | null = null\n const cache = new Map<string, string>()\n let style: HTMLStyleElement | undefined\n let latest = 0\n let teardown: (() => void) | undefined\n let stopTheme: (() => void) | undefined\n\n /** `obsidian-theme theme-dark|theme-light` on `<html>` while a theme is applied; none of the three otherwise (standard look). */\n function applyRootClasses() {\n if (typeof document === 'undefined') return\n const root = document.documentElement.classList\n const on = applied !== null\n root.toggle(THEME_SCOPE_CLASS, on)\n root.toggle('theme-dark', on && dark)\n root.toggle('theme-light', on && !dark)\n }\n\n function removeRootClasses() {\n if (typeof document === 'undefined') return\n document.documentElement.classList.remove(THEME_SCOPE_CLASS, 'theme-dark', 'theme-light')\n }\n\n function removeStyle() {\n style?.remove()\n style = undefined\n }\n\n function showStyle(css: string) {\n if (!style) {\n style = document.createElement('style')\n style.setAttribute(STYLE_ATTRIBUTE, '')\n document.head.appendChild(style)\n }\n style.textContent = css\n }\n\n async function load(id: string): Promise<string> {\n const cached = cache.get(id)\n if (cached !== undefined) return cached\n const
|
|
1
|
+
{"version":3,"file":"theme-runtime.js","names":[],"sources":["../../../src/features/themes/theme-runtime.ts"],"sourcesContent":["import type { FeatureContext } from '../../core/feature'\nimport { scopeThemeCss } from './scope-css'\n\n/** Class that scopes a theme's rules (`scopeThemeCss`) and the bridge stylesheet; the runtime puts it on `<html>`. */\nexport const THEME_SCOPE_CLASS = 'obsidian-theme'\n\n/** Marks the single `<style>` element in `document.head` that carries the active theme. */\nconst STYLE_ATTRIBUTE = 'data-tiptap-theme'\n\n// One runtime per app (keyed by the app's settings store): every surface of the page shares the style element and the\n// cache. Reference-counted: the last release stops it.\nconst runtimes = new Map<object, { count: number, stop: () => void }>()\n\nconst PREFERS_DARK = '(prefers-color-scheme: dark)'\n\n/** Whether the app is in dark mode: `html.dark` (Nuxt colour mode), `html.light`, else the system preference. */\nfunction readDark(): boolean {\n if (typeof document === 'undefined') return false\n const classes = document.documentElement.classList\n if (classes.contains('dark')) return true\n if (classes.contains('light')) return false\n return typeof matchMedia === 'function' && matchMedia(PREFERS_DARK).matches\n}\n\nfunction createRuntime(ctx: FeatureContext): () => void {\n let dark = readDark()\n /** The theme whose style is in the document; null = standard look. */\n let applied: string | null = null\n const cache = new Map<string, string>()\n let style: HTMLStyleElement | undefined\n let latest = 0\n let teardown: (() => void) | undefined\n let stopTheme: (() => void) | undefined\n\n /** `obsidian-theme theme-dark|theme-light` on `<html>` while a theme is applied; none of the three otherwise (standard look). */\n function applyRootClasses() {\n if (typeof document === 'undefined') return\n const root = document.documentElement.classList\n const on = applied !== null\n root.toggle(THEME_SCOPE_CLASS, on)\n root.toggle('theme-dark', on && dark)\n root.toggle('theme-light', on && !dark)\n }\n\n function removeRootClasses() {\n if (typeof document === 'undefined') return\n document.documentElement.classList.remove(THEME_SCOPE_CLASS, 'theme-dark', 'theme-light')\n }\n\n function removeStyle() {\n style?.remove()\n style = undefined\n }\n\n function showStyle(css: string) {\n if (!style) {\n style = document.createElement('style')\n style.setAttribute(STYLE_ATTRIBUTE, '')\n document.head.appendChild(style)\n }\n style.textContent = css\n }\n\n async function load(id: string): Promise<string> {\n const cached = cache.get(id)\n if (cached !== undefined) return cached\n const themes = ctx.backend.themes\n if (!themes) throw new Error(`@ecsplico/editor: backend \"${ctx.backend.name}\" has no themes capability`)\n const css = await themes.css(id)\n const scoped = scopeThemeCss(css, `.${THEME_SCOPE_CLASS}`)\n cache.set(id, scoped)\n return scoped\n }\n\n async function select(id: string | null) {\n const ticket = ++latest\n if (id === null) {\n removeStyle()\n applied = null\n applyRootClasses()\n return\n }\n try {\n const css = await load(id)\n if (ticket !== latest) return\n showStyle(css)\n applied = id\n applyRootClasses()\n }\n catch (error) {\n if (ticket !== latest) return\n // The stored setting stays untouched: the theme may come back (host restored it), the session shows the standard look.\n console.warn(`tiptap-editor: Theme „${id}“ konnte nicht geladen werden, die Standard-Darstellung wird verwendet`, error)\n removeStyle()\n applied = null\n applyRootClasses()\n }\n }\n\n if (typeof document !== 'undefined') {\n void select(ctx.settings.get().appearance.theme)\n let lastTheme = ctx.settings.get().appearance.theme\n stopTheme = ctx.settings.subscribe((value) => {\n if (value.appearance.theme === lastTheme) return\n lastTheme = value.appearance.theme\n void select(lastTheme)\n })\n applyRootClasses()\n const onDarkChange = () => {\n const next = readDark()\n if (next === dark) return\n dark = next\n applyRootClasses()\n }\n const observer = new MutationObserver(onDarkChange)\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n const media = typeof matchMedia === 'function' ? matchMedia(PREFERS_DARK) : undefined\n media?.addEventListener('change', onDarkChange)\n teardown = () => {\n observer.disconnect()\n media?.removeEventListener('change', onDarkChange)\n removeStyle()\n }\n }\n\n return () => {\n latest++\n stopTheme?.()\n teardown?.()\n removeRootClasses()\n }\n}\n\n/**\n * The theme runtime of the app `ctx` belongs to: applies `settings.appearance.theme` (fetches the stylesheet through\n * `ctx.backend.themes`, scopes it, keeps one `<style data-tiptap-theme>` in the document head, puts `obsidian-theme theme-dark|theme-light`\n * on `<html>`) and follows the app's colour mode.\n * Reference-counted per app: the first call starts the runtime, every call returns an idempotent release, the last release\n * stops it (removing the style element and the root classes). Surfaces of one app share a single runtime.\n */\nexport function startObsidianTheme(ctx: FeatureContext): () => void {\n const key: object = ctx.settings\n let entry = runtimes.get(key)\n if (!entry) {\n entry = { count: 0, stop: createRuntime(ctx) }\n runtimes.set(key, entry)\n }\n entry.count++\n const held = entry\n let released = false\n return () => {\n if (released) return\n released = true\n if (--held.count > 0) return\n held.stop()\n if (runtimes.get(key) === held) runtimes.delete(key)\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { metadataOf } from "../../core/metadata.js";
|
|
2
2
|
import { METADATA_LIST } from "../../core/metadata-types.js";
|
|
3
|
+
import { formatSegmentTime } from "./segment-node.js";
|
|
3
4
|
import { useEditorI18n } from "../../vue/use-editor-i18n.js";
|
|
4
5
|
import _sfc_main$1 from "../../vue/components/SuggestionList.js";
|
|
5
|
-
import { computed, createBlock, createCommentVNode, createElementVNode, createVNode, defineComponent, normalizeClass, onBeforeUnmount, openBlock, ref, unref, useTemplateRef, watch, withCtx } from "vue";
|
|
6
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, onBeforeUnmount, openBlock, ref, toDisplayString, unref, useTemplateRef, watch, withCtx, withModifiers } from "vue";
|
|
6
7
|
import { NodeViewContent, NodeViewWrapper, nodeViewProps } from "@tiptap/vue-3";
|
|
7
8
|
//#region src/features/transcript/TranscriptSegmentView.vue
|
|
8
9
|
const _hoisted_1 = { class: "grid grid-cols-[5.5rem_1fr] items-start gap-2" };
|
|
@@ -18,6 +19,7 @@ const _hoisted_3 = [
|
|
|
18
19
|
"aria-expanded",
|
|
19
20
|
"readonly"
|
|
20
21
|
];
|
|
22
|
+
const _hoisted_4 = ["title", "aria-label"];
|
|
21
23
|
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
22
24
|
__name: "TranscriptSegmentView",
|
|
23
25
|
props: nodeViewProps,
|
|
@@ -125,39 +127,57 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
125
127
|
props.editor.commands.focus(props.getPos() + props.node.nodeSize - 1);
|
|
126
128
|
}
|
|
127
129
|
}
|
|
130
|
+
/** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */
|
|
131
|
+
function seek() {
|
|
132
|
+
const start = props.node.attrs.start;
|
|
133
|
+
const pos = props.getPos();
|
|
134
|
+
if (typeof start === "number" && pos !== void 0) props.editor.commands.seekSource?.(pos, start);
|
|
135
|
+
}
|
|
128
136
|
return (_ctx, _cache) => {
|
|
129
137
|
return openBlock(), createBlock(unref(NodeViewWrapper), {
|
|
130
138
|
class: normalizeClass(["transcript-segment my-2", { "is-selected rounded-md ring-1 ring-[var(--sk-primary)]": _ctx.selected }]),
|
|
131
139
|
"data-type": "transcript-segment",
|
|
132
140
|
"data-id": _ctx.node.attrs.id || void 0
|
|
133
141
|
}, {
|
|
134
|
-
default: withCtx(() => [createElementVNode("div", _hoisted_1, [createElementVNode("div", _hoisted_2, [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
|
|
142
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_1, [createElementVNode("div", _hoisted_2, [
|
|
143
|
+
createElementVNode("input", {
|
|
144
|
+
value: draft.value,
|
|
145
|
+
title: draft.value || void 0,
|
|
146
|
+
placeholder: unref(t)("transcript.speaker"),
|
|
147
|
+
"aria-label": unref(t)("transcript.speaker"),
|
|
148
|
+
autocomplete: "off",
|
|
149
|
+
role: "combobox",
|
|
150
|
+
"aria-expanded": open.value && items.value.length > 0,
|
|
151
|
+
readonly: readonly.value,
|
|
152
|
+
class: "w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]",
|
|
153
|
+
onFocus,
|
|
154
|
+
onBlur,
|
|
155
|
+
onMousedown: _cache[0] || (_cache[0] = ($event) => openList("")),
|
|
156
|
+
onInput,
|
|
157
|
+
onKeydown: onKeyDown
|
|
158
|
+
}, null, 40, _hoisted_3),
|
|
159
|
+
_ctx.node.attrs.start != null ? (openBlock(), createElementBlock("button", {
|
|
160
|
+
key: 0,
|
|
161
|
+
type: "button",
|
|
162
|
+
class: "transcript-segment__time block rounded px-1 text-xs tabular-nums text-[var(--sk-muted)] hover:text-[var(--sk-primary)] hover:underline",
|
|
163
|
+
title: unref(t)("transcript.seek", { time: unref(formatSegmentTime)(_ctx.node.attrs.start) }),
|
|
164
|
+
"aria-label": unref(t)("transcript.seek", { time: unref(formatSegmentTime)(_ctx.node.attrs.start) }),
|
|
165
|
+
onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"])),
|
|
166
|
+
onClick: seek
|
|
167
|
+
}, toDisplayString(unref(formatSegmentTime)(_ctx.node.attrs.start)), 41, _hoisted_4)) : createCommentVNode("v-if", true),
|
|
168
|
+
open.value && items.value.length ? (openBlock(), createBlock(_sfc_main$1, {
|
|
169
|
+
key: 1,
|
|
170
|
+
ref_key: "list",
|
|
171
|
+
ref: list,
|
|
172
|
+
class: "absolute top-full left-0 z-50 mt-1",
|
|
173
|
+
items: items.value,
|
|
174
|
+
display: (item) => ({ label: item.label }),
|
|
175
|
+
command: pick,
|
|
176
|
+
loading: false,
|
|
177
|
+
"loading-text": "",
|
|
178
|
+
"empty-text": ""
|
|
179
|
+
}, null, 8, ["items", "display"])) : createCommentVNode("v-if", true)
|
|
180
|
+
]), createVNode(unref(NodeViewContent), { class: "transcript-segment__text" })])]),
|
|
161
181
|
_: 1
|
|
162
182
|
}, 8, ["class", "data-id"]);
|
|
163
183
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranscriptSegmentView.js","names":["selected","node","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","SuggestionList","_createCommentVNode","_createVNode"],"sources":["../../../src/features/transcript/TranscriptSegmentView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewContent, NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { computed, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../core'\nimport SuggestionList from '../../vue/components/SuggestionList.vue'\nimport { useEditorI18n } from '../../vue/use-editor-i18n'\n\nconst props = defineProps(nodeViewProps)\nconst { t } = useEditorI18n()\n\nconst speaker = computed(() => String(props.node.attrs.speaker ?? ''))\n// Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n// speaker list is only extended with whole names, not with every prefix typed on the way.\nconst draft = ref(speaker.value)\nconst focused = ref(false)\nwatch(speaker, (value) => {\n if (!focused.value) draft.value = value\n})\n// `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\nconst readonly = ref(false)\n\nconst open = ref(false)\nconst items = ref<MetadataItem[]>([])\nconst list = useTemplateRef<InstanceType<typeof SuggestionList>>('list')\n\n// Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet pending: AbortController | undefined\nfunction scheduleSearch(query: string) {\n clearTimeout(timer)\n timer = setTimeout(async () => {\n pending?.abort()\n const controller = pending = new AbortController()\n try {\n const found = await metadataOf(props.editor).search(METADATA_LIST.speakers, { query, editor: props.editor, signal: controller.signal })\n if (!controller.signal.aborted) items.value = found.filter(item => item.label.trim())\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n}\nonBeforeUnmount(() => {\n clearTimeout(timer)\n pending?.abort()\n})\n\nfunction openList(query: string) {\n if (!props.editor.isEditable) return\n open.value = true\n scheduleSearch(query)\n}\n\nfunction closeList() {\n open.value = false\n clearTimeout(timer)\n pending?.abort()\n}\n\nfunction onFocus() {\n focused.value = true\n readonly.value = !props.editor.isEditable\n}\n\nfunction onInput(e: Event) {\n draft.value = (e.target as HTMLInputElement).value\n openList(draft.value)\n}\n\n/** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\nasync function commit(name = draft.value.trim()) {\n draft.value = name\n if (readonly.value || name === speaker.value) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(props.editor).search(METADATA_LIST.speakers, { query: name, editor: props.editor }) : null\n props.updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(props.editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n}\n\nfunction pick(item: unknown) {\n closeList()\n void commit((item as MetadataItem).label)\n}\n\nfunction onBlur() {\n focused.value = false\n closeList()\n void commit()\n}\n\n/** The speaker field of the segment `step` places before/after this one (document order), if any. */\nfunction neighbourField(input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined {\n const fields = [...props.editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n}\n\n// An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n// speaker and Enter continues in this segment's text.\nfunction onKeyDown(e: KeyboardEvent) {\n if (open.value && items.value.length && list.value?.onKeyDown(e)) {\n e.preventDefault()\n return\n }\n const input = e.target as HTMLInputElement\n if (e.key === 'Escape' && open.value) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n props.editor.commands.focus(props.getPos()! + props.node.nodeSize - 1)\n }\n}\n</script>\n\n<template>\n <NodeViewWrapper\n :class=\"['transcript-segment my-2', { 'is-selected rounded-md ring-1 ring-[var(--sk-primary)]': selected }]\"\n data-type=\"transcript-segment\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div class=\"transcript-segment__speaker relative pt-0.5\" contenteditable=\"false\">\n <input\n :value=\"draft\"\n :title=\"draft || undefined\"\n :placeholder=\"t('transcript.speaker')\"\n :aria-label=\"t('transcript.speaker')\"\n autocomplete=\"off\"\n role=\"combobox\"\n :aria-expanded=\"open && items.length > 0\"\n :readonly=\"readonly\"\n class=\"w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @mousedown=\"openList('')\"\n @input=\"onInput\"\n @keydown=\"onKeyDown\"\n >\n <SuggestionList\n v-if=\"open && items.length\"\n ref=\"list\"\n class=\"absolute top-full left-0 z-50 mt-1\"\n :items=\"items\"\n :display=\"item => ({ label: (item as MetadataItem).label })\"\n :command=\"pick\"\n :loading=\"false\"\n loading-text=\"\"\n empty-text=\"\"\n />\n </div>\n <NodeViewContent class=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;EAOA,MAAM,QAAQ;EACd,MAAM,EAAE,MAAM,cAAc;EAE5B,MAAM,UAAU,eAAe,OAAO,MAAM,KAAK,MAAM,WAAW,EAAE,CAAC;EAGrE,MAAM,QAAQ,IAAI,QAAQ,KAAK;EAC/B,MAAM,UAAU,IAAI,KAAK;EACzB,MAAM,UAAU,UAAU;GACxB,IAAI,CAAC,QAAQ,OAAO,MAAM,QAAQ;EACpC,CAAC;EAED,MAAM,WAAW,IAAI,KAAK;EAE1B,MAAM,OAAO,IAAI,KAAK;EACtB,MAAM,QAAQ,IAAoB,CAAC,CAAC;EACpC,MAAM,OAAO,eAAoD,MAAM;EAGvE,IAAI;EACJ,IAAI;EACJ,SAAS,eAAe,OAAe;GACrC,aAAa,KAAK;GAClB,QAAQ,WAAW,YAAY;IAC7B,SAAS,MAAM;IACf,MAAM,aAAa,UAAU,IAAI,gBAAgB;IACjD,IAAI;KACF,MAAM,QAAQ,MAAM,WAAW,MAAM,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;MAAE;MAAO,QAAQ,MAAM;MAAQ,QAAQ,WAAW;KAAO,CAAC;KACtI,IAAI,CAAC,WAAW,OAAO,SAAS,MAAM,QAAQ,MAAM,QAAO,SAAQ,KAAK,MAAM,KAAK,CAAC;IACtF,SACO,OAAO;KACZ,IAAI,CAAC,WAAW,OAAO,SAAS,QAAQ,KAAK,oDAAoD,KAAK;IACxG;GACF,GAAG,GAAG;EACR;EACA,sBAAsB;GACpB,aAAa,KAAK;GAClB,SAAS,MAAM;EACjB,CAAC;EAED,SAAS,SAAS,OAAe;GAC/B,IAAI,CAAC,MAAM,OAAO,YAAY;GAC9B,KAAK,QAAQ;GACb,eAAe,KAAK;EACtB;EAEA,SAAS,YAAY;GACnB,KAAK,QAAQ;GACb,aAAa,KAAK;GAClB,SAAS,MAAM;EACjB;EAEA,SAAS,UAAU;GACjB,QAAQ,QAAQ;GAChB,SAAS,QAAQ,CAAC,MAAM,OAAO;EACjC;EAEA,SAAS,QAAQ,GAAU;GACzB,MAAM,QAAS,EAAE,OAA4B;GAC7C,SAAS,MAAM,KAAK;EACtB;;EAGA,eAAe,OAAO,OAAO,MAAM,MAAM,KAAK,GAAG;GAC/C,MAAM,QAAQ;GACd,IAAI,SAAS,SAAS,SAAS,QAAQ,OAAO;GAG9C,MAAM,QAAQ,OAAO,WAAW,MAAM,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;IAAE,OAAO;IAAM,QAAQ,MAAM;GAAO,CAAC,IAAI;GACtH,MAAM,iBAAiB,EAAE,SAAS,KAAK,CAAC;GACxC,IAAI,CAAC,OAAO;GACZ,IAAI,EAAE,MAAM,MAAA,CAAO,MAAK,SAAQ,KAAK,MAAM,YAAY,MAAM,KAAK,YAAY,CAAC,GAC7E,WAAW,MAAM,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;IAAE,IAAI;IAAM,OAAO;GAAK,CAAC;EAErF;EAEA,SAAS,KAAK,MAAe;GAC3B,UAAU;GACV,OAAa,KAAsB,KAAK;EAC1C;EAEA,SAAS,SAAS;GAChB,QAAQ,QAAQ;GAChB,UAAU;GACV,OAAY;EACd;;EAGA,SAAS,eAAe,OAAyB,MAA4C;GAC3F,MAAM,SAAS,CAAC,GAAG,MAAM,OAAO,KAAK,IAAI,iBAAmC,oCAAoC,CAAC;GACjH,OAAO,OAAO,OAAO,QAAQ,KAAK,IAAI;EACxC;EAIA,SAAS,UAAU,GAAkB;GACnC,IAAI,KAAK,SAAS,MAAM,MAAM,UAAU,KAAK,OAAO,UAAU,CAAC,GAAG;IAChE,EAAE,eAAe;IACjB;GACF;GACA,MAAM,QAAQ,EAAE;GAChB,IAAI,EAAE,QAAQ,YAAY,KAAK,OAAO;IACpC,EAAE,eAAe;IACjB,UAAU;GACZ,OACK,IAAI,EAAE,QAAQ,aAAa,EAAE,QAAQ,aAAa;IACrD,MAAM,OAAO,eAAe,OAAO,EAAE,QAAQ,YAAY,KAAK,CAAC;IAC/D,IAAI,CAAC,MAAM;IACX,EAAE,eAAe;IACjB,KAAK,MAAM;IACX,KAAK,kBAAkB,KAAK,MAAM,QAAQ,KAAK,MAAM,MAAM;GAC7D,OACK,IAAI,EAAE,QAAQ,SAAS;IAC1B,EAAE,eAAe;IACjB,MAAM,KAAK;IACX,MAAM,OAAO,SAAS,MAAM,MAAM,OAAO,IAAK,MAAM,KAAK,WAAW,CAAC;GACvE;EACF;;GAIE,OAAAE,UAAA,GAAAC,YAqCkBC,MAAA,eAAA,GAAA;IApCf,OAAKC,eAAA,CAAA,2BAAA,EAAA,0DAA0FL,KAAAA,SAAQ,CAAA,CAAA;IACxG,aAAU;IACT,WAASC,KAAAA,KAAK,MAAM,MAAM,KAAA;;IAE3B,SAAAK,cA+BM,CA/BNC,mBA+BM,OA/BN,YA+BM,CA9BJA,mBA4BM,OA5BN,YA4BM,CA3BJA,mBAeC,SAAA;KAdE,OAAO,MAAA;KACP,OAAO,MAAA,SAAS,KAAA;KAChB,aAAaH,MAAA,CAAA,CAAC,CAAA,oBAAA;KACd,cAAYA,MAAA,CAAA,CAAC,CAAA,oBAAA;KACd,cAAa;KACb,MAAK;KACJ,iBAAe,KAAA,SAAQ,MAAA,MAAM,SAAM;KACnC,UAAU,SAAA;KACX,OAAM;KACE;KACD;KACN,aAAS,OAAA,OAAA,OAAA,MAAA,WAAE,SAAQ,EAAA;KACZ;KACP,WAAS;IAGJ,GAAA,MAAA,IAAA,UAAA,GAAA,KAAA,SAAQ,MAAA,MAAM,UADtBF,UAAA,GAAAC,YAUEK,aAAA;;KARI,SAAA;KAAJ,KAAI;KACJ,OAAM;KACL,OAAO,MAAA;KACP,UAAS,UAAI,EAAA,OAAe,KAAsB,MAAK;KACvD,SAAS;KACT,SAAS;KACV,gBAAa;KACb,cAAW;IAGf,GAAA,MAAA,GAAA,CAAA,SAAA,SAAA,CAAA,KAAAC,mBAAA,QAAA,IAAA,CAAA,CAAA,GAAAC,YAAoDN,MAAA,eAAA,GAAA,EAAnC,OAAM,2BAA0B,CAAA,CAAA,CAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"TranscriptSegmentView.js","names":["selected","node","_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createElementVNode","_createElementBlock","_withModifiers","_toDisplayString","_createCommentVNode","SuggestionList","_createVNode"],"sources":["../../../src/features/transcript/TranscriptSegmentView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { NodeViewContent, NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'\nimport { computed, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'\nimport { METADATA_LIST, type MetadataItem, metadataOf } from '../../core'\nimport SuggestionList from '../../vue/components/SuggestionList.vue'\nimport { useEditorI18n } from '../../vue/use-editor-i18n'\nimport { formatSegmentTime } from './segment-node'\n\nconst props = defineProps(nodeViewProps)\nconst { t } = useEditorI18n()\n\nconst speaker = computed(() => String(props.node.attrs.speaker ?? ''))\n// Typed text is a draft until committed (blur, Enter, picking a suggestion): one undo step per name, and the\n// speaker list is only extended with whole names, not with every prefix typed on the way.\nconst draft = ref(speaker.value)\nconst focused = ref(false)\nwatch(speaker, (value) => {\n if (!focused.value) draft.value = value\n})\n// `editor.isEditable` is not reactive, so a read-only editor is detected when the input gets focus.\nconst readonly = ref(false)\n\nconst open = ref(false)\nconst items = ref<MetadataItem[]>([])\nconst list = useTemplateRef<InstanceType<typeof SuggestionList>>('list')\n\n// Same loading as the composite tags: 150 ms after typing pauses, a newer search aborts the older one (sources may be remote).\nlet timer: ReturnType<typeof setTimeout> | undefined\nlet pending: AbortController | undefined\nfunction scheduleSearch(query: string) {\n clearTimeout(timer)\n timer = setTimeout(async () => {\n pending?.abort()\n const controller = pending = new AbortController()\n try {\n const found = await metadataOf(props.editor).search(METADATA_LIST.speakers, { query, editor: props.editor, signal: controller.signal })\n if (!controller.signal.aborted) items.value = found.filter(item => item.label.trim())\n }\n catch (error) {\n if (!controller.signal.aborted) console.warn('Sprecher-Vorschläge konnten nicht geladen werden', error)\n }\n }, 150)\n}\nonBeforeUnmount(() => {\n clearTimeout(timer)\n pending?.abort()\n})\n\nfunction openList(query: string) {\n if (!props.editor.isEditable) return\n open.value = true\n scheduleSearch(query)\n}\n\nfunction closeList() {\n open.value = false\n clearTimeout(timer)\n pending?.abort()\n}\n\nfunction onFocus() {\n focused.value = true\n readonly.value = !props.editor.isEditable\n}\n\nfunction onInput(e: Event) {\n draft.value = (e.target as HTMLInputElement).value\n openList(draft.value)\n}\n\n/** Writes the draft to the node; a name the speaker list does not know yet is added to it (reported via `metadataCreate`). */\nasync function commit(name = draft.value.trim()) {\n draft.value = name\n if (readonly.value || name === speaker.value) return\n // Searched before the node changes: `search` calls the source synchronously, so a list derived from the document\n // (the default `speakers`) does not already contain the name just typed here.\n const found = name ? metadataOf(props.editor).search(METADATA_LIST.speakers, { query: name, editor: props.editor }) : null\n props.updateAttributes({ speaker: name })\n if (!found) return\n if (!(await found).some(item => item.label.toLowerCase() === name.toLowerCase())) {\n metadataOf(props.editor).create(METADATA_LIST.speakers, { id: name, label: name })\n }\n}\n\nfunction pick(item: unknown) {\n closeList()\n void commit((item as MetadataItem).label)\n}\n\nfunction onBlur() {\n focused.value = false\n closeList()\n void commit()\n}\n\n/** The speaker field of the segment `step` places before/after this one (document order), if any. */\nfunction neighbourField(input: HTMLInputElement, step: -1 | 1): HTMLInputElement | undefined {\n const fields = [...props.editor.view.dom.querySelectorAll<HTMLInputElement>('.transcript-segment__speaker input')]\n return fields[fields.indexOf(input) + step]\n}\n\n// An open list takes ArrowUp/ArrowDown/Enter/Tab; otherwise ArrowUp/ArrowDown move to the neighbouring segment's\n// speaker and Enter continues in this segment's text.\nfunction onKeyDown(e: KeyboardEvent) {\n if (open.value && items.value.length && list.value?.onKeyDown(e)) {\n e.preventDefault()\n return\n }\n const input = e.target as HTMLInputElement\n if (e.key === 'Escape' && open.value) {\n e.preventDefault()\n closeList()\n }\n else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const next = neighbourField(input, e.key === 'ArrowUp' ? -1 : 1)\n if (!next) return\n e.preventDefault()\n next.focus()\n next.setSelectionRange(next.value.length, next.value.length)\n }\n else if (e.key === 'Enter') {\n e.preventDefault()\n input.blur()\n props.editor.commands.focus(props.getPos()! + props.node.nodeSize - 1)\n }\n}\n\n/** Seeks and plays the source recording of the enclosing Baustein (a no-op without the composites feature or a source). */\nfunction seek() {\n const start = props.node.attrs.start\n const pos = props.getPos()\n // `seekSource` is contributed by the composites feature; without it the editor has no command and the click does nothing.\n if (typeof start === 'number' && pos !== undefined) props.editor.commands.seekSource?.(pos, start)\n}\n</script>\n\n<template>\n <NodeViewWrapper\n :class=\"['transcript-segment my-2', { 'is-selected rounded-md ring-1 ring-[var(--sk-primary)]': selected }]\"\n data-type=\"transcript-segment\"\n :data-id=\"node.attrs.id || undefined\"\n >\n <div class=\"grid grid-cols-[5.5rem_1fr] items-start gap-2\">\n <div class=\"transcript-segment__speaker relative pt-0.5\" contenteditable=\"false\">\n <input\n :value=\"draft\"\n :title=\"draft || undefined\"\n :placeholder=\"t('transcript.speaker')\"\n :aria-label=\"t('transcript.speaker')\"\n autocomplete=\"off\"\n role=\"combobox\"\n :aria-expanded=\"open && items.length > 0\"\n :readonly=\"readonly\"\n class=\"w-full truncate rounded bg-transparent px-1 text-sm text-[var(--sk-muted)] outline-none placeholder:text-[var(--sk-fg-dimmed)] focus:bg-[color-mix(in_oklab,var(--sk-bg-accented)_50%,transparent)] focus:text-[var(--sk-fg-highlighted)]\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n @mousedown=\"openList('')\"\n @input=\"onInput\"\n @keydown=\"onKeyDown\"\n >\n <button\n v-if=\"node.attrs.start != null\"\n type=\"button\"\n class=\"transcript-segment__time block rounded px-1 text-xs tabular-nums text-[var(--sk-muted)] hover:text-[var(--sk-primary)] hover:underline\"\n :title=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n :aria-label=\"t('transcript.seek', { time: formatSegmentTime(node.attrs.start) })\"\n @mousedown.prevent\n @click=\"seek\"\n >\n {{ formatSegmentTime(node.attrs.start) }}\n </button>\n <SuggestionList\n v-if=\"open && items.length\"\n ref=\"list\"\n class=\"absolute top-full left-0 z-50 mt-1\"\n :items=\"items\"\n :display=\"item => ({ label: (item as MetadataItem).label })\"\n :command=\"pick\"\n :loading=\"false\"\n loading-text=\"\"\n empty-text=\"\"\n />\n </div>\n <NodeViewContent class=\"transcript-segment__text\" />\n </div>\n </NodeViewWrapper>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;EAQA,MAAM,QAAQ;EACd,MAAM,EAAE,MAAM,cAAc;EAE5B,MAAM,UAAU,eAAe,OAAO,MAAM,KAAK,MAAM,WAAW,EAAE,CAAC;EAGrE,MAAM,QAAQ,IAAI,QAAQ,KAAK;EAC/B,MAAM,UAAU,IAAI,KAAK;EACzB,MAAM,UAAU,UAAU;GACxB,IAAI,CAAC,QAAQ,OAAO,MAAM,QAAQ;EACpC,CAAC;EAED,MAAM,WAAW,IAAI,KAAK;EAE1B,MAAM,OAAO,IAAI,KAAK;EACtB,MAAM,QAAQ,IAAoB,CAAC,CAAC;EACpC,MAAM,OAAO,eAAoD,MAAM;EAGvE,IAAI;EACJ,IAAI;EACJ,SAAS,eAAe,OAAe;GACrC,aAAa,KAAK;GAClB,QAAQ,WAAW,YAAY;IAC7B,SAAS,MAAM;IACf,MAAM,aAAa,UAAU,IAAI,gBAAgB;IACjD,IAAI;KACF,MAAM,QAAQ,MAAM,WAAW,MAAM,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;MAAE;MAAO,QAAQ,MAAM;MAAQ,QAAQ,WAAW;KAAO,CAAC;KACtI,IAAI,CAAC,WAAW,OAAO,SAAS,MAAM,QAAQ,MAAM,QAAO,SAAQ,KAAK,MAAM,KAAK,CAAC;IACtF,SACO,OAAO;KACZ,IAAI,CAAC,WAAW,OAAO,SAAS,QAAQ,KAAK,oDAAoD,KAAK;IACxG;GACF,GAAG,GAAG;EACR;EACA,sBAAsB;GACpB,aAAa,KAAK;GAClB,SAAS,MAAM;EACjB,CAAC;EAED,SAAS,SAAS,OAAe;GAC/B,IAAI,CAAC,MAAM,OAAO,YAAY;GAC9B,KAAK,QAAQ;GACb,eAAe,KAAK;EACtB;EAEA,SAAS,YAAY;GACnB,KAAK,QAAQ;GACb,aAAa,KAAK;GAClB,SAAS,MAAM;EACjB;EAEA,SAAS,UAAU;GACjB,QAAQ,QAAQ;GAChB,SAAS,QAAQ,CAAC,MAAM,OAAO;EACjC;EAEA,SAAS,QAAQ,GAAU;GACzB,MAAM,QAAS,EAAE,OAA4B;GAC7C,SAAS,MAAM,KAAK;EACtB;;EAGA,eAAe,OAAO,OAAO,MAAM,MAAM,KAAK,GAAG;GAC/C,MAAM,QAAQ;GACd,IAAI,SAAS,SAAS,SAAS,QAAQ,OAAO;GAG9C,MAAM,QAAQ,OAAO,WAAW,MAAM,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;IAAE,OAAO;IAAM,QAAQ,MAAM;GAAO,CAAC,IAAI;GACtH,MAAM,iBAAiB,EAAE,SAAS,KAAK,CAAC;GACxC,IAAI,CAAC,OAAO;GACZ,IAAI,EAAE,MAAM,MAAA,CAAO,MAAK,SAAQ,KAAK,MAAM,YAAY,MAAM,KAAK,YAAY,CAAC,GAC7E,WAAW,MAAM,MAAM,CAAC,CAAC,OAAO,cAAc,UAAU;IAAE,IAAI;IAAM,OAAO;GAAK,CAAC;EAErF;EAEA,SAAS,KAAK,MAAe;GAC3B,UAAU;GACV,OAAa,KAAsB,KAAK;EAC1C;EAEA,SAAS,SAAS;GAChB,QAAQ,QAAQ;GAChB,UAAU;GACV,OAAY;EACd;;EAGA,SAAS,eAAe,OAAyB,MAA4C;GAC3F,MAAM,SAAS,CAAC,GAAG,MAAM,OAAO,KAAK,IAAI,iBAAmC,oCAAoC,CAAC;GACjH,OAAO,OAAO,OAAO,QAAQ,KAAK,IAAI;EACxC;EAIA,SAAS,UAAU,GAAkB;GACnC,IAAI,KAAK,SAAS,MAAM,MAAM,UAAU,KAAK,OAAO,UAAU,CAAC,GAAG;IAChE,EAAE,eAAe;IACjB;GACF;GACA,MAAM,QAAQ,EAAE;GAChB,IAAI,EAAE,QAAQ,YAAY,KAAK,OAAO;IACpC,EAAE,eAAe;IACjB,UAAU;GACZ,OACK,IAAI,EAAE,QAAQ,aAAa,EAAE,QAAQ,aAAa;IACrD,MAAM,OAAO,eAAe,OAAO,EAAE,QAAQ,YAAY,KAAK,CAAC;IAC/D,IAAI,CAAC,MAAM;IACX,EAAE,eAAe;IACjB,KAAK,MAAM;IACX,KAAK,kBAAkB,KAAK,MAAM,QAAQ,KAAK,MAAM,MAAM;GAC7D,OACK,IAAI,EAAE,QAAQ,SAAS;IAC1B,EAAE,eAAe;IACjB,MAAM,KAAK;IACX,MAAM,OAAO,SAAS,MAAM,MAAM,OAAO,IAAK,MAAM,KAAK,WAAW,CAAC;GACvE;EACF;;EAGA,SAAS,OAAO;GACd,MAAM,QAAQ,MAAM,KAAK,MAAM;GAC/B,MAAM,MAAM,MAAM,OAAO;GAEzB,IAAI,OAAO,UAAU,YAAY,QAAQ,KAAA,GAAW,MAAM,OAAO,SAAS,aAAa,KAAK,KAAK;EACnG;;GAIE,OAAAE,UAAA,GAAAC,YAgDkBC,MAAA,eAAA,GAAA;IA/Cf,OAAKC,eAAA,CAAA,2BAAA,EAAA,0DAA0FL,KAAAA,SAAQ,CAAA,CAAA;IACxG,aAAU;IACT,WAASC,KAAAA,KAAK,MAAM,MAAM,KAAA;;IAE3B,SAAAK,cA0CM,CA1CNC,mBA0CM,OA1CN,YA0CM,CAzCJA,mBAuCM,OAvCN,YAuCM;KAtCJA,mBAeC,SAAA;MAdE,OAAO,MAAA;MACP,OAAO,MAAA,SAAS,KAAA;MAChB,aAAaH,MAAA,CAAA,CAAC,CAAA,oBAAA;MACd,cAAYA,MAAA,CAAA,CAAC,CAAA,oBAAA;MACd,cAAa;MACb,MAAK;MACJ,iBAAe,KAAA,SAAQ,MAAA,MAAM,SAAM;MACnC,UAAU,SAAA;MACX,OAAM;MACE;MACD;MACN,aAAS,OAAA,OAAA,OAAA,MAAA,WAAE,SAAQ,EAAA;MACZ;MACP,WAAS;;KAGJH,KAAAA,KAAK,MAAM,SAAK,QADxBC,UAAA,GAAAM,mBAUS,UAAA;;MARP,MAAK;MACL,OAAM;MACL,OAAOJ,MAAA,CAAA,CAAC,CAAA,mBAAA,EAAA,MAA4BA,MAAA,iBAAA,CAAiB,CAACH,KAAAA,KAAK,MAAM,KAAK,EAAA,CAAA;MACtE,cAAYG,MAAA,CAAA,CAAC,CAAA,mBAAA,EAAA,MAA4BA,MAAA,iBAAA,CAAiB,CAACH,KAAAA,KAAK,MAAM,KAAK,EAAA,CAAA;MAC3E,aAAS,OAAA,OAAA,OAAA,KAAAQ,oBAAV,CAAA,GAAkB,CAAA,SAAA,CAAA;MACjB,SAAO;KAEL,GAAAC,gBAAAN,MAAA,iBAAA,CAAiB,CAACH,KAAAA,KAAK,MAAM,KAAK,CAAA,GAAA,IAAA,UAAA,KAAAU,mBAAA,QAAA,IAAA;KAG/B,KAAA,SAAQ,MAAA,MAAM,UADtBT,UAAA,GAAAC,YAUES,aAAA;;MARI,SAAA;MAAJ,KAAI;MACJ,OAAM;MACL,OAAO,MAAA;MACP,UAAS,UAAI,EAAA,OAAe,KAAsB,MAAK;MACvD,SAAS;MACT,SAAS;MACV,gBAAa;MACb,cAAW;;IAGf,CAAA,GAAAC,YAAoDT,MAAA,eAAA,GAAA,EAAnC,OAAM,2BAA0B,CAAA,CAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ABILITY } from "../../core/schema/abilities.js";
|
|
2
2
|
import { filterMetadataItems } from "../../core/metadata.js";
|
|
3
|
-
import {
|
|
3
|
+
import { command } from "../../core/commands.js";
|
|
4
4
|
import { speakersIn } from "./speakers.js";
|
|
5
5
|
import { TranscriptSegmentNode } from "./segment-node.js";
|
|
6
|
+
import { transcriptSchemaExtensions } from "./schema.js";
|
|
6
7
|
import { speakerConversions } from "./convert.js";
|
|
7
8
|
import { segmentSpeakerLines } from "./segment.js";
|
|
8
9
|
//#region src/features/transcript/feature.ts
|
|
@@ -26,25 +27,26 @@ function transcriptFeatureCore(ui) {
|
|
|
26
27
|
const TranscriptSegment = transcriptSegment(ui.nodeViews);
|
|
27
28
|
return {
|
|
28
29
|
id: "transcript",
|
|
30
|
+
dimension: "content",
|
|
31
|
+
schemaExtensions: transcriptSchemaExtensions,
|
|
29
32
|
extensions: () => [TranscriptSegment],
|
|
30
|
-
|
|
31
|
-
id: "
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}],
|
|
33
|
+
commands: (ctx) => [command({
|
|
34
|
+
id: "speaker",
|
|
35
|
+
toolbar: {
|
|
36
|
+
group: "inserts",
|
|
37
|
+
order: 10
|
|
38
|
+
},
|
|
39
|
+
icon: "i-lucide-speech",
|
|
40
|
+
label: ctx.t("transcript.toolbar.speaker"),
|
|
41
|
+
kbds: [
|
|
42
|
+
"meta",
|
|
43
|
+
"shift",
|
|
44
|
+
"s"
|
|
45
|
+
],
|
|
46
|
+
ability: ABILITY.transcript,
|
|
47
|
+
command: (c) => c.toggleTranscriptSegment(),
|
|
48
|
+
active: (e) => e.isActive("transcriptSegment")
|
|
49
|
+
})],
|
|
48
50
|
metadataDefaults: () => ({ speakers: ({ query, editor }) => filterMetadataItems(speakersIn(editor.state.doc).map((s) => ({
|
|
49
51
|
id: s,
|
|
50
52
|
label: s
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/transcript/feature.ts"],"sourcesContent":["import type { NodeViewRenderer } from '@tiptap/core'\nimport type { EditorFeature } from '../../core'\nimport {
|
|
1
|
+
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/transcript/feature.ts"],"sourcesContent":["import type { NodeViewRenderer } from '@tiptap/core'\nimport type { EditorFeature } from '../../core'\nimport { command, filterMetadataItems } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport { speakerConversions } from './convert'\nimport { segmentSpeakerLines } from './segment'\nimport { transcriptSchemaExtensions } from './schema'\nimport { TranscriptSegmentNode } from './segment-node'\nimport { speakersIn } from './speakers'\n\n/** What a framework binding supplies to the transcript feature: the node view of the speaker segment. */\nexport interface TranscriptUi {\n nodeViews: {\n transcriptSegment: NodeViewRenderer\n }\n}\n\n/**\n * The speaker segment node with the node view of a framework binding (`VueNodeViewRenderer(TranscriptSegmentView)`,\n * `ReactNodeViewRenderer(TranscriptSegmentView)`). TipTap's default `NodeView.stopEvent` already hands INPUT events to\n * the speaker field, not ProseMirror.\n */\nexport function transcriptSegment(nodeViews: TranscriptUi['nodeViews']) {\n return TranscriptSegmentNode.extend({\n // Explicit return type: keeps the declaration independent of the framework view's inferred types.\n addNodeView(): NodeViewRenderer {\n return nodeViews.transcriptSegment\n },\n })\n}\n\n/**\n * Speaker segments: the node with its speaker field, the „Sprecherblock“ toolbar button (Mod-Shift-S), the conversion\n * into speaker lines and a built-in `speakers` list (speakers already in the document, no backend). Contributes the\n * `segment` stage: `**Name:** text` lines of imported or AI-written Markdown become segments. The only framework part is\n * the node view (`ui.nodeViews`), so the descriptor carries no component slot.\n */\nexport function transcriptFeatureCore(ui: TranscriptUi): EditorFeature<never> {\n const TranscriptSegment = transcriptSegment(ui.nodeViews)\n return {\n id: 'transcript',\n dimension: 'content',\n schemaExtensions: transcriptSchemaExtensions,\n extensions: () => [TranscriptSegment],\n commands: ctx => [\n command({\n id: 'speaker',\n toolbar: { group: 'inserts', order: 10 },\n icon: 'i-lucide-speech',\n label: ctx.t('transcript.toolbar.speaker'),\n kbds: ['meta', 'shift', 's'],\n ability: ABILITY.transcript,\n command: c => c.toggleTranscriptSegment(),\n active: e => e.isActive('transcriptSegment'),\n }),\n ],\n metadataDefaults: () => ({\n speakers: ({ query, editor }) => filterMetadataItems(speakersIn(editor.state.doc).map(s => ({ id: s, label: s })), query),\n }),\n conversions: [speakerConversions],\n blocks: { segment: segmentSpeakerLines },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,SAAgB,kBAAkB,WAAsC;CACtE,OAAO,sBAAsB,OAAO,EAElC,cAAgC;EAC9B,OAAO,UAAU;CACnB,EACF,CAAC;AACH;;;;;;;AAQA,SAAgB,sBAAsB,IAAwC;CAC5E,MAAM,oBAAoB,kBAAkB,GAAG,SAAS;CACxD,OAAO;EACL,IAAI;EACJ,WAAW;EACX,kBAAkB;EAClB,kBAAkB,CAAC,iBAAiB;EACpC,WAAU,QAAO,CACf,QAAQ;GACN,IAAI;GACJ,SAAS;IAAE,OAAO;IAAW,OAAO;GAAG;GACvC,MAAM;GACN,OAAO,IAAI,EAAE,4BAA4B;GACzC,MAAM;IAAC;IAAQ;IAAS;GAAG;GAC3B,SAAS,QAAQ;GACjB,UAAS,MAAK,EAAE,wBAAwB;GACxC,SAAQ,MAAK,EAAE,SAAS,mBAAmB;EAC7C,CAAC,CACH;EACA,yBAAyB,EACvB,WAAW,EAAE,OAAO,aAAa,oBAAoB,WAAW,OAAO,MAAM,GAAG,CAAC,CAAC,KAAI,OAAM;GAAE,IAAI;GAAG,OAAO;EAAE,EAAE,GAAG,KAAK,EAC1H;EACA,aAAa,CAAC,kBAAkB;EAChC,QAAQ,EAAE,SAAS,oBAAoB;CACzC;AACF"}
|