@ecsplico/editor 2.1.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -1
- package/README.md +39 -156
- package/dist/adapters/comments/drizzle.d.ts +1 -1
- package/dist/adapters/comments/{client.js → rest-model.js} +3 -3
- package/dist/adapters/comments/rest-model.js.map +1 -0
- package/dist/adapters/comments/types.d.ts +2 -2
- package/dist/adapters/document-state.d.ts +23 -0
- package/dist/adapters/document-state.js +66 -0
- package/dist/adapters/document-state.js.map +1 -0
- package/dist/adapters/documents.js +14 -11
- package/dist/adapters/documents.js.map +1 -1
- package/dist/adapters/drizzle.d.ts +2 -2
- package/dist/adapters/drizzle.js +97 -88
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/adapters/forge.d.ts +12 -7
- package/dist/adapters/forge.js +38 -45
- package/dist/adapters/forge.js.map +1 -1
- package/dist/adapters/http.d.ts +20 -0
- package/dist/adapters/http.js +68 -0
- package/dist/adapters/http.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/json.d.ts +3 -3
- package/dist/adapters/json.js +58 -49
- package/dist/adapters/json.js.map +1 -1
- package/dist/adapters/memory.d.ts +21 -0
- package/dist/adapters/memory.js +53 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/nitro.d.ts +23 -20
- package/dist/adapters/nitro.js +58 -163
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/react.d.ts +55 -0
- package/dist/adapters/react.js +209 -0
- package/dist/adapters/react.js.map +1 -0
- package/dist/adapters/shared.d.ts +15 -21
- package/dist/adapters/shared.js +10 -8
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/vue.d.ts +15 -30
- package/dist/adapters/vue.js +36 -87
- package/dist/adapters/vue.js.map +1 -1
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.js +95 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/abilities.js +1 -0
- package/dist/core/abilities.js.map +1 -1
- package/dist/{adapters/routes.js → core/api-routes.js} +5 -3
- package/dist/core/api-routes.js.map +1 -0
- package/dist/core/backend-capability.d.ts +7 -0
- package/dist/core/backend-capability.js +17 -0
- package/dist/core/backend-capability.js.map +1 -0
- package/dist/core/backend-types.d.ts +245 -0
- package/dist/core/backend-types.js +17 -0
- package/dist/core/backend-types.js.map +1 -0
- package/dist/core/backend.d.ts +108 -0
- package/dist/core/backend.js +14 -0
- package/dist/core/backend.js.map +1 -0
- package/dist/core/block-convert.d.ts +7 -5
- package/dist/core/block-convert.js +28 -5
- package/dist/core/block-convert.js.map +1 -1
- package/dist/core/command-types.d.ts +89 -0
- package/dist/core/commands.d.ts +40 -0
- package/dist/core/commands.js +151 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/context.d.ts +17 -20
- package/dist/core/context.js +24 -21
- package/dist/core/context.js.map +1 -1
- package/dist/core/core-feature.d.ts +22 -7
- package/dist/core/core-feature.js +51 -18
- package/dist/core/core-feature.js.map +1 -1
- package/dist/core/define-editor.d.ts +4 -3
- package/dist/core/define-editor.js +14 -7
- package/dist/core/define-editor.js.map +1 -1
- package/dist/core/document-markdown.js +12 -3
- package/dist/core/document-markdown.js.map +1 -1
- package/dist/core/editor-ui.d.ts +8 -1
- package/dist/core/editor-ui.js +9 -8
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/extension-points.d.ts +3 -15
- package/dist/core/feature-context.d.ts +8 -7
- package/dist/core/feature-context.js +12 -11
- package/dist/core/feature-context.js.map +1 -1
- package/dist/core/feature.d.ts +35 -19
- package/dist/core/feature.js.map +1 -1
- package/dist/core/fences.d.ts +2 -1
- package/dist/core/fences.js +3 -2
- package/dist/core/fences.js.map +1 -1
- package/dist/core/fetch-error.d.ts +1 -1
- package/dist/core/fetch-error.js +3 -1
- package/dist/core/fetch-error.js.map +1 -1
- package/dist/core/hash.d.ts +5 -0
- package/dist/core/hash.js +11 -0
- package/dist/core/hash.js.map +1 -0
- package/dist/core/http-backend.d.ts +11 -0
- package/dist/core/http-backend.js +195 -0
- package/dist/core/http-backend.js.map +1 -0
- package/dist/core/index.d.ts +14 -9
- package/dist/core/index.js +12 -9
- package/dist/core/palette.d.ts +48 -0
- package/dist/core/palette.js +95 -0
- package/dist/core/palette.js.map +1 -0
- package/dist/core/schema/abilities.d.ts +2 -1
- package/dist/core/schema/abilities.js +5 -1
- package/dist/core/schema/abilities.js.map +1 -1
- package/dist/core/schema/index.d.ts +11 -5
- package/dist/core/schema/index.js +13 -8
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/schema/mark-flags.d.ts +8 -1
- package/dist/core/schema/of-features.d.ts +13 -0
- package/dist/core/schema/of-features.js +14 -0
- package/dist/core/schema/of-features.js.map +1 -0
- package/dist/core/slash.d.ts +8 -6
- package/dist/core/slash.js +24 -16
- package/dist/core/slash.js.map +1 -1
- package/dist/core/surface-session.js +13 -5
- package/dist/core/surface-session.js.map +1 -1
- package/dist/core/toolbar-items.d.ts +10 -4
- package/dist/core/toolbar-items.js +269 -195
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/core/util.js +11 -0
- package/dist/core/util.js.map +1 -0
- package/dist/document/DocumentSurface.d.ts +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/agent-op-review.d.ts +1 -1
- package/dist/features/ai/agent-op-review.js +1 -1
- package/dist/features/ai/agent-ops.js +1 -1
- package/dist/features/ai/ai-apply.d.ts +2 -2
- package/dist/features/ai/ai-apply.js +1 -1
- package/dist/features/ai/ai-review.js +1 -1
- package/dist/features/ai/ai-sse.d.ts +6 -6
- package/dist/features/ai/ai-sse.js +20 -63
- package/dist/features/ai/ai-sse.js.map +1 -1
- package/dist/features/ai/assistant.d.ts +8 -7
- package/dist/features/ai/assistant.js +3 -3
- package/dist/features/ai/assistant.js.map +1 -1
- package/dist/features/ai/block-actions.d.ts +3 -2
- package/dist/features/ai/block-actions.js +3 -2
- package/dist/features/ai/block-actions.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +1 -1
- package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/components/DocumentChatOps.vue.d.ts +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +8 -8
- package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/document-chat-model.d.ts +8 -7
- package/dist/features/ai/document-chat-model.js +5 -5
- package/dist/features/ai/document-chat-model.js.map +1 -1
- package/dist/features/ai/document-chat.js +1 -1
- package/dist/features/ai/feature.js +27 -14
- package/dist/features/ai/feature.js.map +1 -1
- package/dist/features/ai/ghost-autocomplete.d.ts +7 -6
- package/dist/features/ai/ghost-autocomplete.js +6 -9
- package/dist/features/ai/ghost-autocomplete.js.map +1 -1
- package/dist/features/ai/index.d.ts +4 -3
- package/dist/features/ai/index.js +10 -10
- package/dist/features/ai/model-profiles.d.ts +5 -4
- package/dist/features/ai/model-profiles.js +2 -2
- package/dist/features/ai/model-profiles.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.js +1 -1
- package/dist/features/ai/protocol/block-lines.js.map +1 -1
- package/dist/features/ai/protocol/chat.js +4 -1
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -2
- package/dist/features/ai/protocol/models.d.ts +2 -16
- package/dist/features/ai/protocol/models.js.map +1 -1
- package/dist/features/ai/rate-limit.d.ts +1 -1
- package/dist/features/ai/rate-limit.js +3 -1
- package/dist/features/ai/rate-limit.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js +2 -2
- package/dist/features/ai/react/AiSidebar.js +4 -4
- package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/react/DocumentChatOps.d.ts +1 -1
- package/dist/features/ai/react/DocumentChatOps.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +8 -10
- package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/react/use-ai-assistant.js +3 -3
- package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/react/use-document-chat.js +2 -2
- package/dist/features/ai/react/use-document-chat.js.map +1 -1
- package/dist/features/ai/react/use-model-profiles.d.ts +2 -1
- package/dist/features/ai/selection-flow.d.ts +1 -1
- package/dist/features/ai/selection-flow.js +1 -1
- package/dist/features/ai/sidebar-flow.d.ts +1 -1
- package/dist/features/ai/sidebar-flow.js +1 -1
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-ai-assistant.js +3 -3
- package/dist/features/ai/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/use-document-chat.js +2 -2
- package/dist/features/ai/use-document-chat.js.map +1 -1
- package/dist/features/ai/use-model-profiles.d.ts +2 -1
- package/dist/features/authors/attribution.d.ts +1 -1
- package/dist/features/authors/feature.js +12 -11
- package/dist/features/authors/feature.js.map +1 -1
- package/dist/features/block-ui/block-actions.d.ts +22 -1
- package/dist/features/block-ui/block-actions.js +26 -2
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/components/BlockActionsOverlay.js +33 -4
- package/dist/features/block-ui/components/BlockActionsOverlay.js.map +1 -1
- package/dist/features/block-ui/components/BlockActionsOverlay.vue.d.ts +4 -0
- package/dist/features/block-ui/components/BlockUi.js +1 -1
- package/dist/features/block-ui/feature.js +1 -0
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/index.d.ts +2 -2
- package/dist/features/block-ui/index.js +2 -2
- package/dist/features/block-ui/react/BlockActionsOverlay.d.ts +4 -0
- package/dist/features/block-ui/react/BlockActionsOverlay.js +38 -12
- package/dist/features/block-ui/react/BlockActionsOverlay.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.js +1 -1
- package/dist/features/collab/anchor-resolver.js +1 -1
- package/dist/features/collab/feature.js +2 -0
- package/dist/features/collab/feature.js.map +1 -1
- package/dist/features/comments/components/CommentThreadView.vue.d.ts +2 -1
- package/dist/features/comments/feature.js +12 -11
- package/dist/features/comments/feature.js.map +1 -1
- package/dist/features/comments/index.d.ts +3 -2
- package/dist/features/comments/index.js +1 -1
- package/dist/features/comments/margin-layout.d.ts +3 -2
- package/dist/features/comments/model.d.ts +3 -67
- package/dist/features/comments/model.js +2 -2
- package/dist/features/comments/model.js.map +1 -1
- package/dist/features/comments/react/CommentThreadView.d.ts +3 -2
- package/dist/features/comments/react/use-comments.d.ts +2 -1
- package/dist/features/comments/react.d.ts +1 -1
- package/dist/features/comments/schema.d.ts +2 -52
- package/dist/features/comments/schema.js.map +1 -1
- package/dist/features/comments/snapshot-equality.js.map +1 -1
- package/dist/features/comments/use-comments.d.ts +3 -2
- package/dist/features/comments/vue.d.ts +1 -1
- package/dist/features/composites/accepted-children.d.ts +1 -0
- package/dist/features/composites/accepted-children.js +16 -1
- package/dist/features/composites/accepted-children.js.map +1 -1
- package/dist/features/composites/ai-brief.js +31 -0
- package/dist/features/composites/ai-brief.js.map +1 -1
- package/dist/features/composites/bibliography-command.d.ts +11 -0
- package/dist/features/composites/bibliography-form.js +90 -0
- package/dist/features/composites/bibliography-form.js.map +1 -0
- package/dist/features/composites/bibliography.d.ts +33 -0
- package/dist/features/composites/bibliography.js +120 -0
- package/dist/features/composites/bibliography.js.map +1 -0
- package/dist/features/composites/components/BibliographyEditor.js +261 -0
- package/dist/features/composites/components/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/components/CompositeChrome.js +75 -26
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.js +46 -11
- package/dist/features/composites/components/CompositeDetails.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.vue.d.ts +2 -1
- package/dist/features/composites/components/CompositeFieldEditor.js +37 -4
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeInspector.js +17 -5
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/components/CompositeTable.js +13 -4
- package/dist/features/composites/components/CompositeTable.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/components/CompositeView.js +11 -1
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +103 -0
- package/dist/features/composites/components/DerivationBadge.js.map +1 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js +60 -0
- package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.js +122 -0
- package/dist/features/composites/components/ReferenceView.js.map +1 -0
- package/dist/features/composites/components/ReferenceView.vue.d.ts +96 -0
- package/dist/features/composites/components/SettingsEditingTab.js +1 -1
- package/dist/features/composites/convert.js +2 -2
- package/dist/features/composites/convert.js.map +1 -1
- package/dist/features/composites/derivation.d.ts +61 -0
- package/dist/features/composites/derivation.js +98 -0
- package/dist/features/composites/derivation.js.map +1 -0
- package/dist/features/composites/derived-chrome.js +69 -0
- package/dist/features/composites/derived-chrome.js.map +1 -0
- package/dist/features/composites/feature.js +159 -109
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/fences.d.ts +4 -3
- package/dist/features/composites/fences.js +51 -7
- package/dist/features/composites/fences.js.map +1 -1
- package/dist/features/composites/index.d.ts +12 -3
- package/dist/features/composites/index.js +15 -6
- package/dist/features/composites/inspector-model.d.ts +4 -1
- package/dist/features/composites/inspector-model.js +8 -4
- package/dist/features/composites/inspector-model.js.map +1 -1
- package/dist/features/composites/kinds.d.ts +8 -1
- package/dist/features/composites/kinds.js +145 -6
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/legacy.d.ts +4 -1
- package/dist/features/composites/legacy.js +56 -1
- package/dist/features/composites/legacy.js.map +1 -1
- package/dist/features/composites/meta-display.js +40 -1
- package/dist/features/composites/meta-display.js.map +1 -1
- package/dist/features/composites/meta-keys.d.ts +7 -0
- package/dist/features/composites/meta-keys.js +21 -0
- package/dist/features/composites/meta-keys.js.map +1 -0
- package/dist/features/composites/node-views.d.ts +6 -3
- package/dist/features/composites/node-views.js +7 -3
- package/dist/features/composites/node-views.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +6 -1
- package/dist/features/composites/nodes.js +33 -7
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/react/BibliographyEditor.js +229 -0
- package/dist/features/composites/react/BibliographyEditor.js.map +1 -0
- package/dist/features/composites/react/CompositeChrome.js +65 -17
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.d.ts +2 -1
- package/dist/features/composites/react/CompositeDetails.js +45 -6
- package/dist/features/composites/react/CompositeDetails.js.map +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.js +24 -1
- package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeInspector.js +11 -5
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.js +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/react/CompositeTable.js +16 -2
- package/dist/features/composites/react/CompositeTable.js.map +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js +2 -2
- package/dist/features/composites/react/CompositeView.js +6 -1
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +116 -0
- package/dist/features/composites/react/DerivationBadge.js.map +1 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js +52 -0
- package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -0
- package/dist/features/composites/react/ReferenceView.d.ts +11 -0
- package/dist/features/composites/react/ReferenceView.js +115 -0
- package/dist/features/composites/react/ReferenceView.js.map +1 -0
- package/dist/features/composites/react/SettingsEditingTab.js +2 -2
- package/dist/features/composites/react.d.ts +2 -1
- package/dist/features/composites/react.js +7 -3
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/reference-ai.js +16 -0
- package/dist/features/composites/reference-ai.js.map +1 -0
- package/dist/features/composites/reference-label.d.ts +19 -0
- package/dist/features/composites/reference-label.js +58 -0
- package/dist/features/composites/reference-label.js.map +1 -0
- package/dist/features/composites/reference-suggestion.d.ts +10 -0
- package/dist/features/composites/reference-suggestion.js +41 -0
- package/dist/features/composites/reference-suggestion.js.map +1 -0
- package/dist/features/composites/reference-text.d.ts +18 -0
- package/dist/features/composites/reference-text.js +89 -0
- package/dist/features/composites/reference-text.js.map +1 -0
- package/dist/features/composites/reference.d.ts +44 -0
- package/dist/features/composites/reference.js +148 -0
- package/dist/features/composites/reference.js.map +1 -0
- package/dist/features/composites/reveal-source.d.ts +32 -0
- package/dist/features/composites/reveal-source.js +89 -0
- package/dist/features/composites/reveal-source.js.map +1 -0
- package/dist/features/composites/rows.d.ts +21 -2
- package/dist/features/composites/rows.js +63 -9
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/features/composites/schema.d.ts +9 -3
- package/dist/features/composites/schema.js +11 -4
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/table-model.js +12 -21
- package/dist/features/composites/table-model.js.map +1 -1
- package/dist/features/composites/vue.d.ts +3 -2
- package/dist/features/composites/vue.js +8 -4
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/media/assets.d.ts +2 -0
- package/dist/features/media/assets.js +3 -2
- package/dist/features/media/assets.js.map +1 -1
- package/dist/features/media/components/FileView.js +13 -4
- package/dist/features/media/components/FileView.js.map +1 -1
- package/dist/features/media/feature.d.ts +3 -3
- package/dist/features/media/feature.js +21 -28
- package/dist/features/media/feature.js.map +1 -1
- package/dist/features/media/file-import.js +2 -2
- package/dist/features/media/file-import.js.map +1 -1
- package/dist/features/media/index.d.ts +2 -2
- package/dist/features/media/index.js +2 -2
- package/dist/features/media/react/FileView.js +19 -2
- package/dist/features/media/react/FileView.js.map +1 -1
- package/dist/features/media/schema.d.ts +8 -0
- package/dist/features/media/schema.js +126 -11
- package/dist/features/media/schema.js.map +1 -1
- package/dist/features/media/upload.d.ts +16 -14
- package/dist/features/media/upload.js +13 -19
- package/dist/features/media/upload.js.map +1 -1
- package/dist/features/mentions/index.d.ts +4 -3
- package/dist/features/mentions/index.js +4 -1
- package/dist/features/mentions/index.js.map +1 -1
- package/dist/features/mentions/mention-text.d.ts +1 -1
- package/dist/features/mentions/mention-text.js.map +1 -1
- package/dist/features/mentions/mention.d.ts +0 -6
- package/dist/features/mentions/mention.js.map +1 -1
- package/dist/features/mentions/metadata-defaults.d.ts +2 -2
- package/dist/features/mentions/metadata-defaults.js +3 -9
- package/dist/features/mentions/metadata-defaults.js.map +1 -1
- package/dist/features/mentions/schema.d.ts +2 -1
- package/dist/features/mentions/schema.js.map +1 -1
- package/dist/features/tables/feature.d.ts +2 -1
- package/dist/features/tables/feature.js +90 -70
- package/dist/features/tables/feature.js.map +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js +6 -9
- package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/feature.js +2 -0
- package/dist/features/themes/feature.js.map +1 -1
- package/dist/features/themes/index.d.ts +2 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js +7 -10
- package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/schema.d.ts +2 -6
- package/dist/features/themes/schema.js.map +1 -1
- package/dist/features/themes/theme-runtime.d.ts +1 -1
- package/dist/features/themes/theme-runtime.js +4 -2
- package/dist/features/themes/theme-runtime.js.map +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js +48 -28
- package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/feature.js +21 -19
- package/dist/features/transcript/feature.js.map +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js +55 -36
- package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/segment-node.d.ts +2 -0
- package/dist/features/transcript/segment-node.js +35 -7
- package/dist/features/transcript/segment-node.js.map +1 -1
- package/dist/i18n/de/composites.js +91 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/integrations.js +49 -8
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/media.js +0 -1
- package/dist/i18n/de/media.js.map +1 -1
- package/dist/i18n/de/palette.js +22 -0
- package/dist/i18n/de/palette.js.map +1 -0
- package/dist/i18n/de/tables.js +0 -1
- package/dist/i18n/de/tables.js.map +1 -1
- package/dist/i18n/de/transcript.js +2 -1
- package/dist/i18n/de/transcript.js.map +1 -1
- package/dist/i18n/de/tree.js +3 -0
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +157 -11
- package/dist/i18n/de.js +3 -1
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en/composites.js +89 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/integrations.js +49 -8
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/media.js +0 -1
- package/dist/i18n/en/media.js.map +1 -1
- package/dist/i18n/en/palette.js +22 -0
- package/dist/i18n/en/palette.js.map +1 -0
- package/dist/i18n/en/tables.js +0 -1
- package/dist/i18n/en/tables.js.map +1 -1
- package/dist/i18n/en/transcript.js +2 -1
- package/dist/i18n/en/transcript.js.map +1 -1
- package/dist/i18n/en/tree.js +3 -0
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/i18n/en.js +3 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/integrations/composite-ai/bibliography-fetch.d.ts +31 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js +159 -0
- package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -0
- package/dist/integrations/composite-ai/composite-fill.d.ts +30 -8
- package/dist/integrations/composite-ai/composite-fill.js +97 -56
- package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
- package/dist/integrations/composite-ai/composite-summary.d.ts +4 -12
- package/dist/integrations/composite-ai/composite-summary.js +7 -26
- package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +29 -0
- package/dist/integrations/composite-ai/derive.js +173 -0
- package/dist/integrations/composite-ai/derive.js.map +1 -0
- package/dist/integrations/composite-ai/index.d.ts +11 -7
- package/dist/integrations/composite-ai/index.js +72 -14
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/rendition.d.ts +45 -0
- package/dist/integrations/composite-ai/rendition.js +314 -0
- package/dist/integrations/composite-ai/rendition.js.map +1 -0
- package/dist/integrations/composite-media/index.d.ts +7 -5
- package/dist/integrations/composite-media/index.js +86 -13
- package/dist/integrations/composite-media/index.js.map +1 -1
- package/dist/integrations/transcript-ai/index.d.ts +1 -1
- package/dist/integrations/transcript-ai/index.js +5 -2
- package/dist/integrations/transcript-ai/index.js.map +1 -1
- package/dist/integrations/transcript-ai/protocol.d.ts +2 -12
- package/dist/integrations/transcript-ai/protocol.js +0 -2
- package/dist/integrations/transcript-ai/protocol.js.map +1 -1
- package/dist/integrations/transcript-ai/transcript-cleanup.d.ts +5 -5
- package/dist/integrations/transcript-ai/transcript-cleanup.js +7 -10
- package/dist/integrations/transcript-ai/transcript-cleanup.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +10 -9
- package/dist/markdown/MarkdownSurface2.js +3 -6
- package/dist/markdown/MarkdownSurface2.js.map +1 -1
- package/dist/markdown/document.js +1 -1
- package/dist/markdown/source-mode.d.ts +1 -1
- package/dist/nuxt/module.d.ts +1402 -54
- package/dist/nuxt/module.js +6 -49
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/install-context.d.ts +7 -3
- package/dist/nuxt/runtime/install-context.js +14 -8
- package/dist/nuxt/runtime/install-context.js.map +1 -1
- package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +2 -2
- package/dist/nuxt/runtime/server/plugins/editor-server.js +20 -12
- package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
- package/dist/presets/compose.d.ts +94 -0
- package/dist/presets/compose.js +164 -0
- package/dist/presets/compose.js.map +1 -0
- package/dist/react/AdvancedEditor.d.ts +15 -15
- package/dist/react/AdvancedEditor.js +15 -13
- package/dist/react/AdvancedEditor.js.map +1 -1
- package/dist/react/EditorCommandPalette.d.ts +13 -0
- package/dist/react/EditorCommandPalette.js +118 -0
- package/dist/react/EditorCommandPalette.js.map +1 -0
- package/dist/react/EditorSettingsModal.d.ts +5 -5
- package/dist/react/EditorSettingsModal.js +7 -4
- package/dist/react/EditorSettingsModal.js.map +1 -1
- package/dist/react/EditorSettingsPanel.d.ts +7 -7
- package/dist/react/EditorSettingsPanel.js +8 -5
- package/dist/react/EditorSettingsPanel.js.map +1 -1
- package/dist/react/EditorSurface.d.ts +7 -7
- package/dist/react/EditorSurface.js +10 -6
- package/dist/react/EditorSurface.js.map +1 -1
- package/dist/react/EditorToolbar.d.ts +4 -6
- package/dist/react/EditorToolbar.js +29 -37
- package/dist/react/EditorToolbar.js.map +1 -1
- package/dist/react/SimpleEditor.d.ts +22 -23
- package/dist/react/SimpleEditor.js +21 -7
- package/dist/react/SimpleEditor.js.map +1 -1
- package/dist/react/advanced.d.ts +4 -3
- package/dist/react/advanced.js +2 -2
- package/dist/react/compose.d.ts +20 -0
- package/dist/react/compose.js +49 -0
- package/dist/react/compose.js.map +1 -0
- package/dist/react/composed-surface.d.ts +39 -0
- package/dist/react/composed-surface.js +71 -0
- package/dist/react/composed-surface.js.map +1 -0
- package/dist/react/context.js +6 -2
- package/dist/react/context.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +2 -1
- package/dist/react/key-labels.js +15 -0
- package/dist/react/key-labels.js.map +1 -0
- package/dist/react/settings.d.ts +1 -1
- package/dist/react/simple.d.ts +2 -2
- package/dist/react/ui/command.js +36 -1
- package/dist/react/ui/command.js.map +1 -1
- package/dist/schema/advanced.d.ts +6 -6
- package/dist/schema/advanced.js +26 -29
- package/dist/schema/advanced.js.map +1 -1
- package/dist/server/ai.d.ts +5 -16
- package/dist/server/ai.js.map +1 -1
- package/dist/server/auth.d.ts +9 -0
- package/dist/server/collab-state.d.ts +1 -1
- package/dist/server/collab-state.js.map +1 -1
- package/dist/server/collab.d.ts +13 -13
- package/dist/server/collab.js +34 -18
- package/dist/server/collab.js.map +1 -1
- package/dist/server/crossref.js +78 -0
- package/dist/server/crossref.js.map +1 -0
- package/dist/server/errors.d.ts +1 -1
- package/dist/server/errors.js +1 -1
- package/dist/server/errors.js.map +1 -1
- package/dist/server/extract.js +308 -0
- package/dist/server/extract.js.map +1 -0
- package/dist/{nuxt/runtime/server/utils/editor-route.js → server/h3.js} +8 -16
- package/dist/server/h3.js.map +1 -0
- package/dist/server/handlers/ai.d.ts +30 -9
- package/dist/server/handlers/ai.js +170 -36
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +12 -5
- package/dist/server/handlers/context.js +10 -4
- package/dist/server/handlers/context.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +19 -13
- package/dist/server/handlers/editor.js +49 -38
- package/dist/server/handlers/editor.js.map +1 -1
- package/dist/server/handlers/index.d.ts +4 -4
- package/dist/server/handlers/index.js +4 -4
- package/dist/server/host.d.ts +16 -25
- package/dist/server/host.js +58 -41
- package/dist/server/host.js.map +1 -1
- package/dist/server/index.d.ts +6 -4
- package/dist/server/index.js +3 -2
- package/dist/server/pdf.js +97 -0
- package/dist/server/pdf.js.map +1 -0
- package/dist/server/{adapter.d.ts → ports.d.ts} +81 -30
- package/dist/server/ports.js +29 -0
- package/dist/server/ports.js.map +1 -0
- package/dist/server/routes.js +144 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/styles/composites.css +25 -0
- package/dist/tree/DocumentTree.js +56 -29
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +23 -0
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +1 -1
- package/dist/vue/AdvancedEditor.js +30 -9
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +32 -12
- package/dist/vue/SimpleEditor.js +62 -16
- package/dist/vue/SimpleEditor.js.map +1 -1
- package/dist/vue/SimpleEditor.vue.d.ts +37 -7
- package/dist/vue/advanced.d.ts +3 -2
- package/dist/vue/advanced.js +2 -2
- package/dist/vue/components/EditorCommandPalette.js +91 -0
- package/dist/vue/components/EditorCommandPalette.js.map +1 -0
- package/dist/vue/components/EditorCommandPalette.vue.d.ts +15 -0
- package/dist/vue/components/EditorSettingsModal.js +10 -6
- package/dist/vue/components/EditorSettingsModal.js.map +1 -1
- package/dist/vue/components/EditorSettingsModal.vue.d.ts +4 -4
- package/dist/vue/components/EditorSettingsPanel.js +10 -6
- package/dist/vue/components/EditorSettingsPanel.js.map +1 -1
- package/dist/vue/components/EditorSettingsPanel.vue.d.ts +6 -6
- package/dist/vue/components/EditorSurface.js +11 -6
- package/dist/vue/components/EditorSurface.js.map +1 -1
- package/dist/vue/components/EditorSurface.vue.d.ts +11 -11
- package/dist/vue/components/EditorToolbar.js +41 -49
- package/dist/vue/components/EditorToolbar.js.map +1 -1
- package/dist/vue/components/EditorToolbar.vue.d.ts +2 -4
- package/dist/vue/compose.d.ts +17 -0
- package/dist/vue/compose.js +51 -0
- package/dist/vue/compose.js.map +1 -0
- package/dist/vue/context-key.d.ts +6 -0
- package/dist/vue/context-key.js +6 -0
- package/dist/vue/context-key.js.map +1 -0
- package/dist/vue/context.d.ts +8 -6
- package/dist/vue/context.js +6 -5
- package/dist/vue/context.js.map +1 -1
- package/dist/vue/index.d.ts +10 -8
- package/dist/vue/index.js +10 -8
- package/dist/vue/kit/index.d.ts +2 -2
- package/dist/vue/kit/nuxt-ui.js +23 -0
- package/dist/vue/kit/nuxt-ui.js.map +1 -1
- package/dist/vue/kit/types.d.ts +40 -1
- package/package.json +30 -16
- package/dist/adapters/comments/client.d.ts +0 -64
- package/dist/adapters/comments/client.js.map +0 -1
- package/dist/adapters/routes.js.map +0 -1
- package/dist/core/toolbar.d.ts +0 -74
- package/dist/core/toolbar.js +0 -45
- package/dist/core/toolbar.js.map +0 -1
- package/dist/integrations/composite-ai/document-transcribe.d.ts +0 -14
- package/dist/integrations/composite-ai/document-transcribe.js +0 -89
- package/dist/integrations/composite-ai/document-transcribe.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/chat.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/chat.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/chat.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/complete.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/complete.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/complete.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/generate.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/generate.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/generate.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/models.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/models.get.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/models.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcribe.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js +0 -8
- package/dist/nuxt/runtime/server/api/ai/transcript-cleanup.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id]/[name].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/assets/[id].post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/settings.put.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/settings.put.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/settings.put.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/tags.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/tags.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/tags.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].delete.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/[id].get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.get.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/themes/index.post.js.map +0 -1
- package/dist/nuxt/runtime/server/api/editor/users.get.d.ts +0 -5
- package/dist/nuxt/runtime/server/api/editor/users.get.js +0 -8
- package/dist/nuxt/runtime/server/api/editor/users.get.js.map +0 -1
- package/dist/nuxt/runtime/server/plugins/collab.d.ts +0 -5
- package/dist/nuxt/runtime/server/plugins/collab.js +0 -10
- package/dist/nuxt/runtime/server/plugins/collab.js.map +0 -1
- package/dist/nuxt/runtime/server/routes/collab/[id].d.ts +0 -5
- package/dist/nuxt/runtime/server/routes/collab/[id].js +0 -35
- package/dist/nuxt/runtime/server/routes/collab/[id].js.map +0 -1
- package/dist/nuxt/runtime/server/utils/editor-route.d.ts +0 -11
- package/dist/nuxt/runtime/server/utils/editor-route.js.map +0 -1
- package/dist/presets/advanced.d.ts +0 -33
- package/dist/presets/advanced.js +0 -50
- package/dist/presets/advanced.js.map +0 -1
- package/dist/presets/simple.d.ts +0 -9
- package/dist/presets/simple.js +0 -12
- package/dist/presets/simple.js.map +0 -1
- package/dist/react/advanced-preset.d.ts +0 -11
- package/dist/react/advanced-preset.js +0 -31
- package/dist/react/advanced-preset.js.map +0 -1
- package/dist/vue/advanced-preset.d.ts +0 -13
- package/dist/vue/advanced-preset.js +0 -33
- package/dist/vue/advanced-preset.js.map +0 -1
- package/docs/extending.md +0 -538
- package/docs/integration.md +0 -630
- package/docs/llms.md +0 -383
- package/docs/usage.md +0 -1076
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ABILITY } from "../../core/schema/abilities.js";
|
|
2
|
-
import {
|
|
2
|
+
import { command } from "../../core/commands.js";
|
|
3
3
|
import { tablesSchemaExtensions } from "./schema.js";
|
|
4
4
|
//#region src/features/tables/feature.ts
|
|
5
5
|
/** The toolbar group the tables feature contributes; its position follows the core groups (`blocks` is 40). */
|
|
@@ -10,88 +10,108 @@ const INSERT = {
|
|
|
10
10
|
cols: 3,
|
|
11
11
|
withHeaderRow: true
|
|
12
12
|
};
|
|
13
|
-
const
|
|
13
|
+
const toolbarGroups = [{
|
|
14
14
|
id: TABLE_TOOLBAR_GROUP,
|
|
15
15
|
order: 60,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
order: 20,
|
|
31
|
-
icon: "i-lucide-between-horizontal-end",
|
|
32
|
-
label: ctx.t("tables.toolbar.addRow"),
|
|
33
|
-
ability: ABILITY.blockContent,
|
|
34
|
-
command: (c) => c.addRowAfter()
|
|
35
|
-
}),
|
|
36
|
-
commandItem({
|
|
37
|
-
id: "addColumn",
|
|
38
|
-
order: 30,
|
|
39
|
-
icon: "i-lucide-between-vertical-end",
|
|
40
|
-
label: ctx.t("tables.toolbar.addColumn"),
|
|
41
|
-
ability: ABILITY.blockContent,
|
|
42
|
-
command: (c) => c.addColumnAfter()
|
|
43
|
-
}),
|
|
44
|
-
commandItem({
|
|
45
|
-
id: "deleteRow",
|
|
46
|
-
order: 40,
|
|
47
|
-
icon: "i-lucide-table-rows-split",
|
|
48
|
-
label: ctx.t("tables.toolbar.deleteRow"),
|
|
49
|
-
ability: ABILITY.blockContent,
|
|
50
|
-
command: (c) => c.deleteRow()
|
|
51
|
-
}),
|
|
52
|
-
commandItem({
|
|
53
|
-
id: "deleteColumn",
|
|
54
|
-
order: 50,
|
|
55
|
-
icon: "i-lucide-table-columns-split",
|
|
56
|
-
label: ctx.t("tables.toolbar.deleteColumn"),
|
|
16
|
+
labelKey: "palette.group.table"
|
|
17
|
+
}];
|
|
18
|
+
const commands = (ctx) => [
|
|
19
|
+
{
|
|
20
|
+
...command({
|
|
21
|
+
id: "insertTable",
|
|
22
|
+
toolbar: {
|
|
23
|
+
group: TABLE_TOOLBAR_GROUP,
|
|
24
|
+
order: 10
|
|
25
|
+
},
|
|
26
|
+
slash: { id: "table.insert" },
|
|
27
|
+
creates: { type: "table" },
|
|
28
|
+
icon: "i-lucide-table",
|
|
29
|
+
label: ctx.t("tables.toolbar.insert"),
|
|
57
30
|
ability: ABILITY.blockContent,
|
|
58
|
-
command: (c) => c.
|
|
31
|
+
command: (c) => c.insertTable(INSERT)
|
|
59
32
|
}),
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
33
|
+
can: (editor) => !editor.isActive("table") && editor.can().insertTable(INSERT)
|
|
34
|
+
},
|
|
35
|
+
command({
|
|
36
|
+
id: "addRow",
|
|
37
|
+
toolbar: {
|
|
38
|
+
group: TABLE_TOOLBAR_GROUP,
|
|
39
|
+
order: 20
|
|
40
|
+
},
|
|
41
|
+
icon: "i-lucide-between-horizontal-end",
|
|
42
|
+
label: ctx.t("tables.toolbar.addRow"),
|
|
43
|
+
ability: ABILITY.blockContent,
|
|
44
|
+
command: (c) => c.addRowAfter()
|
|
45
|
+
}),
|
|
46
|
+
command({
|
|
47
|
+
id: "addColumn",
|
|
48
|
+
toolbar: {
|
|
49
|
+
group: TABLE_TOOLBAR_GROUP,
|
|
50
|
+
order: 30
|
|
51
|
+
},
|
|
52
|
+
icon: "i-lucide-between-vertical-end",
|
|
53
|
+
label: ctx.t("tables.toolbar.addColumn"),
|
|
54
|
+
ability: ABILITY.blockContent,
|
|
55
|
+
command: (c) => c.addColumnAfter()
|
|
56
|
+
}),
|
|
57
|
+
command({
|
|
58
|
+
id: "deleteRow",
|
|
59
|
+
toolbar: {
|
|
60
|
+
group: TABLE_TOOLBAR_GROUP,
|
|
61
|
+
order: 40
|
|
62
|
+
},
|
|
63
|
+
icon: "i-lucide-table-rows-split",
|
|
64
|
+
label: ctx.t("tables.toolbar.deleteRow"),
|
|
65
|
+
ability: ABILITY.blockContent,
|
|
66
|
+
command: (c) => c.deleteRow()
|
|
67
|
+
}),
|
|
68
|
+
command({
|
|
69
|
+
id: "deleteColumn",
|
|
70
|
+
toolbar: {
|
|
71
|
+
group: TABLE_TOOLBAR_GROUP,
|
|
72
|
+
order: 50
|
|
73
|
+
},
|
|
74
|
+
icon: "i-lucide-table-columns-split",
|
|
75
|
+
label: ctx.t("tables.toolbar.deleteColumn"),
|
|
76
|
+
ability: ABILITY.blockContent,
|
|
77
|
+
command: (c) => c.deleteColumn()
|
|
78
|
+
}),
|
|
79
|
+
command({
|
|
80
|
+
id: "deleteTable",
|
|
81
|
+
toolbar: {
|
|
82
|
+
group: TABLE_TOOLBAR_GROUP,
|
|
83
|
+
order: 60
|
|
84
|
+
},
|
|
85
|
+
icon: "i-lucide-trash-2",
|
|
86
|
+
label: ctx.t("tables.toolbar.deleteTable"),
|
|
87
|
+
ability: ABILITY.blockContent,
|
|
88
|
+
command: (c) => c.deleteTable()
|
|
89
|
+
}),
|
|
90
|
+
command({
|
|
91
|
+
id: "task.insert",
|
|
92
|
+
slash: { id: "task.insert" },
|
|
93
|
+
creates: { type: "taskList" },
|
|
94
|
+
icon: "i-lucide-list-checks",
|
|
95
|
+
label: ctx.t("tables.slash.tasks"),
|
|
96
|
+
ability: ABILITY.blockContent,
|
|
97
|
+
command: (c) => c.toggleTaskList()
|
|
98
|
+
})
|
|
99
|
+
];
|
|
83
100
|
/**
|
|
84
101
|
* GFM tables and task lists: the table and task-list nodes (Markdown round-trips through `@tiptap/markdown`, tables as
|
|
85
102
|
* pipe tables, tasks as `- [ ]`/`- [x]`), the toolbar group `table` (insert 3×3, add row, add column, delete row, delete
|
|
86
|
-
* column, delete table) and the slash entries `table.insert` and `task.insert
|
|
103
|
+
* column, delete table) and the slash entries `table.insert` and `task.insert`: one command each, the table commands
|
|
104
|
+
* on the toolbar, `insertTable` and `task.insert` also in the `/` menu. All are offered where the caret has
|
|
87
105
|
* `block.content`. Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.
|
|
88
106
|
*/
|
|
89
107
|
function tablesFeature() {
|
|
90
108
|
return {
|
|
91
109
|
id: "tables",
|
|
110
|
+
dimension: "content",
|
|
111
|
+
schemaExtensions: tablesSchemaExtensions,
|
|
92
112
|
extensions: () => tablesSchemaExtensions(),
|
|
93
|
-
|
|
94
|
-
|
|
113
|
+
toolbarGroups,
|
|
114
|
+
commands
|
|
95
115
|
};
|
|
96
116
|
}
|
|
97
117
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/tables/feature.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"feature.js","names":[],"sources":["../../../src/features/tables/feature.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type CommandDef, command, type EditorFeature, type FeatureContext, type ToolbarGroupDef } from '../../core'\nimport { ABILITY } from '../../core/schema'\nimport { tablesSchemaExtensions } from './schema'\n\n/** The toolbar group the tables feature contributes; its position follows the core groups (`blocks` is 40). */\nexport const TABLE_TOOLBAR_GROUP = 'table'\n\n/** Columns, rows and header row of a table the toolbar and the slash menu insert. */\nconst INSERT = { rows: 3, cols: 3, withHeaderRow: true } as const\n\nconst toolbarGroups: ToolbarGroupDef[] = [{ id: TABLE_TOOLBAR_GROUP, order: 60, labelKey: 'palette.group.table' }]\n\nconst commands = (ctx: FeatureContext): CommandDef<never>[] => [\n {\n ...command({\n id: 'insertTable',\n toolbar: { group: TABLE_TOOLBAR_GROUP, order: 10 },\n slash: { id: 'table.insert' },\n creates: { type: 'table' },\n icon: 'i-lucide-table',\n label: ctx.t('tables.toolbar.insert'),\n ability: ABILITY.blockContent,\n command: c => c.insertTable(INSERT),\n }),\n // GFM has no nested tables: inside one the command is off.\n can: (editor: Editor) => !editor.isActive('table') && editor.can().insertTable(INSERT),\n },\n command({ id: 'addRow', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 20 }, icon: 'i-lucide-between-horizontal-end', label: ctx.t('tables.toolbar.addRow'), ability: ABILITY.blockContent, command: c => c.addRowAfter() }),\n command({ id: 'addColumn', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 30 }, icon: 'i-lucide-between-vertical-end', label: ctx.t('tables.toolbar.addColumn'), ability: ABILITY.blockContent, command: c => c.addColumnAfter() }),\n command({ id: 'deleteRow', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 40 }, icon: 'i-lucide-table-rows-split', label: ctx.t('tables.toolbar.deleteRow'), ability: ABILITY.blockContent, command: c => c.deleteRow() }),\n command({ id: 'deleteColumn', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 50 }, icon: 'i-lucide-table-columns-split', label: ctx.t('tables.toolbar.deleteColumn'), ability: ABILITY.blockContent, command: c => c.deleteColumn() }),\n command({ id: 'deleteTable', toolbar: { group: TABLE_TOOLBAR_GROUP, order: 60 }, icon: 'i-lucide-trash-2', label: ctx.t('tables.toolbar.deleteTable'), ability: ABILITY.blockContent, command: c => c.deleteTable() }),\n command({ id: 'task.insert', slash: { id: 'task.insert' }, creates: { type: 'taskList' }, icon: 'i-lucide-list-checks', label: ctx.t('tables.slash.tasks'), ability: ABILITY.blockContent, command: c => c.toggleTaskList() }),\n]\n\n/**\n * GFM tables and task lists: the table and task-list nodes (Markdown round-trips through `@tiptap/markdown`, tables as\n * pipe tables, tasks as `- [ ]`/`- [x]`), the toolbar group `table` (insert 3×3, add row, add column, delete row, delete\n * column, delete table) and the slash entries `table.insert` and `task.insert`: one command each, the table commands\n * on the toolbar, `insertTable` and `task.insert` also in the `/` menu. All are offered where the caret has\n * `block.content`. Contributes no components (`EditorFeature<never>`), so it fits a preset of any framework layer.\n */\nexport function tablesFeature(): EditorFeature<never> {\n return {\n id: 'tables',\n dimension: 'content',\n schemaExtensions: tablesSchemaExtensions,\n extensions: () => tablesSchemaExtensions(),\n toolbarGroups,\n commands,\n }\n}\n"],"mappings":";;;;;AAMA,MAAa,sBAAsB;;AAGnC,MAAM,SAAS;CAAE,MAAM;CAAG,MAAM;CAAG,eAAe;AAAK;AAEvD,MAAM,gBAAmC,CAAC;CAAE,IAAI;CAAqB,OAAO;CAAI,UAAU;AAAsB,CAAC;AAEjH,MAAM,YAAY,QAA6C;CAC7D;EACE,GAAG,QAAQ;GACT,IAAI;GACJ,SAAS;IAAE,OAAO;IAAqB,OAAO;GAAG;GACjD,OAAO,EAAE,IAAI,eAAe;GAC5B,SAAS,EAAE,MAAM,QAAQ;GACzB,MAAM;GACN,OAAO,IAAI,EAAE,uBAAuB;GACpC,SAAS,QAAQ;GACjB,UAAS,MAAK,EAAE,YAAY,MAAM;EACpC,CAAC;EAED,MAAM,WAAmB,CAAC,OAAO,SAAS,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,YAAY,MAAM;CACvF;CACA,QAAQ;EAAE,IAAI;EAAU,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAmC,OAAO,IAAI,EAAE,uBAAuB;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,YAAY;CAAE,CAAC;CAC1N,QAAQ;EAAE,IAAI;EAAa,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAiC,OAAO,IAAI,EAAE,0BAA0B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,eAAe;CAAE,CAAC;CACjO,QAAQ;EAAE,IAAI;EAAa,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAA6B,OAAO,IAAI,EAAE,0BAA0B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,UAAU;CAAE,CAAC;CACxN,QAAQ;EAAE,IAAI;EAAgB,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAgC,OAAO,IAAI,EAAE,6BAA6B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,aAAa;CAAE,CAAC;CACpO,QAAQ;EAAE,IAAI;EAAe,SAAS;GAAE,OAAO;GAAqB,OAAO;EAAG;EAAG,MAAM;EAAoB,OAAO,IAAI,EAAE,4BAA4B;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,YAAY;CAAE,CAAC;CACrN,QAAQ;EAAE,IAAI;EAAe,OAAO,EAAE,IAAI,cAAc;EAAG,SAAS,EAAE,MAAM,WAAW;EAAG,MAAM;EAAwB,OAAO,IAAI,EAAE,oBAAoB;EAAG,SAAS,QAAQ;EAAc,UAAS,MAAK,EAAE,eAAe;CAAE,CAAC;AAC/N;;;;;;;;AASA,SAAgB,gBAAsC;CACpD,OAAO;EACL,IAAI;EACJ,WAAW;EACX,kBAAkB;EAClB,kBAAkB,uBAAuB;EACzC;EACA;CACF;AACF"}
|
|
@@ -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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"}
|