@ecsplico/editor 3.6.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -0
- package/dist/adapters/direct-ai.d.ts +4 -4
- package/dist/adapters/direct-ai.js +2 -2
- package/dist/adapters/direct-ai.js.map +1 -1
- package/dist/adapters/document-state.d.ts +1 -1
- package/dist/adapters/document-state.js.map +1 -1
- package/dist/adapters/forge.d.ts +2 -2
- package/dist/adapters/forge.js.map +1 -1
- package/dist/adapters/nitro.d.ts +11 -3
- package/dist/adapters/nitro.js +3 -1
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +6 -6
- package/dist/adapters/shared.js.map +1 -1
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/config/index.d.ts +14 -14
- package/dist/config/index.js +8 -8
- package/dist/config/index.js.map +1 -1
- package/dist/core/backend-types.d.ts +3 -3
- package/dist/core/backend-types.js.map +1 -1
- package/dist/core/backend.d.ts +4 -4
- package/dist/core/backend.js.map +1 -1
- package/dist/core/block-convert.d.ts +1 -1
- package/dist/core/block-convert.js +5 -5
- package/dist/core/block-convert.js.map +1 -1
- package/dist/core/block-transfer.d.ts +8 -4
- package/dist/core/block-transfer.js +23 -14
- package/dist/core/block-transfer.js.map +1 -1
- package/dist/core/capabilities.d.ts +113 -0
- package/dist/core/capabilities.js +155 -0
- package/dist/core/capabilities.js.map +1 -0
- package/dist/core/command-types.d.ts +1 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/convert.d.ts +5 -5
- package/dist/core/convert.js +5 -5
- package/dist/core/convert.js.map +1 -1
- package/dist/core/core-feature.js +1 -1
- package/dist/core/define-editor.d.ts +2 -2
- package/dist/core/define-editor.js +34 -6
- package/dist/core/define-editor.js.map +1 -1
- package/dist/core/derivation.d.ts +119 -0
- package/dist/core/derivation.js +187 -0
- package/dist/core/derivation.js.map +1 -0
- package/dist/core/derive.d.ts +61 -0
- package/dist/core/derive.js +69 -0
- package/dist/core/derive.js.map +1 -0
- package/dist/core/extension-points.d.ts +16 -0
- package/dist/core/feature-context.d.ts +5 -1
- package/dist/core/feature-context.js +10 -3
- package/dist/core/feature-context.js.map +1 -1
- package/dist/core/feature.d.ts +41 -19
- package/dist/core/feature.js.map +1 -1
- package/dist/core/fences.js +1 -1
- package/dist/core/fences.js.map +1 -1
- package/dist/core/index.d.ts +6 -2
- package/dist/core/index.js +5 -1
- package/dist/core/pipeline.d.ts +14 -5
- package/dist/core/pipeline.js +19 -7
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/properties.d.ts +22 -0
- package/dist/core/properties.js +17 -0
- package/dist/core/properties.js.map +1 -0
- package/dist/core/schema/block-ids.js +5 -5
- package/dist/core/schema/block-ids.js.map +1 -1
- package/dist/core/schema/index.d.ts +2 -1
- package/dist/core/schema/index.js +2 -1
- package/dist/core/schema/index.js.map +1 -1
- package/dist/core/schema/opaque-fence.d.ts +4 -4
- package/dist/core/schema/opaque-fence.js +7 -7
- package/dist/core/schema/opaque-fence.js.map +1 -1
- package/dist/core/schema/persisted-names.d.ts +58 -0
- package/dist/core/schema/persisted-names.js +96 -0
- package/dist/core/schema/persisted-names.js.map +1 -0
- package/dist/core/toolbar-items.js +1 -1
- package/dist/core/toolbar-items.js.map +1 -1
- package/dist/core/util.js +5 -1
- package/dist/core/util.js.map +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/agent-ops.js +1 -1
- package/dist/features/ai/ai-review.js +1 -1
- package/dist/features/ai/assistant.d.ts +4 -4
- package/dist/features/ai/assistant.js +1 -1
- package/dist/features/ai/assistant.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.js +7 -7
- package/dist/features/ai/components/ModelQuickSwitch.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.vue.d.ts +4 -4
- package/dist/features/ai/components/SettingsAiTab.js +8 -8
- package/dist/features/ai/components/SettingsAiTab.js.map +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/document-chat-model.d.ts +2 -2
- package/dist/features/ai/document-chat-model.js +5 -5
- package/dist/features/ai/document-chat-model.js.map +1 -1
- package/dist/features/ai/document-chat.js +2 -2
- package/dist/features/ai/document-chat.js.map +1 -1
- package/dist/features/ai/feature.d.ts +2 -2
- package/dist/features/ai/feature.js +2 -2
- package/dist/features/ai/feature.js.map +1 -1
- package/dist/features/ai/ghost-autocomplete.d.ts +4 -4
- package/dist/features/ai/ghost-autocomplete.js +1 -1
- package/dist/features/ai/ghost-autocomplete.js.map +1 -1
- package/dist/features/ai/index.d.ts +3 -3
- package/dist/features/ai/index.js +2 -2
- package/dist/features/ai/model-profiles.d.ts +9 -9
- package/dist/features/ai/model-profiles.js +13 -13
- package/dist/features/ai/model-profiles.js.map +1 -1
- package/dist/features/ai/protocol/agent-ops.d.ts +2 -2
- package/dist/features/ai/protocol/agent-ops.js +1 -1
- package/dist/features/ai/protocol/agent-ops.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.d.ts +7 -7
- package/dist/features/ai/protocol/block-lines.js +10 -10
- package/dist/features/ai/protocol/block-lines.js.map +1 -1
- package/dist/features/ai/protocol/chat.d.ts +5 -5
- package/dist/features/ai/protocol/chat.js +12 -12
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/generate.d.ts +1 -1
- package/dist/features/ai/protocol/generate.js +1 -1
- package/dist/features/ai/protocol/generate.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -3
- package/dist/features/ai/protocol/index.js +2 -2
- package/dist/features/ai/protocol/models.d.ts +3 -3
- package/dist/features/ai/protocol/models.js +3 -3
- package/dist/features/ai/protocol/models.js.map +1 -1
- package/dist/features/ai/protocol/schemas.d.ts +1 -1
- package/dist/features/ai/protocol/schemas.js +1 -1
- package/dist/features/ai/protocol/schemas.js.map +1 -1
- package/dist/features/ai/protocol/tasks.d.ts +5 -5
- package/dist/features/ai/protocol/tasks.js +12 -12
- package/dist/features/ai/protocol/tasks.js.map +1 -1
- package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/react/ModelQuickSwitch.d.ts +4 -4
- package/dist/features/ai/react/ModelQuickSwitch.js +6 -6
- package/dist/features/ai/react/ModelQuickSwitch.js.map +1 -1
- package/dist/features/ai/react/SettingsAiTab.d.ts +1 -1
- package/dist/features/ai/react/SettingsAiTab.js +8 -8
- package/dist/features/ai/react/SettingsAiTab.js.map +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.d.ts +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js.map +1 -1
- package/dist/features/ai/react/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/react/use-ai-assistant.js +1 -1
- package/dist/features/ai/react/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/react/use-model-profiles.d.ts +5 -5
- package/dist/features/ai/react/use-model-profiles.js +2 -2
- package/dist/features/ai/react/use-model-profiles.js.map +1 -1
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-ai-assistant.js +1 -1
- package/dist/features/ai/use-ai-assistant.js.map +1 -1
- package/dist/features/ai/use-model-profiles.d.ts +4 -4
- package/dist/features/ai/use-model-profiles.js +2 -2
- package/dist/features/ai/use-model-profiles.js.map +1 -1
- package/dist/features/block-ui/block-actions.d.ts +8 -4
- package/dist/features/block-ui/block-actions.js +38 -22
- package/dist/features/block-ui/block-actions.js.map +1 -1
- package/dist/features/block-ui/block-target.d.ts +29 -0
- package/dist/features/block-ui/block-target.js +63 -0
- package/dist/features/block-ui/block-target.js.map +1 -0
- package/dist/features/block-ui/components/BlockHandles.js +3 -3
- package/dist/features/block-ui/components/BlockHandles.js.map +1 -1
- package/dist/features/block-ui/components/BlockUi.js +11 -23
- package/dist/features/block-ui/components/BlockUi.js.map +1 -1
- package/dist/features/block-ui/feature.d.ts +3 -2
- package/dist/features/block-ui/feature.js +3 -2
- package/dist/features/block-ui/feature.js.map +1 -1
- package/dist/features/block-ui/handle-geometry.js +12 -23
- package/dist/features/block-ui/handle-geometry.js.map +1 -1
- package/dist/features/block-ui/index.d.ts +2 -1
- package/dist/features/block-ui/index.js +2 -1
- package/dist/features/block-ui/react/BlockHandles.d.ts +1 -1
- package/dist/features/block-ui/react/BlockHandles.js +4 -4
- package/dist/features/block-ui/react/BlockHandles.js.map +1 -1
- package/dist/features/block-ui/react/BlockUi.js +13 -25
- package/dist/features/block-ui/react/BlockUi.js.map +1 -1
- package/dist/features/comments/model.js +1 -1
- package/dist/features/composites/accepted-children.d.ts +5 -4
- package/dist/features/composites/accepted-children.js +26 -26
- package/dist/features/composites/accepted-children.js.map +1 -1
- package/dist/features/composites/ai-brief.d.ts +4 -4
- package/dist/features/composites/ai-brief.js +14 -12
- package/dist/features/composites/ai-brief.js.map +1 -1
- package/dist/features/composites/anchors.d.ts +1 -1
- package/dist/features/composites/anchors.js.map +1 -1
- package/dist/features/composites/baskets.d.ts +1 -1
- package/dist/features/composites/baskets.js.map +1 -1
- package/dist/features/composites/bibliography-command.d.ts +1 -1
- package/dist/features/composites/bibliography-form.js +1 -1
- package/dist/features/composites/bibliography-form.js.map +1 -1
- package/dist/features/composites/bibliography.js.map +1 -1
- package/dist/features/composites/block-types-json.d.ts +38 -0
- package/dist/features/composites/block-types-json.js +122 -0
- package/dist/features/composites/block-types-json.js.map +1 -0
- package/dist/features/composites/block-types.d.ts +119 -0
- package/dist/features/composites/block-types.js +343 -0
- package/dist/features/composites/block-types.js.map +1 -0
- package/dist/features/composites/capabilities.js +109 -0
- package/dist/features/composites/capabilities.js.map +1 -0
- package/dist/features/composites/components/BibliographyEditor.js +5 -5
- package/dist/features/composites/components/BibliographyEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeBasketMenu.js +2 -2
- package/dist/features/composites/components/CompositeBasketMenu.js.map +1 -1
- package/dist/features/composites/components/CompositeChrome.js +18 -8
- package/dist/features/composites/components/CompositeChrome.js.map +1 -1
- package/dist/features/composites/components/CompositeChrome.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeDetails.js +11 -10
- package/dist/features/composites/components/CompositeDetails.js.map +1 -1
- package/dist/features/composites/components/CompositeDetails.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeFieldEditor.js +7 -7
- package/dist/features/composites/components/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeFieldEditor.vue.d.ts +5 -5
- package/dist/features/composites/components/CompositeInspector.js +136 -113
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/components/CompositeInspector.vue.d.ts +3 -3
- package/dist/features/composites/components/CompositeStatusMenu.js +2 -2
- package/dist/features/composites/components/CompositeStatusMenu.js.map +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeSummaryEditor.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeTable.js +2 -2
- package/dist/features/composites/components/CompositeTable.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.js.map +1 -1
- package/dist/features/composites/components/CompositeTagsEditor.vue.d.ts +1 -1
- package/dist/features/composites/components/CompositeView.js +19 -17
- package/dist/features/composites/components/CompositeView.js.map +1 -1
- package/dist/features/composites/components/DerivationBadge.js +15 -6
- package/dist/features/composites/components/DerivationBadge.js.map +1 -1
- package/dist/features/composites/components/FocusChip.js +1 -1
- package/dist/features/composites/components/FocusChip.js.map +1 -1
- package/dist/features/composites/components/FocusChip.vue.d.ts +1 -1
- package/dist/features/composites/components/ReferenceFieldSelect.js +3 -3
- package/dist/features/composites/components/ReferenceFieldSelect.js.map +1 -1
- package/dist/features/composites/components/ReferenceView.js +1 -1
- package/dist/features/composites/components/ReferenceView.js.map +1 -1
- package/dist/features/composites/components/SettingsEditingTab.js +18 -18
- package/dist/features/composites/components/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/components/SettingsEditingTab.vue.d.ts +3 -3
- package/dist/features/composites/convert.d.ts +4 -4
- package/dist/features/composites/convert.js +27 -28
- package/dist/features/composites/convert.js.map +1 -1
- package/dist/features/composites/derived-chrome.js +34 -28
- package/dist/features/composites/derived-chrome.js.map +1 -1
- package/dist/features/composites/editing-settings-keys.js +6 -6
- package/dist/features/composites/editing-settings-keys.js.map +1 -1
- package/dist/features/composites/feature.d.ts +10 -10
- package/dist/features/composites/feature.js +58 -46
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/fences.d.ts +3 -3
- package/dist/features/composites/fences.js +18 -13
- package/dist/features/composites/fences.js.map +1 -1
- package/dist/features/composites/focus.d.ts +10 -10
- package/dist/features/composites/focus.js +11 -11
- package/dist/features/composites/focus.js.map +1 -1
- package/dist/features/composites/heading-composites.d.ts +5 -5
- package/dist/features/composites/heading-composites.js +13 -16
- package/dist/features/composites/heading-composites.js.map +1 -1
- package/dist/features/composites/index.d.ts +20 -18
- package/dist/features/composites/index.js +9 -7
- package/dist/features/composites/inspector-model.d.ts +45 -22
- package/dist/features/composites/inspector-model.js +53 -14
- package/dist/features/composites/inspector-model.js.map +1 -1
- package/dist/features/composites/legacy.d.ts +5 -4
- package/dist/features/composites/legacy.js +16 -23
- package/dist/features/composites/legacy.js.map +1 -1
- package/dist/features/composites/markdown-export.js +3 -3
- package/dist/features/composites/markdown-export.js.map +1 -1
- package/dist/features/composites/merge.d.ts +8 -8
- package/dist/features/composites/merge.js +23 -22
- package/dist/features/composites/merge.js.map +1 -1
- package/dist/features/composites/meta-display.js +5 -5
- package/dist/features/composites/meta-display.js.map +1 -1
- package/dist/features/composites/meta-keys.d.ts +5 -5
- package/dist/features/composites/meta-keys.js +8 -8
- package/dist/features/composites/meta-keys.js.map +1 -1
- package/dist/features/composites/node-views.d.ts +3 -3
- package/dist/features/composites/node-views.js +2 -2
- package/dist/features/composites/node-views.js.map +1 -1
- package/dist/features/composites/nodes.d.ts +13 -13
- package/dist/features/composites/nodes.js +100 -77
- package/dist/features/composites/nodes.js.map +1 -1
- package/dist/features/composites/outline.d.ts +15 -12
- package/dist/features/composites/outline.js +24 -21
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/properties.d.ts +51 -0
- package/dist/features/composites/properties.js +89 -0
- package/dist/features/composites/properties.js.map +1 -0
- package/dist/features/composites/react/BibliographyEditor.js +5 -5
- package/dist/features/composites/react/BibliographyEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeBasketMenu.js +2 -2
- package/dist/features/composites/react/CompositeBasketMenu.js.map +1 -1
- package/dist/features/composites/react/CompositeChrome.d.ts +1 -1
- package/dist/features/composites/react/CompositeChrome.js +15 -6
- package/dist/features/composites/react/CompositeChrome.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.d.ts +1 -1
- package/dist/features/composites/react/CompositeDetails.js +11 -10
- package/dist/features/composites/react/CompositeDetails.js.map +1 -1
- package/dist/features/composites/react/CompositeFieldEditor.d.ts +5 -5
- package/dist/features/composites/react/CompositeFieldEditor.js +6 -6
- package/dist/features/composites/react/CompositeFieldEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeInspector.d.ts +3 -3
- package/dist/features/composites/react/CompositeInspector.js +142 -128
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.js +2 -2
- package/dist/features/composites/react/CompositeStatusMenu.js.map +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js +1 -1
- package/dist/features/composites/react/CompositeSummaryEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeTable.d.ts +1 -1
- package/dist/features/composites/react/CompositeTable.js +3 -3
- package/dist/features/composites/react/CompositeTable.js.map +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.d.ts +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js +1 -1
- package/dist/features/composites/react/CompositeTagsEditor.js.map +1 -1
- package/dist/features/composites/react/CompositeView.js +17 -15
- package/dist/features/composites/react/CompositeView.js.map +1 -1
- package/dist/features/composites/react/DerivationBadge.js +13 -4
- package/dist/features/composites/react/DerivationBadge.js.map +1 -1
- package/dist/features/composites/react/FocusChip.d.ts +1 -1
- package/dist/features/composites/react/FocusChip.js +1 -1
- package/dist/features/composites/react/FocusChip.js.map +1 -1
- package/dist/features/composites/react/ReferenceFieldSelect.js +3 -3
- package/dist/features/composites/react/ReferenceFieldSelect.js.map +1 -1
- package/dist/features/composites/react/ReferenceView.d.ts +1 -1
- package/dist/features/composites/react/ReferenceView.js +1 -1
- package/dist/features/composites/react/ReferenceView.js.map +1 -1
- package/dist/features/composites/react/SettingsEditingTab.d.ts +4 -4
- package/dist/features/composites/react/SettingsEditingTab.js +20 -20
- package/dist/features/composites/react/SettingsEditingTab.js.map +1 -1
- package/dist/features/composites/react.js +1 -1
- package/dist/features/composites/react.js.map +1 -1
- package/dist/features/composites/reference-ai.js +1 -1
- package/dist/features/composites/reference-ai.js.map +1 -1
- package/dist/features/composites/reference-label.d.ts +3 -3
- package/dist/features/composites/reference-label.js +6 -6
- package/dist/features/composites/reference-label.js.map +1 -1
- package/dist/features/composites/reference-suggestion.d.ts +1 -1
- package/dist/features/composites/reference-suggestion.js +3 -3
- package/dist/features/composites/reference-suggestion.js.map +1 -1
- package/dist/features/composites/reference-text.d.ts +2 -2
- package/dist/features/composites/reference-text.js +4 -4
- package/dist/features/composites/reference-text.js.map +1 -1
- package/dist/features/composites/reference.d.ts +3 -3
- package/dist/features/composites/reference.js +1 -1
- package/dist/features/composites/reference.js.map +1 -1
- package/dist/features/composites/reveal-source.d.ts +3 -3
- package/dist/features/composites/reveal-source.js +6 -6
- package/dist/features/composites/reveal-source.js.map +1 -1
- package/dist/features/composites/rows-model.d.ts +2 -2
- package/dist/features/composites/rows-model.js +4 -3
- package/dist/features/composites/rows-model.js.map +1 -1
- package/dist/features/composites/rows.d.ts +16 -28
- package/dist/features/composites/rows.js +20 -54
- package/dist/features/composites/rows.js.map +1 -1
- package/dist/features/composites/schema.d.ts +12 -11
- package/dist/features/composites/schema.js +10 -9
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/source.d.ts +20 -0
- package/dist/features/composites/source.js +40 -0
- package/dist/features/composites/source.js.map +1 -0
- package/dist/features/composites/split.d.ts +11 -11
- package/dist/features/composites/split.js +20 -20
- package/dist/features/composites/split.js.map +1 -1
- package/dist/features/composites/structure-mode.js +3 -3
- package/dist/features/composites/structure-mode.js.map +1 -1
- package/dist/features/composites/table-model.js +2 -2
- package/dist/features/composites/table-model.js.map +1 -1
- package/dist/features/composites/use-composite-inspector.js +3 -1
- package/dist/features/composites/use-composite-inspector.js.map +1 -1
- package/dist/features/composites/use-composite-rows.d.ts +2 -2
- package/dist/features/composites/use-composite-rows.js.map +1 -1
- package/dist/features/composites/vue.js +1 -1
- package/dist/features/composites/vue.js.map +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.js +150 -87
- package/dist/features/image-studio/components/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/components/ImageStudioDialog.vue.d.ts +5 -1
- package/dist/features/image-studio/components/ImageStudioLibraryPanel.js +186 -0
- package/dist/features/image-studio/components/ImageStudioLibraryPanel.js.map +1 -0
- package/dist/features/image-studio/components/ImageStudioLibraryTile.js +134 -0
- package/dist/features/image-studio/components/ImageStudioLibraryTile.js.map +1 -0
- package/dist/features/image-studio/components/ImageStudioView.js.map +1 -1
- package/dist/features/image-studio/feature.d.ts +1 -1
- package/dist/features/image-studio/feature.js +1 -1
- package/dist/features/image-studio/feature.js.map +1 -1
- package/dist/features/image-studio/index.d.ts +3 -2
- package/dist/features/image-studio/index.js +2 -2
- package/dist/features/image-studio/library.d.ts +35 -0
- package/dist/features/image-studio/node-core.d.ts +3 -3
- package/dist/features/image-studio/node-core.js +1 -1
- package/dist/features/image-studio/node-core.js.map +1 -1
- package/dist/features/image-studio/node-store.d.ts +1 -1
- package/dist/features/image-studio/node-store.js +1 -1
- package/dist/features/image-studio/node-store.js.map +1 -1
- package/dist/features/image-studio/react/ImageStudioDialog.d.ts +1 -1
- package/dist/features/image-studio/react/ImageStudioDialog.js +46 -5
- package/dist/features/image-studio/react/ImageStudioDialog.js.map +1 -1
- package/dist/features/image-studio/react/ImageStudioLibraryPanel.js +141 -0
- package/dist/features/image-studio/react/ImageStudioLibraryPanel.js.map +1 -0
- package/dist/features/image-studio/react/ImageStudioLibraryTile.js +105 -0
- package/dist/features/image-studio/react/ImageStudioLibraryTile.js.map +1 -0
- package/dist/features/image-studio/react/ImageStudioView.js +1 -1
- package/dist/features/image-studio/react/ImageStudioView.js.map +1 -1
- package/dist/features/image-studio/react/store.d.ts +8 -3
- package/dist/features/image-studio/react/store.js.map +1 -1
- package/dist/features/image-studio/react.d.ts +4 -3
- package/dist/features/image-studio/react.js +3 -3
- package/dist/features/image-studio/react.js.map +1 -1
- package/dist/features/image-studio/schema.d.ts +1 -1
- package/dist/features/image-studio/schema.js +1 -1
- package/dist/features/image-studio/schema.js.map +1 -1
- package/dist/features/image-studio/state.d.ts +8 -3
- package/dist/features/image-studio/state.js +11 -3
- package/dist/features/image-studio/state.js.map +1 -1
- package/dist/features/image-studio/store.d.ts +8 -3
- package/dist/features/image-studio/vue.d.ts +4 -3
- package/dist/features/image-studio/vue.js +3 -3
- package/dist/features/image-studio/vue.js.map +1 -1
- package/dist/features/mentions/tag.js +2 -1
- package/dist/features/mentions/tag.js.map +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js +1 -1
- package/dist/features/themes/components/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/feature.d.ts +1 -1
- package/dist/features/themes/feature.js.map +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.d.ts +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js +1 -1
- package/dist/features/themes/react/SettingsAppearanceTab.js.map +1 -1
- package/dist/features/themes/theme-runtime.js +1 -1
- package/dist/features/themes/theme-runtime.js.map +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js +1 -1
- package/dist/features/transcript/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js +1 -1
- package/dist/features/transcript/react/TranscriptSegmentView.js.map +1 -1
- package/dist/features/transcript/segment-node.js.map +1 -1
- package/dist/features/transfer/feature.js +1 -1
- package/dist/features/transfer/feature.js.map +1 -1
- package/dist/features/transfer/transforms.d.ts +4 -4
- package/dist/features/transfer/transforms.js +12 -12
- package/dist/features/transfer/transforms.js.map +1 -1
- package/dist/i18n/de/ai.js +4 -4
- package/dist/i18n/de/ai.js.map +1 -1
- package/dist/i18n/de/authors.js +1 -1
- package/dist/i18n/de/authors.js.map +1 -1
- package/dist/i18n/de/comments.js +1 -1
- package/dist/i18n/de/comments.js.map +1 -1
- package/dist/i18n/de/composites.js +62 -59
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/image-studio.js +11 -2
- package/dist/i18n/de/image-studio.js.map +1 -1
- package/dist/i18n/de/integrations.js +20 -20
- package/dist/i18n/de/integrations.js.map +1 -1
- package/dist/i18n/de/markdown.js +1 -1
- package/dist/i18n/de/markdown.js.map +1 -1
- package/dist/i18n/de/palette.js +1 -1
- package/dist/i18n/de/palette.js.map +1 -1
- package/dist/i18n/de/themes.js +3 -3
- package/dist/i18n/de/themes.js.map +1 -1
- package/dist/i18n/de/transfer.js +2 -2
- package/dist/i18n/de/transfer.js.map +1 -1
- package/dist/i18n/de/tree.js +6 -6
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +110 -98
- package/dist/i18n/en/ai.js +2 -2
- package/dist/i18n/en/ai.js.map +1 -1
- package/dist/i18n/en/composites.js +64 -61
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/image-studio.js +9 -0
- package/dist/i18n/en/image-studio.js.map +1 -1
- package/dist/i18n/en/integrations.js +9 -9
- package/dist/i18n/en/integrations.js.map +1 -1
- package/dist/i18n/en/markdown.js +1 -1
- package/dist/i18n/en/markdown.js.map +1 -1
- package/dist/i18n/en/palette.js +1 -1
- package/dist/i18n/en/palette.js.map +1 -1
- package/dist/i18n/en/themes.js +4 -4
- package/dist/i18n/en/themes.js.map +1 -1
- package/dist/i18n/en/tree.js +5 -5
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/integrations/composite-ai/bibliography-fetch.d.ts +6 -6
- package/dist/integrations/composite-ai/bibliography-fetch.js +19 -19
- package/dist/integrations/composite-ai/bibliography-fetch.js.map +1 -1
- package/dist/integrations/composite-ai/composite-fill.d.ts +4 -4
- package/dist/integrations/composite-ai/composite-fill.js +5 -5
- package/dist/integrations/composite-ai/composite-fill.js.map +1 -1
- package/dist/integrations/composite-ai/composite-summary.d.ts +2 -2
- package/dist/integrations/composite-ai/composite-summary.js +5 -5
- package/dist/integrations/composite-ai/composite-summary.js.map +1 -1
- package/dist/integrations/composite-ai/derive.d.ts +17 -17
- package/dist/integrations/composite-ai/derive.js +27 -26
- package/dist/integrations/composite-ai/derive.js.map +1 -1
- package/dist/integrations/composite-ai/index.d.ts +14 -15
- package/dist/integrations/composite-ai/index.js +98 -77
- package/dist/integrations/composite-ai/index.js.map +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.d.ts +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.js +1 -1
- package/dist/integrations/composite-ai/outline-sanitize.js.map +1 -1
- package/dist/integrations/composite-ai/rendition.d.ts +9 -9
- package/dist/integrations/composite-ai/rendition.js +27 -27
- package/dist/integrations/composite-ai/rendition.js.map +1 -1
- package/dist/integrations/composite-ai/split.d.ts +8 -8
- package/dist/integrations/composite-ai/split.js +12 -12
- package/dist/integrations/composite-ai/split.js.map +1 -1
- package/dist/integrations/composite-ai/transfer-transforms.d.ts +2 -2
- package/dist/integrations/composite-ai/transfer-transforms.js +11 -11
- package/dist/integrations/composite-ai/transfer-transforms.js.map +1 -1
- package/dist/integrations/composite-media/index.d.ts +4 -3
- package/dist/integrations/composite-media/index.js +20 -14
- package/dist/integrations/composite-media/index.js.map +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
- package/dist/markdown/document.js +1 -1
- package/dist/markdown/document.js.map +1 -1
- package/dist/nuxt/module.d.ts +48 -24
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/server/plugins/editor-server.js +3 -2
- package/dist/nuxt/runtime/server/plugins/editor-server.js.map +1 -1
- package/dist/presets/compose.d.ts +6 -6
- package/dist/presets/compose.js +7 -4
- package/dist/presets/compose.js.map +1 -1
- package/dist/react/AdvancedEditor.d.ts +2 -2
- package/dist/react/AdvancedEditor.js +1 -1
- package/dist/react/AdvancedEditor.js.map +1 -1
- package/dist/react/compose.d.ts +1 -1
- package/dist/react/compose.js +1 -1
- package/dist/react/compose.js.map +1 -1
- package/dist/react/composed-surface.d.ts +5 -5
- package/dist/react/composed-surface.js +2 -2
- package/dist/react/composed-surface.js.map +1 -1
- package/dist/server/ai.d.ts +7 -7
- package/dist/server/ai.js +1 -1
- package/dist/server/ai.js.map +1 -1
- package/dist/server/auth.d.ts +1 -1
- package/dist/server/collab-state.d.ts +1 -1
- package/dist/server/collab-state.js.map +1 -1
- package/dist/server/collab.d.ts +4 -4
- package/dist/server/collab.js +22 -17
- package/dist/server/collab.js.map +1 -1
- package/dist/server/handlers/ai.d.ts +9 -6
- package/dist/server/handlers/ai.js +13 -17
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +4 -0
- package/dist/server/handlers/context.js.map +1 -1
- package/dist/server/handlers/editor.d.ts +33 -32
- package/dist/server/handlers/editor.js +13 -8
- package/dist/server/handlers/editor.js.map +1 -1
- package/dist/server/handlers/images.d.ts +1 -1
- package/dist/server/handlers/images.js +1 -1
- package/dist/server/handlers/images.js.map +1 -1
- package/dist/server/host.d.ts +4 -0
- package/dist/server/host.js +10 -2
- package/dist/server/host.js.map +1 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/ports.d.ts +8 -8
- package/dist/server/ports.js.map +1 -1
- package/dist/settings/index.d.ts +20 -19
- package/dist/settings/index.js +25 -24
- package/dist/settings/index.js.map +1 -1
- package/dist/settings/migrate.d.ts +18 -0
- package/dist/settings/migrate.js +46 -0
- package/dist/settings/migrate.js.map +1 -0
- package/dist/settings/values.d.ts +12 -12
- package/dist/settings/values.js +11 -11
- package/dist/settings/values.js.map +1 -1
- package/dist/styles/composites.css +10 -10
- package/dist/tree/DocumentTree.js +12 -5
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/drop-rules.js +5 -5
- package/dist/tree/drop-rules.js.map +1 -1
- package/dist/tree/react/DocumentTree.js +14 -5
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/tree-host.js +5 -5
- package/dist/tree/tree-host.js.map +1 -1
- package/dist/tree/tree-items.js +34 -28
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +16 -16
- package/dist/tree/tree-model.js.map +1 -1
- package/dist/tree/tree-rows.js +2 -2
- package/dist/tree/tree-rows.js.map +1 -1
- package/dist/tree/tree-view-model.d.ts +2 -2
- package/dist/tree/tree-view-model.js.map +1 -1
- package/dist/vue/AdvancedEditor.js +2 -2
- package/dist/vue/AdvancedEditor.js.map +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +10 -10
- package/dist/vue/SimpleEditor.vue.d.ts +4 -4
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/dist/vue/compose.d.ts +1 -1
- package/dist/vue/compose.js +1 -1
- package/dist/vue/compose.js.map +1 -1
- package/package.json +1 -1
- package/dist/features/composites/derivation.d.ts +0 -62
- package/dist/features/composites/derivation.js +0 -102
- package/dist/features/composites/derivation.js.map +0 -1
- package/dist/features/composites/kinds.d.ts +0 -89
- package/dist/features/composites/kinds.js +0 -285
- package/dist/features/composites/kinds.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentTree.js","names":["_useModel","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_withKeys","_createSlots","_withCtx","_createCommentVNode","_createBlock","_Fragment","_renderList","_normalizeClass","_normalizeStyle","_toDisplayString","_withModifiers","_createTextVNode","_renderSlot"],"sources":["../../src/tree/DocumentTree.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Editor } from '@tiptap/core'\nimport { computed } from 'vue'\nimport { BLOCK_MIME, blockTransferOf, readBlockTransfer, writeBlockTransfer } from '../core/block-transfer'\nimport { findBlockById } from '../core/schema/block-ids'\nimport type { DropdownItem } from '../vue/kit/types'\nimport { useUiKit } from '../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../vue/settings'\nimport { useEditorI18n } from '../vue/use-editor-i18n'\nimport type { DocumentStore } from './store'\nimport { basketById } from '../features/composites'\nimport { DRAG_MIME, focusCrumbs, itemBasket, type TreeItem } from './tree-items'\nimport { rowOf } from './tree-model'\nimport { useTreeModel } from './use-tree-model'\n\n// File browser of a document store: a „Dokumente“ root with folders and documents. It only renders the `TreeModel` (see\n// `tree-model.ts`), which owns what is open, filtered, selected, dragged and renamed; the React twin renders the same model.\n\nconst props = defineProps<{\n /** Where documents and folders live; the host implements it (see `DocumentStore`). */\n store: DocumentStore\n /** Editor of the open document (its outline is listed under the document); `undefined` while none is mounted. */\n editor: Editor | undefined\n}>()\n\ndefineSlots<{\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n 'document-actions'?: (props: { id: string, title: string }) => unknown\n}>()\n\n/** The folder new documents, folders and host imports go to. Reset to `''` when it is deleted or moved away. */\nconst selectedFolder = defineModel<string>('selectedFolder', { default: '' })\n\nconst { t } = useEditorI18n()\nconst { Button: UButton, DropdownMenu: UDropdownMenu, Icon: UIcon, Input: UInput, Popover: UPopover, Tooltip: UTooltip, Tree: UTree } = useUiKit()\nconst { model, state } = useTreeModel({ store: () => props.store, editor: () => props.editor, selectedFolder })\nconst { settings } = useEditorSettings()\n\n// Mirrors the model's `isVirtual` rule: the file root only takes effect for a composite document that is open. Without\n// it the button is disabled (and says why) instead of letting the setting silently fall back to the folders.\nconst canUseFileRoot = computed(() => state.value.hasEditor && state.value.structure === 'composite')\n\nfunction toggleTreeRoot() {\n settings.value.blocks.treeRoot = settings.value.blocks.treeRoot === 'file' ? 'folders' : 'file'\n}\n\nconst row = (item: TreeItem) => rowOf(state.value, item, t)\n\n/** The basket `item` is in, when the open editor offers it (the row shows its colour as a dot). */\nconst basketOf = (item: TreeItem) => itemBasket(item, state.value.baskets)\n\n/** The basket filter button needs a live editor that offers baskets. */\nconst showBasketFilter = computed(() => state.value.hasEditor && state.value.baskets.length > 0)\n/** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */\nconst shownBaskets = computed(() => state.value.basketFilter.filter(id => basketById(state.value.baskets, id)))\nconst basketOption = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-[var(--sk-fg)] transition-colors duration-150 hover:bg-[var(--sk-bg-elevated)] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--sk-primary)]'\n\n/** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Bausteine down to the focused one. */\nconst breadcrumb = computed(() => focusCrumbs(state.value.focusPath))\n\n/** Why dropping the drag on `item` is refused, while the pointer is over it. */\nfunction refusal(item: TreeItem): string | null {\n const drop = row(item).drop\n return drop && !drop.ok ? drop.reason : null\n}\n\n/** UTree reports the keys of the rows it wants open; the model decides what that changes. */\nfunction onExpanded(next: string[]) {\n const wanted = new Set(next)\n const open = new Set(state.value.view.open)\n for (const key of wanted) if (!open.has(key)) model.value.setOpen(key, true)\n for (const key of open) if (!wanted.has(key)) model.value.setOpen(key, false)\n}\n\n/** The label split around the part the title filter matched, `null` when it did not match here. */\nfunction labelParts(item: TreeItem): [string, string, string] | null {\n const match = row(item).match\n if (!match) return null\n const [at, length] = match\n return [item.label.slice(0, at), item.label.slice(at, at + length), item.label.slice(at + length)]\n}\n\n// Folders and the root toggle through the tree's own row click; documents and outline entries (whose row click opens or\n// reveals) toggle through their chevron.\nfunction onChevronClick(event: Event, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.value.toggle(item.key)\n}\n\nfunction filterMenu(item: TreeItem): DropdownItem[][] {\n const { filterKinds, hiddenKinds } = row(item)\n const groups: DropdownItem[][] = [[\n { type: 'label', label: t('tree.filter.show') },\n ...filterKinds.map<DropdownItem>(kind => ({\n label: kind.label,\n icon: kind.icon,\n type: 'checkbox',\n checked: !hiddenKinds.includes(kind.id),\n onUpdateChecked: visible => model.value.setKindHidden(item.key, kind.id, !visible),\n // Stay open: several kinds are usually toggled in one go.\n onSelect: event => event.preventDefault(),\n })),\n ]]\n if (hiddenKinds.length) groups.push([{ label: t('tree.filter.reset'), icon: 'i-lucide-filter-x', onSelect: () => model.value.clearFilter(item.key) }])\n return groups\n}\n\nfunction addMenu(item: TreeItem): DropdownItem[][] {\n return model.value.addMenu(item).map(group => group.map(entry => ({ label: entry.label, icon: entry.icon, onSelect: entry.run })))\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\nfunction onDragStart(event: DragEvent, item: TreeItem) {\n const payload = model.value.dragStart(item)\n if (!payload) return event.preventDefault()\n if (!event.dataTransfer) return model.value.dragEnd()\n event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload))\n // A Baustein row also carries the block itself, so another surface or tree can take a copy.\n const editor = props.editor\n const found = payload.type === 'composite' && editor && !editor.isDestroyed ? findBlockById(editor.state.doc, payload.blockId) : null\n const transfer = found && editor ? blockTransferOf(editor, found.pos) : null\n if (transfer) writeBlockTransfer(event.dataTransfer, transfer)\n event.dataTransfer.effectAllowed = transfer ? 'copyMove' : 'move'\n}\n\n/** A drag of this tree's own rows is running (a foreign drag is not). */\nconst ownDrag = () => !!state.value.drag && state.value.drag.source.type !== 'foreign-block'\n\nfunction onDragOver(event: DragEvent, item: TreeItem) {\n const foreign = !ownDrag() && !!event.dataTransfer?.types.includes(BLOCK_MIME)\n const verdict = foreign ? model.value.foreignDragOver(item) : model.value.dragOver(item)\n if (!verdict) return\n // Only an allowed target takes the drop; over a refused one the browser shows „not allowed“ and the row shows why.\n if (verdict.ok) event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = verdict.ok ? (foreign ? 'copy' : 'move') : 'none'\n}\n\nfunction onDragLeave(event: DragEvent, item: TreeItem) {\n if (!(event.currentTarget as Node).contains(event.relatedTarget as Node | null)) model.value.dragLeave(item)\n}\n\nfunction onDrop(event: DragEvent, item: TreeItem) {\n if (ownDrag()) {\n event.preventDefault()\n return model.value.drop(item)\n }\n const payload = event.dataTransfer ? readBlockTransfer(event.dataTransfer) : null\n if (!payload) return\n event.preventDefault()\n return model.value.dropForeign(item, payload, event.shiftKey, event.clientX, event.clientY)\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-1\">\n <UInput\n :model-value=\"state.filterText\"\n class=\"min-w-0 flex-1\"\n icon=\"i-lucide-search\"\n size=\"sm\"\n :placeholder=\"t('tree.filterText.placeholder')\"\n :aria-label=\"t('tree.filterText.placeholder')\"\n @update:model-value=\"model.setFilterText($event)\"\n @keydown.esc=\"model.setFilterText('')\"\n >\n <template v-if=\"state.filterText\" #trailing>\n <UButton\n icon=\"i-lucide-x\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"link\"\n :aria-label=\"t('tree.filterText.clear')\"\n @click=\"model.setFilterText('')\"\n />\n </template>\n </UInput>\n <!-- A popover, not a dropdown menu: a menu item cannot show a dot in an arbitrary CSS colour. -->\n <UPopover\n v-if=\"showBasketFilter\"\n :content=\"{ side: 'bottom', align: 'end', sideOffset: 4, onCloseAutoFocus: (event: Event) => event.preventDefault() }\"\n :ui=\"{ content: 'w-44 p-1' }\"\n >\n <UButton\n icon=\"i-lucide-paint-bucket\"\n size=\"xs\"\n :color=\"shownBaskets.length ? 'primary' : 'neutral'\"\n :variant=\"shownBaskets.length ? 'soft' : 'ghost'\"\n :title=\"t('tree.basketFilter.label')\"\n :aria-label=\"t('tree.basketFilter.label')\"\n />\n <template #content>\n <div role=\"menu\" :aria-label=\"t('tree.basketFilter.label')\">\n <button\n v-for=\"basket in state.baskets\"\n :key=\"basket.id\"\n type=\"button\"\n role=\"menuitemcheckbox\"\n :aria-checked=\"shownBaskets.includes(basket.id)\"\n :class=\"basketOption\"\n @click=\"model.setBasketShown(basket.id, !shownBaskets.includes(basket.id))\"\n >\n <span class=\"size-2.5 shrink-0 rounded-full\" :style=\"{ backgroundColor: basket.color }\" />\n <span class=\"truncate\">{{ basket.label }}</span>\n <UIcon v-if=\"shownBaskets.includes(basket.id)\" name=\"i-lucide-check\" class=\"ml-auto size-3.5 shrink-0\" />\n </button>\n <button v-if=\"shownBaskets.length\" type=\"button\" role=\"menuitem\" :class=\"basketOption\" @click=\"model.clearBasketFilter()\">\n <UIcon name=\"i-lucide-filter-x\" class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\" />\n <span class=\"truncate\">{{ t('tree.basketFilter.reset') }}</span>\n </button>\n </div>\n </template>\n </UPopover>\n </div>\n\n <div class=\"flex items-center justify-end\">\n <UTooltip :text=\"canUseFileRoot ? '' : t('tree.root.unavailable')\">\n <!-- A disabled button swallows pointer events, so the tooltip hangs on the wrapper. -->\n <span class=\"inline-flex\">\n <UButton\n :icon=\"state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'\"\n :label=\"t(state.virtual ? 'tree.root.file' : 'tree.root.folders')\"\n size=\"xs\"\n color=\"neutral\"\n :variant=\"state.virtual ? 'soft' : 'ghost'\"\n :disabled=\"!canUseFileRoot\"\n :aria-pressed=\"state.virtual\"\n @click=\"toggleTreeRoot\"\n />\n </span>\n </UTooltip>\n </div>\n\n <div v-if=\"state.view.hasKindFilters\" class=\"flex items-center\">\n <UButton\n icon=\"i-lucide-filter-x\"\n :label=\"t('tree.filter.reset')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"subtle\"\n @click=\"model.clearAllFilters\"\n />\n </div>\n\n <div v-if=\"state.creatingFolder\" class=\"flex flex-col gap-1\">\n <UInput\n :model-value=\"state.creatingFolder.draft\"\n size=\"sm\"\n autofocus\n :color=\"state.creatingFolder.error ? 'error' : undefined\"\n :highlight=\"!!state.creatingFolder.error\"\n :placeholder=\"state.creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: state.creatingFolder.parent }) : t('tree.folder.placeholder')\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!state.creatingFolder.error\"\n @update:model-value=\"model.setFolderDraft($event)\"\n @keydown.enter=\"model.commitCreateFolder\"\n @keydown.esc=\"model.cancelCreateFolder\"\n @blur=\"model.cancelCreateFolder\"\n />\n <p v-if=\"state.creatingFolder.error\" class=\"px-0.5 text-xs text-[var(--sk-danger)]\" role=\"alert\">\n {{ state.creatingFolder.error }}\n </p>\n </div>\n\n <div v-if=\"breadcrumb.crumbs.length\" class=\"flex flex-wrap items-center gap-0.5 text-xs\">\n <UButton\n icon=\"i-lucide-corner-up-left\"\n :label=\"breadcrumb.document?.label ?? t('tree.focus.root')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.leave')\"\n :aria-label=\"t('tree.focus.leave')\"\n @click=\"model.defocus()\"\n />\n <template v-for=\"(crumb, index) in breadcrumb.crumbs\" :key=\"crumb.key\">\n <UIcon name=\"i-lucide-chevron-right\" class=\"size-3 shrink-0 text-[var(--sk-muted)]\" />\n <span v-if=\"index === breadcrumb.crumbs.length - 1\" class=\"min-w-0 truncate px-1.5 font-medium\" aria-current=\"location\">{{ crumb.label }}</span>\n <UButton v-else :label=\"crumb.label\" size=\"xs\" color=\"neutral\" variant=\"ghost\" @click=\"model.focus(crumb.key)\" />\n </template>\n </div>\n\n <p v-if=\"!state.tree && !state.virtual\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ state.tree === null ? t('tree.loadFailed') : t('tree.loading') }}\n </p>\n\n <UTree\n v-else\n :expanded=\"state.view.open\"\n :items=\"state.view.items\"\n :get-key=\"(item: TreeItem) => item.key\"\n size=\"sm\"\n :aria-label=\"t('tree.aria')\"\n @update:expanded=\"onExpanded\"\n >\n <template #item-wrapper=\"{ item }: { item: TreeItem }\">\n <div class=\"w-full\">\n <div\n class=\"group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none hover:bg-[color-mix(in_oklab,var(--sk-bg-elevated)_60%,transparent)]\"\n :class=\"[\n row(item).active && 'bg-[color-mix(in_oklab,var(--sk-primary)_10%,transparent)] font-medium text-[var(--sk-primary)]',\n row(item).selected && 'bg-[var(--sk-bg-elevated)]',\n row(item).drop?.ok && 'ring-1 ring-[var(--sk-primary)]',\n refusal(item) && 'bg-[color-mix(in_oklab,var(--sk-danger)_10%,transparent)] ring-1 ring-[var(--sk-danger)]',\n row(item).dimmed && 'opacity-60',\n ]\"\n :draggable=\"model.isDraggable(item)\"\n @dragstart=\"onDragStart($event, item)\"\n @dragover=\"onDragOver($event, item)\"\n @dragleave=\"onDragLeave($event, item)\"\n @drop=\"onDrop($event, item)\"\n @dragend=\"model.dragEnd\"\n >\n <UIcon\n v-if=\"row(item).expandable\"\n name=\"i-lucide-chevron-right\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)] transition-transform\"\n :class=\"row(item).open && 'rotate-90'\"\n @click=\"onChevronClick($event, item)\"\n />\n <span v-else-if=\"item.type === 'composite' || item.type === 'heading'\" class=\"size-3.5 shrink-0\" />\n <UIcon :name=\"item.icon\" class=\"size-4 shrink-0\" :style=\"item.tone ? { color: item.tone } : undefined\" />\n\n <div v-if=\"row(item).renaming\" class=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <UInput\n :model-value=\"row(item).renaming!.draft\"\n size=\"xs\"\n class=\"min-w-0\"\n autofocus\n :color=\"row(item).renaming!.error ? 'error' : undefined\"\n :highlight=\"!!row(item).renaming!.error\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!row(item).renaming!.error\"\n @update:model-value=\"model.setRenameDraft($event)\"\n @focus=\"($event.target as HTMLInputElement).select()\"\n @click.stop\n @keydown.stop\n @keydown.enter=\"model.commitRename\"\n @keydown.esc=\"model.cancelRename\"\n @blur=\"model.blurRename\"\n />\n <p v-if=\"row(item).renaming!.error\" class=\"text-xs text-[var(--sk-danger)]\" role=\"alert\" @click.stop>\n {{ row(item).renaming!.error }}\n </p>\n </div>\n <template v-else>\n <span\n v-if=\"basketOf(item)\"\n class=\"size-2 shrink-0 rounded-full\"\n :style=\"{ backgroundColor: basketOf(item)!.color }\"\n :title=\"basketOf(item)!.label\"\n :aria-label=\"basketOf(item)!.label\"\n role=\"img\"\n data-basket-dot\n />\n <span class=\"min-w-0 flex-1 truncate\" :class=\"item.labelFromBody && 'italic text-[var(--sk-muted)]'\" :title=\"item.label\" :data-label-from-body=\"item.labelFromBody || undefined\">\n <template v-if=\"labelParts(item)\">{{ labelParts(item)![0] }}<mark class=\"rounded-sm bg-[color-mix(in_oklab,var(--sk-primary)_20%,transparent)] text-inherit\">{{ labelParts(item)![1] }}</mark>{{ labelParts(item)![2] }}</template>\n <template v-else>{{ item.label }}</template>\n </span>\n <UIcon\n v-if=\"item.hasSummary\"\n name=\"i-lucide-text-quote\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\"\n :title=\"t('tree.summary')\"\n />\n <div class=\"flex shrink-0\" @click.stop>\n <UDropdownMenu v-if=\"row(item).showFilter\" :items=\"filterMenu(item)\" :content=\"{ align: 'start' }\">\n <UButton\n icon=\"i-lucide-filter\"\n size=\"xs\"\n :color=\"row(item).hiddenKinds.length ? 'primary' : 'neutral'\"\n :variant=\"row(item).hiddenKinds.length ? 'soft' : 'ghost'\"\n :class=\"!row(item).hiddenKinds.length && 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\"\n :title=\"t('tree.filter.title')\"\n :aria-label=\"t('tree.filter.title')\"\n />\n </UDropdownMenu>\n <div class=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n <UButton\n v-if=\"item.type === 'composite' && item.blockId\"\n icon=\"i-lucide-focus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.enter')\"\n :aria-label=\"t('tree.focus.enter')\"\n @click=\"model.focus(item.key)\"\n />\n <UButton\n v-if=\"model.canMerge(item)\"\n icon=\"i-lucide-merge\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.merge')\"\n :aria-label=\"t('tree.merge')\"\n @click=\"model.merge(item)\"\n />\n <UDropdownMenu v-if=\"addMenu(item).length\" :items=\"addMenu(item)\" :content=\"{ align: 'start', onCloseAutoFocus: (event: Event) => event.preventDefault() }\">\n <UButton\n icon=\"i-lucide-plus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.add')\"\n :aria-label=\"t('tree.add')\"\n />\n </UDropdownMenu>\n <slot v-if=\"item.type === 'document'\" name=\"document-actions\" :id=\"item.id!\" :title=\"item.label\" />\n <UButton\n v-if=\"item.type === 'document' || item.type === 'folder'\"\n icon=\"i-lucide-trash-2\"\n size=\"xs\"\n color=\"error\"\n variant=\"ghost\"\n :aria-label=\"t('tree.delete')\"\n loading-auto\n @click=\"model.remove(item)\"\n />\n </div>\n </div>\n </template>\n </div>\n <p v-if=\"refusal(item)\" class=\"px-2 pt-0.5 text-xs text-[var(--sk-danger)]\" role=\"status\">\n {{ refusal(item) }}\n </p>\n </div>\n </template>\n </UTree>\n\n <p v-if=\"state.view.noMatches\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.noMatches') }}\n </p>\n <p v-if=\"state.view.isEmpty\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.empty') }}\n </p>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocumentTree.js","names":["_useModel","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_unref","_withKeys","_createSlots","_withCtx","_createCommentVNode","_createBlock","_Fragment","_renderList","_normalizeClass","_normalizeStyle","_toDisplayString","_withModifiers","_createTextVNode","_renderSlot"],"sources":["../../src/tree/DocumentTree.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Editor } from '@tiptap/core'\nimport { computed } from 'vue'\nimport { BLOCK_MIME, blockTransferOf, readBlockTransfer, writeBlockTransfer } from '../core/block-transfer'\nimport { findBlockById } from '../core/schema/block-ids'\nimport type { DropdownItem } from '../vue/kit/types'\nimport { useUiKit } from '../vue/kit/use-ui-kit'\nimport { useEditorSettings } from '../vue/settings'\nimport { useEditorI18n } from '../vue/use-editor-i18n'\nimport type { DocumentStore } from './store'\nimport { basketById } from '../features/composites'\nimport { DRAG_MIME, focusCrumbs, itemBasket, type TreeItem } from './tree-items'\nimport { rowOf } from './tree-model'\nimport { useTreeModel } from './use-tree-model'\n\n// File browser of a document store: a „Dokumente“ root with folders and documents. It only renders the `TreeModel` (see\n// `tree-model.ts`), which owns what is open, filtered, selected, dragged and renamed; the React twin renders the same model.\n\nconst props = defineProps<{\n /** Where documents and folders live; the host implements it (see `DocumentStore`). */\n store: DocumentStore\n /** Editor of the open document (its outline is listed under the document); `undefined` while none is mounted. */\n editor: Editor | undefined\n}>()\n\ndefineSlots<{\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n 'document-actions'?: (props: { id: string, title: string }) => unknown\n}>()\n\n/** The folder new documents, folders and host imports go to. Reset to `''` when it is deleted or moved away. */\nconst selectedFolder = defineModel<string>('selectedFolder', { default: '' })\n\nconst { t } = useEditorI18n()\nconst { Button: UButton, DropdownMenu: UDropdownMenu, Icon: UIcon, Input: UInput, Popover: UPopover, Tooltip: UTooltip, Tree: UTree } = useUiKit()\nconst { model, state } = useTreeModel({ store: () => props.store, editor: () => props.editor, selectedFolder })\nconst { settings } = useEditorSettings()\n\n// Mirrors the model's `isVirtual` rule: the file root only takes effect for a composite document that is open. Without\n// it the button is disabled (and says why) instead of letting the setting silently fall back to the folders.\nconst canUseFileRoot = computed(() => state.value.hasEditor && state.value.structure === 'structured')\n\nfunction toggleTreeRoot() {\n settings.value.blocks.treeRoot = settings.value.blocks.treeRoot === 'file' ? 'folders' : 'file'\n}\n\nconst row = (item: TreeItem) => rowOf(state.value, item, t)\n\n/** The basket `item` is in, when the open editor offers it (the row shows its colour as a dot). */\nconst basketOf = (item: TreeItem) => itemBasket(item, state.value.baskets)\n\n/** The basket filter button needs a live editor that offers baskets. */\nconst showBasketFilter = computed(() => state.value.hasEditor && state.value.baskets.length > 0)\n/** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */\nconst shownBaskets = computed(() => state.value.basketFilter.filter(id => basketById(state.value.baskets, id)))\nconst basketOption = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-[var(--sk-fg)] transition-colors duration-150 hover:bg-[var(--sk-bg-elevated)] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--sk-primary)]'\n\n/** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Typed blocks down to the focused one. */\nconst breadcrumb = computed(() => focusCrumbs(state.value.focusPath))\n\n/** Why dropping the drag on `item` is refused, while the pointer is over it. */\nfunction refusal(item: TreeItem): string | null {\n const drop = row(item).drop\n return drop && !drop.ok ? drop.reason : null\n}\n\n/** UTree reports the keys of the rows it wants open; the model decides what that changes. */\nfunction onExpanded(next: string[]) {\n const wanted = new Set(next)\n const open = new Set(state.value.view.open)\n for (const key of wanted) if (!open.has(key)) model.value.setOpen(key, true)\n for (const key of open) if (!wanted.has(key)) model.value.setOpen(key, false)\n}\n\n/** The label split around the part the title filter matched, `null` when it did not match here. */\nfunction labelParts(item: TreeItem): [string, string, string] | null {\n const match = row(item).match\n if (!match) return null\n const [at, length] = match\n return [item.label.slice(0, at), item.label.slice(at, at + length), item.label.slice(at + length)]\n}\n\n// Folders and the root toggle through the tree's own row click; documents and outline entries (whose row click opens or\n// reveals) toggle through their chevron.\nfunction onChevronClick(event: Event, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.value.toggle(item.key)\n}\n\nfunction filterMenu(item: TreeItem): DropdownItem[][] {\n const { filterKinds, hiddenKinds } = row(item)\n const groups: DropdownItem[][] = [[\n { type: 'label', label: t('tree.filter.show') },\n ...filterKinds.map<DropdownItem>(kind => ({\n label: kind.label,\n icon: kind.icon,\n type: 'checkbox',\n checked: !hiddenKinds.includes(kind.id),\n onUpdateChecked: visible => model.value.setKindHidden(item.key, kind.id, !visible),\n // Stay open: several kinds are usually toggled in one go.\n onSelect: event => event.preventDefault(),\n })),\n ]]\n if (hiddenKinds.length) groups.push([{ label: t('tree.filter.reset'), icon: 'i-lucide-filter-x', onSelect: () => model.value.clearFilter(item.key) }])\n return groups\n}\n\nfunction addMenu(item: TreeItem): DropdownItem[][] {\n return model.value.addMenu(item).map(group => group.map(entry => ({ label: entry.label, icon: entry.icon, onSelect: entry.run })))\n}\n\n// --- drag & drop -----------------------------------------------------------------------------------------------\n\nfunction onDragStart(event: DragEvent, item: TreeItem) {\n const payload = model.value.dragStart(item)\n if (!payload) return event.preventDefault()\n if (!event.dataTransfer) return model.value.dragEnd()\n event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload))\n // A Typed block row also carries the block itself, so another surface or tree can take a copy.\n const editor = props.editor\n const found = payload.type === 'typedBlock' && editor && !editor.isDestroyed ? findBlockById(editor.state.doc, payload.blockId) : null\n const transfer = found && editor ? blockTransferOf(editor, found.pos) : null\n if (transfer) writeBlockTransfer(event.dataTransfer, transfer)\n event.dataTransfer.effectAllowed = transfer ? 'copyMove' : 'move'\n}\n\n/** A drag of this tree's own rows is running (a foreign drag is not). */\nconst ownDrag = () => !!state.value.drag && state.value.drag.source.type !== 'foreign-block'\n\nfunction onDragOver(event: DragEvent, item: TreeItem) {\n const foreign = !ownDrag() && !!event.dataTransfer?.types.includes(BLOCK_MIME)\n const verdict = foreign ? model.value.foreignDragOver(item) : model.value.dragOver(item)\n if (!verdict) return\n // Only an allowed target takes the drop; over a refused one the browser shows „not allowed“ and the row shows why.\n if (verdict.ok) event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = verdict.ok ? (foreign ? 'copy' : 'move') : 'none'\n}\n\nfunction onDragLeave(event: DragEvent, item: TreeItem) {\n if (!(event.currentTarget as Node).contains(event.relatedTarget as Node | null)) model.value.dragLeave(item)\n}\n\nfunction onDrop(event: DragEvent, item: TreeItem) {\n if (ownDrag()) {\n event.preventDefault()\n return model.value.drop(item)\n }\n const payload = event.dataTransfer ? readBlockTransfer(event.dataTransfer) : null\n if (!payload) return\n event.preventDefault()\n return model.value.dropForeign(item, payload, event.shiftKey, event.clientX, event.clientY)\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-1\">\n <UInput\n :model-value=\"state.filterText\"\n class=\"min-w-0 flex-1\"\n icon=\"i-lucide-search\"\n size=\"sm\"\n :placeholder=\"t('tree.filterText.placeholder')\"\n :aria-label=\"t('tree.filterText.placeholder')\"\n @update:model-value=\"model.setFilterText($event)\"\n @keydown.esc=\"model.setFilterText('')\"\n >\n <template v-if=\"state.filterText\" #trailing>\n <UButton\n icon=\"i-lucide-x\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"link\"\n :aria-label=\"t('tree.filterText.clear')\"\n @click=\"model.setFilterText('')\"\n />\n </template>\n </UInput>\n <!-- A popover, not a dropdown menu: a menu item cannot show a dot in an arbitrary CSS colour. -->\n <UPopover\n v-if=\"showBasketFilter\"\n :content=\"{ side: 'bottom', align: 'end', sideOffset: 4, onCloseAutoFocus: (event: Event) => event.preventDefault() }\"\n :ui=\"{ content: 'w-44 p-1' }\"\n >\n <UButton\n icon=\"i-lucide-paint-bucket\"\n size=\"xs\"\n :color=\"shownBaskets.length ? 'primary' : 'neutral'\"\n :variant=\"shownBaskets.length ? 'soft' : 'ghost'\"\n :title=\"t('tree.basketFilter.label')\"\n :aria-label=\"t('tree.basketFilter.label')\"\n />\n <template #content>\n <div role=\"menu\" :aria-label=\"t('tree.basketFilter.label')\">\n <button\n v-for=\"basket in state.baskets\"\n :key=\"basket.id\"\n type=\"button\"\n role=\"menuitemcheckbox\"\n :aria-checked=\"shownBaskets.includes(basket.id)\"\n :class=\"basketOption\"\n @click=\"model.setBasketShown(basket.id, !shownBaskets.includes(basket.id))\"\n >\n <span class=\"size-2.5 shrink-0 rounded-full\" :style=\"{ backgroundColor: basket.color }\" />\n <span class=\"truncate\">{{ basket.label }}</span>\n <UIcon v-if=\"shownBaskets.includes(basket.id)\" name=\"i-lucide-check\" class=\"ml-auto size-3.5 shrink-0\" />\n </button>\n <button v-if=\"shownBaskets.length\" type=\"button\" role=\"menuitem\" :class=\"basketOption\" @click=\"model.clearBasketFilter()\">\n <UIcon name=\"i-lucide-filter-x\" class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\" />\n <span class=\"truncate\">{{ t('tree.basketFilter.reset') }}</span>\n </button>\n </div>\n </template>\n </UPopover>\n </div>\n\n <div class=\"flex items-center justify-end\">\n <UTooltip :text=\"canUseFileRoot ? '' : t('tree.root.unavailable')\">\n <!-- A disabled button swallows pointer events, so the tooltip hangs on the wrapper. -->\n <span class=\"inline-flex\">\n <UButton\n :icon=\"state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'\"\n :label=\"t(state.virtual ? 'tree.root.file' : 'tree.root.folders')\"\n size=\"xs\"\n color=\"neutral\"\n :variant=\"state.virtual ? 'soft' : 'ghost'\"\n :disabled=\"!canUseFileRoot\"\n :aria-pressed=\"state.virtual\"\n @click=\"toggleTreeRoot\"\n />\n </span>\n </UTooltip>\n </div>\n\n <div v-if=\"state.view.hasKindFilters\" class=\"flex items-center\">\n <UButton\n icon=\"i-lucide-filter-x\"\n :label=\"t('tree.filter.reset')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"subtle\"\n @click=\"model.clearAllFilters\"\n />\n </div>\n\n <div v-if=\"state.creatingFolder\" class=\"flex flex-col gap-1\">\n <UInput\n :model-value=\"state.creatingFolder.draft\"\n size=\"sm\"\n autofocus\n :color=\"state.creatingFolder.error ? 'error' : undefined\"\n :highlight=\"!!state.creatingFolder.error\"\n :placeholder=\"state.creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: state.creatingFolder.parent }) : t('tree.folder.placeholder')\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!state.creatingFolder.error\"\n @update:model-value=\"model.setFolderDraft($event)\"\n @keydown.enter=\"model.commitCreateFolder\"\n @keydown.esc=\"model.cancelCreateFolder\"\n @blur=\"model.cancelCreateFolder\"\n />\n <p v-if=\"state.creatingFolder.error\" class=\"px-0.5 text-xs text-[var(--sk-danger)]\" role=\"alert\">\n {{ state.creatingFolder.error }}\n </p>\n </div>\n\n <div v-if=\"breadcrumb.crumbs.length\" class=\"flex flex-wrap items-center gap-0.5 text-xs\">\n <UButton\n icon=\"i-lucide-corner-up-left\"\n :label=\"breadcrumb.document?.label ?? t('tree.focus.root')\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.leave')\"\n :aria-label=\"t('tree.focus.leave')\"\n @click=\"model.defocus()\"\n />\n <template v-for=\"(crumb, index) in breadcrumb.crumbs\" :key=\"crumb.key\">\n <UIcon name=\"i-lucide-chevron-right\" class=\"size-3 shrink-0 text-[var(--sk-muted)]\" />\n <span v-if=\"index === breadcrumb.crumbs.length - 1\" class=\"min-w-0 truncate px-1.5 font-medium\" aria-current=\"location\">{{ crumb.label }}</span>\n <UButton v-else :label=\"crumb.label\" size=\"xs\" color=\"neutral\" variant=\"ghost\" @click=\"model.focus(crumb.key)\" />\n </template>\n </div>\n\n <p v-if=\"!state.tree && !state.virtual\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ state.tree === null ? t('tree.loadFailed') : t('tree.loading') }}\n </p>\n\n <UTree\n v-else\n :expanded=\"state.view.open\"\n :items=\"state.view.items\"\n :get-key=\"(item: TreeItem) => item.key\"\n size=\"sm\"\n :aria-label=\"t('tree.aria')\"\n @update:expanded=\"onExpanded\"\n >\n <template #item-wrapper=\"{ item }: { item: TreeItem }\">\n <div class=\"w-full\">\n <div\n class=\"group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none hover:bg-[color-mix(in_oklab,var(--sk-bg-elevated)_60%,transparent)]\"\n :class=\"[\n row(item).active && 'bg-[color-mix(in_oklab,var(--sk-primary)_10%,transparent)] font-medium text-[var(--sk-primary)]',\n row(item).selected && 'bg-[var(--sk-bg-elevated)]',\n row(item).drop?.ok && 'ring-1 ring-[var(--sk-primary)]',\n refusal(item) && 'bg-[color-mix(in_oklab,var(--sk-danger)_10%,transparent)] ring-1 ring-[var(--sk-danger)]',\n row(item).dimmed && 'opacity-60',\n ]\"\n :draggable=\"model.isDraggable(item)\"\n @dragstart=\"onDragStart($event, item)\"\n @dragover=\"onDragOver($event, item)\"\n @dragleave=\"onDragLeave($event, item)\"\n @drop=\"onDrop($event, item)\"\n @dragend=\"model.dragEnd\"\n >\n <UIcon\n v-if=\"row(item).expandable\"\n name=\"i-lucide-chevron-right\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)] transition-transform\"\n :class=\"row(item).open && 'rotate-90'\"\n @click=\"onChevronClick($event, item)\"\n />\n <span v-else-if=\"item.type === 'typedBlock' || item.type === 'heading'\" class=\"size-3.5 shrink-0\" />\n <UIcon :name=\"item.icon\" class=\"size-4 shrink-0\" :style=\"item.tone ? { color: item.tone } : undefined\" />\n\n <div v-if=\"row(item).renaming\" class=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <UInput\n :model-value=\"row(item).renaming!.draft\"\n size=\"xs\"\n class=\"min-w-0\"\n autofocus\n :color=\"row(item).renaming!.error ? 'error' : undefined\"\n :highlight=\"!!row(item).renaming!.error\"\n :aria-label=\"t('tree.folder.name')\"\n :aria-invalid=\"!!row(item).renaming!.error\"\n @update:model-value=\"model.setRenameDraft($event)\"\n @focus=\"($event.target as HTMLInputElement).select()\"\n @click.stop\n @keydown.stop\n @keydown.enter=\"model.commitRename\"\n @keydown.esc=\"model.cancelRename\"\n @blur=\"model.blurRename\"\n />\n <p v-if=\"row(item).renaming!.error\" class=\"text-xs text-[var(--sk-danger)]\" role=\"alert\" @click.stop>\n {{ row(item).renaming!.error }}\n </p>\n </div>\n <template v-else>\n <span\n v-if=\"basketOf(item)\"\n class=\"size-2 shrink-0 rounded-full\"\n :style=\"{ backgroundColor: basketOf(item)!.color }\"\n :title=\"basketOf(item)!.label\"\n :aria-label=\"basketOf(item)!.label\"\n role=\"img\"\n data-basket-dot\n />\n <span class=\"min-w-0 flex-1 truncate\" :class=\"item.labelFromBody && 'italic text-[var(--sk-muted)]'\" :title=\"item.label\" :data-label-from-body=\"item.labelFromBody || undefined\">\n <template v-if=\"labelParts(item)\">{{ labelParts(item)![0] }}<mark class=\"rounded-sm bg-[color-mix(in_oklab,var(--sk-primary)_20%,transparent)] text-inherit\">{{ labelParts(item)![1] }}</mark>{{ labelParts(item)![2] }}</template>\n <template v-else>{{ item.label }}</template>\n </span>\n <UIcon\n v-if=\"item.hasSummary\"\n name=\"i-lucide-text-quote\"\n class=\"size-3.5 shrink-0 text-[var(--sk-muted)]\"\n :title=\"t('tree.summary')\"\n />\n <UIcon\n v-if=\"item.sourceMissing\"\n name=\"i-lucide-unlink\"\n class=\"size-3.5 shrink-0 text-[var(--sk-warning)]\"\n :title=\"t('composites.derivation.sourceMissing')\"\n data-source-missing\n />\n <div class=\"flex shrink-0\" @click.stop>\n <UDropdownMenu v-if=\"row(item).showFilter\" :items=\"filterMenu(item)\" :content=\"{ align: 'start' }\">\n <UButton\n icon=\"i-lucide-filter\"\n size=\"xs\"\n :color=\"row(item).hiddenKinds.length ? 'primary' : 'neutral'\"\n :variant=\"row(item).hiddenKinds.length ? 'soft' : 'ghost'\"\n :class=\"!row(item).hiddenKinds.length && 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\"\n :title=\"t('tree.filter.title')\"\n :aria-label=\"t('tree.filter.title')\"\n />\n </UDropdownMenu>\n <div class=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n <UButton\n v-if=\"item.type === 'typedBlock' && item.blockId\"\n icon=\"i-lucide-focus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.focus.enter')\"\n :aria-label=\"t('tree.focus.enter')\"\n @click=\"model.focus(item.key)\"\n />\n <UButton\n v-if=\"model.canMerge(item)\"\n icon=\"i-lucide-merge\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.merge')\"\n :aria-label=\"t('tree.merge')\"\n @click=\"model.merge(item)\"\n />\n <UDropdownMenu v-if=\"addMenu(item).length\" :items=\"addMenu(item)\" :content=\"{ align: 'start', onCloseAutoFocus: (event: Event) => event.preventDefault() }\">\n <UButton\n icon=\"i-lucide-plus\"\n size=\"xs\"\n color=\"neutral\"\n variant=\"ghost\"\n :title=\"t('tree.add')\"\n :aria-label=\"t('tree.add')\"\n />\n </UDropdownMenu>\n <slot v-if=\"item.type === 'document'\" name=\"document-actions\" :id=\"item.id!\" :title=\"item.label\" />\n <UButton\n v-if=\"item.type === 'document' || item.type === 'folder'\"\n icon=\"i-lucide-trash-2\"\n size=\"xs\"\n color=\"error\"\n variant=\"ghost\"\n :aria-label=\"t('tree.delete')\"\n loading-auto\n @click=\"model.remove(item)\"\n />\n </div>\n </div>\n </template>\n </div>\n <p v-if=\"refusal(item)\" class=\"px-2 pt-0.5 text-xs text-[var(--sk-danger)]\" role=\"status\">\n {{ refusal(item) }}\n </p>\n </div>\n </template>\n </UTree>\n\n <p v-if=\"state.view.noMatches\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.noMatches') }}\n </p>\n <p v-if=\"state.view.isEmpty\" class=\"px-2.5 py-1.5 text-sm text-[var(--sk-muted)]\">\n {{ t('tree.empty') }}\n </p>\n </div>\n</template>\n"],"mappings":"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"}
|
package/dist/tree/drop-rules.js
CHANGED
|
@@ -7,8 +7,8 @@ const refuse = (reason) => ({
|
|
|
7
7
|
});
|
|
8
8
|
/**
|
|
9
9
|
* Whether `source` may be dropped on `target`.
|
|
10
|
-
* - A
|
|
11
|
-
* - A
|
|
10
|
+
* - A Typed block (virtual mode) goes on the document root or on a Typed block: the Block types' accepted children decide.
|
|
11
|
+
* - A Typed block from outside (`foreign-block`, virtual mode) goes on the document root or on a Typed block with an ID, if the
|
|
12
12
|
* open editor takes blocks from outside (`canTransfer`); the insert guards are judged when it is dropped.
|
|
13
13
|
* - A document goes on the root or a folder of the store, unless it lives there already.
|
|
14
14
|
* - A folder goes on the root or a folder, unless that is itself, one of its sub-folders or its current parent.
|
|
@@ -16,7 +16,7 @@ const refuse = (reason) => ({
|
|
|
16
16
|
function dropVerdict(source, target, env) {
|
|
17
17
|
const { t } = env;
|
|
18
18
|
if (!source) return refuse(t("tree.drop.nothing"));
|
|
19
|
-
if (source.type === "
|
|
19
|
+
if (source.type === "typedBlock") return blockVerdict(source.blockId, target, env);
|
|
20
20
|
if (source.type === "foreign-block") return foreignBlockVerdict(target, env);
|
|
21
21
|
if (env.virtual) return refuse(t("tree.drop.noDocumentMove"));
|
|
22
22
|
if (target.type !== "root" && target.type !== "folder") return refuse(t("tree.drop.foldersOnly"));
|
|
@@ -30,13 +30,13 @@ function blockVerdict(blockId, target, env) {
|
|
|
30
30
|
const { t } = env;
|
|
31
31
|
if (!env.virtual || !env.moveVerdict) return refuse(t("tree.drop.blocksOnlyInDocument"));
|
|
32
32
|
if (target.key === "vroot") return env.moveVerdict(blockId, null);
|
|
33
|
-
if (target.type !== "
|
|
33
|
+
if (target.type !== "typedBlock") return refuse(t("tree.drop.noBlocksHere"));
|
|
34
34
|
if (!target.blockId) return refuse(t("tree.drop.blockWithoutId"));
|
|
35
35
|
return env.moveVerdict(blockId, target.blockId);
|
|
36
36
|
}
|
|
37
37
|
function foreignBlockVerdict(target, env) {
|
|
38
38
|
if (env.virtual && !env.canTransfer) return refuse(env.t("tree.drop.noTransfer"));
|
|
39
|
-
if (env.virtual && (target.key === "vroot" || target.type === "
|
|
39
|
+
if (env.virtual && (target.key === "vroot" || target.type === "typedBlock" && target.blockId)) return ok;
|
|
40
40
|
return refuse(env.t("tree.drop.noBlocksHere"));
|
|
41
41
|
}
|
|
42
42
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drop-rules.js","names":[],"sources":["../../src/tree/drop-rules.ts"],"sourcesContent":["import type { MoveVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type DragPayload, parentPath, type TreeItem, VIRTUAL_ROOT } from './tree-items'\n\n// The drop rules of the document tree as one pure function: no state, no editor, no store. Where the open document\n// decides (the
|
|
1
|
+
{"version":3,"file":"drop-rules.js","names":[],"sources":["../../src/tree/drop-rules.ts"],"sourcesContent":["import type { MoveVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { type DragPayload, parentPath, type TreeItem, VIRTUAL_ROOT } from './tree-items'\n\n// The drop rules of the document tree as one pure function: no state, no editor, no store. Where the open document\n// decides (the Block types' accepted children), the caller passes that rule in.\n\n/** Whether a drop may happen; a refusal carries a short sentence (in the language of the `t` that judged it) the tree shows under the target. */\nexport type DropVerdict = { ok: true } | { ok: false, reason: string }\n\n/** What `dropVerdict` needs from the outside. */\nexport interface DropEnv {\n /** The tree shows the open document as its root (Typed blocks can be moved); otherwise it shows the store's folders. */\n virtual: boolean\n /**\n * Whether block `id` may move into the Typed block `targetId` (`null` = the end of the document), judged on the live\n * editor (`moveIntoVerdict`). `undefined` while no editor is mounted.\n */\n moveVerdict: ((id: string, targetId: string | null) => MoveVerdict) | undefined\n /** Whether the open editor takes blocks from outside (it has the transfer feature); `false` while no editor is mounted. */\n canTransfer: boolean\n /** Words the refusals of this file's rules; the reasons of `moveVerdict` come already worded. */\n t: Translate\n}\n\nconst ok: DropVerdict = { ok: true }\nconst refuse = (reason: string): DropVerdict => ({ ok: false, reason })\n\n/**\n * Whether `source` may be dropped on `target`.\n * - A Typed block (virtual mode) goes on the document root or on a Typed block: the Block types' accepted children decide.\n * - A Typed block from outside (`foreign-block`, virtual mode) goes on the document root or on a Typed block with an ID, if the\n * open editor takes blocks from outside (`canTransfer`); the insert guards are judged when it is dropped.\n * - A document goes on the root or a folder of the store, unless it lives there already.\n * - A folder goes on the root or a folder, unless that is itself, one of its sub-folders or its current parent.\n */\nexport function dropVerdict(source: DragPayload | null, target: TreeItem, env: DropEnv): DropVerdict {\n const { t } = env\n if (!source) return refuse(t('tree.drop.nothing'))\n if (source.type === 'typedBlock') return blockVerdict(source.blockId, target, env)\n if (source.type === 'foreign-block') return foreignBlockVerdict(target, env)\n if (env.virtual) return refuse(t('tree.drop.noDocumentMove'))\n if (target.type !== 'root' && target.type !== 'folder') return refuse(t('tree.drop.foldersOnly'))\n const targetPath = target.path!\n if (source.type === 'document') return source.path === targetPath ? refuse(t('tree.drop.documentInFolder')) : ok\n if (targetPath === source.path || targetPath.startsWith(`${source.path}/`)) return refuse(t('tree.drop.folderIntoSelf'))\n if (parentPath(source.path) === targetPath) return refuse(t('tree.drop.folderInFolder'))\n return ok\n}\n\nfunction blockVerdict(blockId: string, target: TreeItem, env: DropEnv): DropVerdict {\n const { t } = env\n if (!env.virtual || !env.moveVerdict) return refuse(t('tree.drop.blocksOnlyInDocument'))\n if (target.key === VIRTUAL_ROOT) return env.moveVerdict(blockId, null)\n if (target.type !== 'typedBlock') return refuse(t('tree.drop.noBlocksHere'))\n if (!target.blockId) return refuse(t('tree.drop.blockWithoutId'))\n return env.moveVerdict(blockId, target.blockId)\n}\n\nfunction foreignBlockVerdict(target: TreeItem, env: DropEnv): DropVerdict {\n if (env.virtual && !env.canTransfer) return refuse(env.t('tree.drop.noTransfer'))\n if (env.virtual && (target.key === VIRTUAL_ROOT || (target.type === 'typedBlock' && target.blockId))) return ok\n return refuse(env.t('tree.drop.noBlocksHere'))\n}\n"],"mappings":";;AAyBA,MAAM,KAAkB,EAAE,IAAI,KAAK;AACnC,MAAM,UAAU,YAAiC;CAAE,IAAI;CAAO;AAAO;;;;;;;;;AAUrE,SAAgB,YAAY,QAA4B,QAAkB,KAA2B;CACnG,MAAM,EAAE,MAAM;CACd,IAAI,CAAC,QAAQ,OAAO,OAAO,EAAE,mBAAmB,CAAC;CACjD,IAAI,OAAO,SAAS,cAAc,OAAO,aAAa,OAAO,SAAS,QAAQ,GAAG;CACjF,IAAI,OAAO,SAAS,iBAAiB,OAAO,oBAAoB,QAAQ,GAAG;CAC3E,IAAI,IAAI,SAAS,OAAO,OAAO,EAAE,0BAA0B,CAAC;CAC5D,IAAI,OAAO,SAAS,UAAU,OAAO,SAAS,UAAU,OAAO,OAAO,EAAE,uBAAuB,CAAC;CAChG,MAAM,aAAa,OAAO;CAC1B,IAAI,OAAO,SAAS,YAAY,OAAO,OAAO,SAAS,aAAa,OAAO,EAAE,4BAA4B,CAAC,IAAI;CAC9G,IAAI,eAAe,OAAO,QAAQ,WAAW,WAAW,GAAG,OAAO,KAAK,EAAE,GAAG,OAAO,OAAO,EAAE,0BAA0B,CAAC;CACvH,IAAI,WAAW,OAAO,IAAI,MAAM,YAAY,OAAO,OAAO,EAAE,0BAA0B,CAAC;CACvF,OAAO;AACT;AAEA,SAAS,aAAa,SAAiB,QAAkB,KAA2B;CAClF,MAAM,EAAE,MAAM;CACd,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,aAAa,OAAO,OAAO,EAAE,gCAAgC,CAAC;CACvF,IAAI,OAAO,QAAA,SAAsB,OAAO,IAAI,YAAY,SAAS,IAAI;CACrE,IAAI,OAAO,SAAS,cAAc,OAAO,OAAO,EAAE,wBAAwB,CAAC;CAC3E,IAAI,CAAC,OAAO,SAAS,OAAO,OAAO,EAAE,0BAA0B,CAAC;CAChE,OAAO,IAAI,YAAY,SAAS,OAAO,OAAO;AAChD;AAEA,SAAS,oBAAoB,QAAkB,KAA2B;CACxE,IAAI,IAAI,WAAW,CAAC,IAAI,aAAa,OAAO,OAAO,IAAI,EAAE,sBAAsB,CAAC;CAChF,IAAI,IAAI,YAAY,OAAO,QAAA,WAAyB,OAAO,SAAS,gBAAgB,OAAO,UAAW,OAAO;CAC7G,OAAO,OAAO,IAAI,EAAE,wBAAwB,CAAC;AAC/C"}
|
|
@@ -19,7 +19,7 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
19
19
|
//#region src/tree/react/DocumentTree.tsx
|
|
20
20
|
const BASKET_OPTION = "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-foreground transition-colors duration-150 hover:bg-muted focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary";
|
|
21
21
|
const REVEAL_ON_HOVER = "opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100";
|
|
22
|
-
const isOutlineItem = (item) => item.type === "
|
|
22
|
+
const isOutlineItem = (item) => item.type === "typedBlock" || item.type === "heading";
|
|
23
23
|
/** The label with the part the title filter matched highlighted. */
|
|
24
24
|
function Label({ label, match }) {
|
|
25
25
|
if (!match) return /* @__PURE__ */ jsx(Fragment$1, { children: label });
|
|
@@ -46,13 +46,13 @@ function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, d
|
|
|
46
46
|
const treeElement = useRef(null);
|
|
47
47
|
if (!binding) return null;
|
|
48
48
|
const { model, state } = binding;
|
|
49
|
-
const canUseFileRoot = state.hasEditor && state.structure === "
|
|
49
|
+
const canUseFileRoot = state.hasEditor && state.structure === "structured";
|
|
50
50
|
const { view, creatingFolder } = state;
|
|
51
51
|
/** The basket filter button needs a live editor that offers baskets. */
|
|
52
52
|
const showBasketFilter = state.hasEditor && state.baskets.length > 0;
|
|
53
53
|
/** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */
|
|
54
54
|
const shownBaskets = state.basketFilter.filter((id) => basketById(state.baskets, id));
|
|
55
|
-
/** The breadcrumb of the focus: the document (first crumb, leaves focus) and the
|
|
55
|
+
/** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Typed blocks down to the focused one. */
|
|
56
56
|
const { document: focusDocument, crumbs } = focusCrumbs(state.focusPath);
|
|
57
57
|
function onChevronClick(event, item) {
|
|
58
58
|
if (item.type === "root" || item.type === "folder") return;
|
|
@@ -75,7 +75,7 @@ function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, d
|
|
|
75
75
|
const payload = model.dragStart(item);
|
|
76
76
|
if (!payload) return event.preventDefault();
|
|
77
77
|
event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload));
|
|
78
|
-
const found = payload.type === "
|
|
78
|
+
const found = payload.type === "typedBlock" && editor && !editor.isDestroyed ? findBlockById(editor.state.doc, payload.blockId) : null;
|
|
79
79
|
const transfer = found && editor ? blockTransferOf(editor, found.pos) : null;
|
|
80
80
|
if (transfer) writeBlockTransfer(event.dataTransfer, transfer);
|
|
81
81
|
event.dataTransfer.effectAllowed = transfer ? "copyMove" : "move";
|
|
@@ -202,6 +202,15 @@ function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, d
|
|
|
202
202
|
name: "i-lucide-text-quote",
|
|
203
203
|
className: "size-3.5 text-muted-foreground"
|
|
204
204
|
}),
|
|
205
|
+
item.sourceMissing && /* @__PURE__ */ jsx("span", {
|
|
206
|
+
className: "shrink-0",
|
|
207
|
+
title: t("composites.derivation.sourceMissing"),
|
|
208
|
+
"data-source-missing": true,
|
|
209
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
210
|
+
name: "i-lucide-unlink",
|
|
211
|
+
className: "size-3.5 text-(--sk-warning)"
|
|
212
|
+
})
|
|
213
|
+
}),
|
|
205
214
|
/* @__PURE__ */ jsxs("div", {
|
|
206
215
|
className: "flex shrink-0",
|
|
207
216
|
onClick: (event) => event.stopPropagation(),
|
|
@@ -218,7 +227,7 @@ function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, d
|
|
|
218
227
|
}), filterMenu(item)] }), /* @__PURE__ */ jsxs("div", {
|
|
219
228
|
className: "flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100",
|
|
220
229
|
children: [
|
|
221
|
-
item.type === "
|
|
230
|
+
item.type === "typedBlock" && item.blockId && /* @__PURE__ */ jsx(Button, {
|
|
222
231
|
size: "icon-xs",
|
|
223
232
|
variant: "ghost",
|
|
224
233
|
title: t("tree.focus.enter"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentTree.js","names":[],"sources":["../../../src/tree/react/DocumentTree.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type DragEvent, Fragment, type KeyboardEvent, type ReactNode, useRef } from 'react'\nimport { BLOCK_MIME, blockTransferOf, readBlockTransfer, writeBlockTransfer } from '../../core/block-transfer'\nimport { findBlockById } from '../../core/schema/block-ids'\nimport { Icon } from '../../react/Icon'\nimport { useEditorI18n } from '../../react/use-editor-i18n'\nimport { useEditorSettings } from '../../react/settings'\nimport { Button } from '../../react/ui/button'\nimport { Collapsible, CollapsibleContent } from '../../react/ui/collapsible'\nimport { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '../../react/ui/dropdown-menu'\nimport { Input } from '../../react/ui/input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../react/ui/popover'\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../react/ui/tooltip'\nimport { cn } from '../../react/ui/utils'\nimport type { DocumentStore } from '../store'\nimport { basketById } from '../../features/composites'\nimport { DRAG_MIME, focusCrumbs, itemBasket, type TreeItem } from '../tree-items'\nimport { rowOf } from '../tree-model'\nimport { useTreeModel } from './use-tree-model'\n\n// File browser of a document store: a „Dokumente“ root with folders and documents. It only renders the `TreeModel` (see\n// `tree-model.ts`), which owns what is open, filtered, selected, dragged and renamed; the Vue twin renders the same model.\n\n/** Props of `DocumentTree`. */\nexport interface DocumentTreeProps {\n /** Where documents and folders live; the host implements it (see `DocumentStore`). */\n store: DocumentStore\n /** Editor of the open document (its outline is listed under the document); `undefined` while none is mounted. */\n editor: Editor | undefined\n /** The folder new documents, folders and host imports go to (controlled). Omit it to let the tree keep it. Reset to `''` when it is deleted or moved away. */\n selectedFolder?: string\n onSelectedFolderChange?: (folder: string) => void\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n documentActions?: (document: { id: string, title: string }) => ReactNode\n className?: string\n}\n\nconst BASKET_OPTION = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-foreground transition-colors duration-150 hover:bg-muted focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary'\n\nconst REVEAL_ON_HOVER = 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\nconst isOutlineItem = (item: TreeItem) => item.type === 'composite' || item.type === 'heading'\n\n/** The label with the part the title filter matched highlighted. */\nfunction Label({ label, match }: { label: string, match: readonly [number, number] | null }) {\n if (!match) return <>{label}</>\n const [at, length] = match\n return (\n <>\n {label.slice(0, at)}\n <mark className=\"rounded-sm bg-primary/20 text-inherit\">{label.slice(at, at + length)}</mark>\n {label.slice(at + length)}\n </>\n )\n}\n\n/** The file browser of a document store: folders and documents with their drag and drop, the open document's outline below it. */\nexport function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, documentActions, className }: DocumentTreeProps) {\n const { t } = useEditorI18n()\n const { update } = useEditorSettings()\n const binding = useTreeModel({ store, editor, selectedFolder, onSelectedFolderChange })\n const treeElement = useRef<HTMLUListElement>(null)\n if (!binding) return null\n const { model, state } = binding\n // Mirrors the model's `isVirtual` rule: the file root only takes effect for a composite document that is open. Without\n // it the button is disabled (and says why) instead of letting the setting silently fall back to the folders.\n const canUseFileRoot = state.hasEditor && state.structure === 'composite'\n const { view, creatingFolder } = state\n\n /** The basket filter button needs a live editor that offers baskets. */\n const showBasketFilter = state.hasEditor && state.baskets.length > 0\n /** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */\n const shownBaskets = state.basketFilter.filter(id => basketById(state.baskets, id))\n /** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Bausteine down to the focused one. */\n const { document: focusDocument, crumbs } = focusCrumbs(state.focusPath)\n\n // Folders and the root toggle through the row click; documents and outline entries (whose row click opens or reveals)\n // toggle through their chevron.\n function onChevronClick(event: { stopPropagation: () => void }, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.toggle(item.key)\n }\n\n function onRowKeyDown(event: KeyboardEvent<HTMLDivElement>, item: TreeItem) {\n if (event.target !== event.currentTarget) return\n const { expandable, open } = rowOf(state, item, t)\n if (event.key === 'Enter' || event.key === ' ') model.activate(item)\n else if (event.key === 'ArrowRight' && expandable && !open) model.setOpen(item.key, true)\n else if (event.key === 'ArrowLeft' && expandable && open) model.setOpen(item.key, false)\n else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const rows = [...(treeElement.current?.querySelectorAll<HTMLElement>('[role=\"treeitem\"]') ?? [])]\n rows[rows.indexOf(event.currentTarget) + (event.key === 'ArrowDown' ? 1 : -1)]?.focus()\n } else return\n event.preventDefault()\n }\n\n // --- drag & drop -----------------------------------------------------------------------------------------------\n\n function onDragStart(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const payload = model.dragStart(item)\n if (!payload) return event.preventDefault()\n event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload))\n // A Baustein row also carries the block itself, so another surface or tree can take a copy.\n const found = payload.type === 'composite' && editor && !editor.isDestroyed ? findBlockById(editor.state.doc, payload.blockId) : null\n const transfer = found && editor ? blockTransferOf(editor, found.pos) : null\n if (transfer) writeBlockTransfer(event.dataTransfer, transfer)\n event.dataTransfer.effectAllowed = transfer ? 'copyMove' : 'move'\n }\n\n /** A drag of this tree's own rows is running (a foreign drag is not). */\n const ownDrag = !!state.drag && state.drag.source.type !== 'foreign-block'\n\n function onDragOver(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const foreign = !ownDrag && event.dataTransfer.types.includes(BLOCK_MIME)\n const verdict = foreign ? model.foreignDragOver(item) : model.dragOver(item)\n if (!verdict) return\n // Only an allowed target takes the drop; over a refused one the browser shows „not allowed“ and the row shows why.\n if (verdict.ok) event.preventDefault()\n event.dataTransfer.dropEffect = verdict.ok ? (foreign ? 'copy' : 'move') : 'none'\n }\n\n function onDragLeave(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) model.dragLeave(item)\n }\n\n function onDrop(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (ownDrag) {\n event.preventDefault()\n void model.drop(item)\n return\n }\n const payload = readBlockTransfer(event.dataTransfer)\n if (!payload) return\n event.preventDefault()\n void model.dropForeign(item, payload, event.shiftKey, event.clientX, event.clientY)\n }\n\n // --- rendering -------------------------------------------------------------------------------------------------\n\n const filterMenu = (item: TreeItem) => {\n const { filterKinds, hiddenKinds } = rowOf(state, item, t)\n return (\n <DropdownMenuContent align=\"start\" className=\"w-auto\">\n <DropdownMenuLabel>{t('tree.filter.show')}</DropdownMenuLabel>\n {filterKinds.map(kind => (\n <DropdownMenuCheckboxItem\n key={kind.id}\n checked={!hiddenKinds.includes(kind.id)}\n onCheckedChange={visible => model.setKindHidden(item.key, kind.id, !visible)}\n // Stay open: several kinds are usually toggled in one go.\n onSelect={event => event.preventDefault()}\n >\n <Icon name={kind.icon} />\n {kind.label}\n </DropdownMenuCheckboxItem>\n ))}\n {hiddenKinds.length > 0 && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={() => model.clearFilter(item.key)}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n )\n }\n\n const renderRow = (item: TreeItem, level: number) => {\n const row = rowOf(state, item, t)\n const add = model.addMenu(item)\n const filtered = row.hiddenKinds.length > 0\n const refused = row.drop && !row.drop.ok ? row.drop.reason : null\n const basket = itemBasket(item, state.baskets)\n return (\n <>\n <div\n role=\"treeitem\"\n tabIndex={0}\n aria-level={level + 1}\n aria-expanded={row.expandable ? row.open : undefined}\n aria-selected={row.active ? true : undefined}\n className={cn(\n 'group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none outline-none hover:bg-muted/60 focus-visible:ring-1 focus-visible:ring-ring',\n row.active && 'bg-primary/10 font-medium text-primary',\n row.selected && 'bg-muted',\n row.drop?.ok && 'ring-1 ring-primary',\n refused && 'bg-destructive/10 ring-1 ring-destructive',\n row.dimmed && 'opacity-60',\n )}\n draggable={model.isDraggable(item)}\n onClick={() => model.activate(item)}\n onKeyDown={event => onRowKeyDown(event, item)}\n onDragStart={event => onDragStart(event, item)}\n onDragOver={event => onDragOver(event, item)}\n onDragLeave={event => onDragLeave(event, item)}\n onDrop={event => onDrop(event, item)}\n onDragEnd={() => model.dragEnd()}\n >\n {row.expandable\n ? (\n <span className=\"flex shrink-0\" onClick={event => onChevronClick(event, item)}>\n <Icon name=\"i-lucide-chevron-right\" className={cn('size-3.5 text-muted-foreground transition-transform', row.open && 'rotate-90')} />\n </span>\n )\n : isOutlineItem(item) && <span className=\"size-3.5 shrink-0\" />}\n <Icon name={item.icon} style={item.tone ? { color: item.tone } : undefined} />\n\n {row.renaming\n ? (\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <Input\n value={row.renaming.draft}\n className=\"h-6 min-w-0 px-2 text-xs md:text-xs\"\n autoFocus\n aria-label={t('tree.folder.name')}\n aria-invalid={!!row.renaming.error}\n onChange={event => model.setRenameDraft(event.target.value)}\n onFocus={event => event.currentTarget.select()}\n onClick={event => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === 'Enter') void model.commitRename()\n else if (event.key === 'Escape') model.cancelRename()\n }}\n onBlur={() => void model.blurRename()}\n />\n {row.renaming.error && <p className=\"text-xs text-destructive\" role=\"alert\" onClick={event => event.stopPropagation()}>{row.renaming.error}</p>}\n </div>\n )\n : (\n <>\n {basket && (\n <span\n className=\"size-2 shrink-0 rounded-full\"\n style={{ backgroundColor: basket.color }}\n title={basket.label}\n aria-label={basket.label}\n role=\"img\"\n data-basket-dot\n />\n )}\n <span className={cn('min-w-0 flex-1 truncate', item.labelFromBody && 'italic text-muted-foreground')} title={item.label} data-label-from-body={item.labelFromBody || undefined}><Label label={item.label} match={row.match} /></span>\n {item.hasSummary && <Icon name=\"i-lucide-text-quote\" className=\"size-3.5 text-muted-foreground\" />}\n <div className=\"flex shrink-0\" onClick={event => event.stopPropagation()}>\n {row.showFilter && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"icon-xs\"\n variant={filtered ? 'secondary' : 'ghost'}\n className={cn(!filtered && REVEAL_ON_HOVER)}\n title={t('tree.filter.title')}\n aria-label={t('tree.filter.title')}\n >\n <Icon name=\"i-lucide-filter\" />\n </Button>\n </DropdownMenuTrigger>\n {filterMenu(item)}\n </DropdownMenu>\n )}\n <div className=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n {item.type === 'composite' && item.blockId && (\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.focus.enter')} aria-label={t('tree.focus.enter')} onClick={() => model.focus(item.key)}>\n <Icon name=\"i-lucide-focus\" />\n </Button>\n )}\n {model.canMerge(item) && (\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.merge')} aria-label={t('tree.merge')} onClick={() => model.merge(item)}>\n <Icon name=\"i-lucide-merge\" />\n </Button>\n )}\n {add.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.add')} aria-label={t('tree.add')}>\n <Icon name=\"i-lucide-plus\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-auto\" onCloseAutoFocus={event => event.preventDefault()}>\n {add.map((group, index) => (\n <div key={index}>\n {index > 0 && <DropdownMenuSeparator />}\n {group.map(entry => (\n <DropdownMenuItem key={entry.label} onSelect={entry.run}>\n <Icon name={entry.icon} />\n {entry.label}\n </DropdownMenuItem>\n ))}\n </div>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n {item.type === 'document' && documentActions?.({ id: item.id!, title: item.label })}\n {(item.type === 'document' || item.type === 'folder') && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"text-destructive hover:text-destructive\"\n aria-label={t('tree.delete')}\n disabled={row.removing}\n onClick={() => void model.remove(item)}\n >\n <Icon name={row.removing ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={cn(row.removing && 'animate-spin')} />\n </Button>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n {refused && <p className=\"px-2 pt-0.5 text-xs text-destructive\" role=\"status\">{refused}</p>}\n </>\n )\n }\n\n const renderItems = (list: readonly TreeItem[], level: number): ReactNode => (\n <ul role={level === 0 ? 'tree' : 'group'} aria-label={level === 0 ? t('tree.aria') : undefined} ref={level === 0 ? treeElement : undefined} className={cn('m-0 list-none p-0', level > 0 && 'ml-3 border-l border-border pl-1')}>\n {list.map(item => (\n <li key={item.key} role=\"none\">\n <Collapsible open={rowOf(state, item, t).open}>\n {renderRow(item, level)}\n {item.children?.length ? <CollapsibleContent>{renderItems(item.children, level + 1)}</CollapsibleContent> : null}\n </Collapsible>\n </li>\n ))}\n </ul>\n )\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"flex items-center gap-1\">\n <div className=\"relative min-w-0 flex-1\">\n <Icon name=\"i-lucide-search\" className=\"pointer-events-none absolute top-1/2 left-2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={state.filterText}\n className=\"h-7 pr-7 pl-7 text-sm md:text-sm\"\n placeholder={t('tree.filterText.placeholder')}\n aria-label={t('tree.filterText.placeholder')}\n onChange={event => model.setFilterText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Escape') model.setFilterText('')\n }}\n />\n {state.filterText && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"absolute top-1/2 right-1 -translate-y-1/2\"\n aria-label={t('tree.filterText.clear')}\n onClick={() => model.setFilterText('')}\n >\n <Icon name=\"i-lucide-x\" />\n </Button>\n )}\n </div>\n {showBasketFilter && (\n // A popover, not a dropdown menu: a menu item cannot show a dot in an arbitrary CSS colour.\n <Popover>\n <PopoverTrigger asChild>\n <Button\n size=\"icon-xs\"\n variant={shownBaskets.length ? 'secondary' : 'ghost'}\n title={t('tree.basketFilter.label')}\n aria-label={t('tree.basketFilter.label')}\n >\n <Icon name=\"i-lucide-paint-bucket\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent side=\"bottom\" align=\"end\" sideOffset={4} className=\"w-44 gap-0 p-1\" onCloseAutoFocus={event => event.preventDefault()}>\n <div role=\"menu\" aria-label={t('tree.basketFilter.label')}>\n {state.baskets.map((basket) => {\n const shown = shownBaskets.includes(basket.id)\n return (\n <button\n key={basket.id}\n type=\"button\"\n role=\"menuitemcheckbox\"\n aria-checked={shown}\n className={BASKET_OPTION}\n onClick={() => model.setBasketShown(basket.id, !shown)}\n >\n <span className=\"size-2.5 shrink-0 rounded-full\" style={{ backgroundColor: basket.color }} />\n <span className=\"truncate\">{basket.label}</span>\n {shown && <Icon name=\"i-lucide-check\" className=\"ml-auto size-3.5 shrink-0\" />}\n </button>\n )\n })}\n {shownBaskets.length > 0 && (\n <button type=\"button\" role=\"menuitem\" className={BASKET_OPTION} onClick={() => model.clearBasketFilter()}>\n <Icon name=\"i-lucide-filter-x\" className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{t('tree.basketFilter.reset')}</span>\n </button>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n\n <div className=\"flex items-center justify-end\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n {/* A disabled button swallows pointer events, so the tooltip hangs on the wrapper. */}\n <span className=\"inline-flex\">\n <Button\n size=\"xs\"\n variant={state.virtual ? 'secondary' : 'ghost'}\n disabled={!canUseFileRoot}\n aria-pressed={state.virtual}\n onClick={() => update((draft) => { draft.blocks.treeRoot = draft.blocks.treeRoot === 'file' ? 'folders' : 'file' })}\n >\n <Icon name={state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'} />\n {t(state.virtual ? 'tree.root.file' : 'tree.root.folders')}\n </Button>\n </span>\n </TooltipTrigger>\n {!canUseFileRoot && <TooltipContent>{t('tree.root.unavailable')}</TooltipContent>}\n </Tooltip>\n </TooltipProvider>\n </div>\n\n {view.hasKindFilters && (\n <div className=\"flex items-center\">\n <Button size=\"xs\" variant=\"secondary\" onClick={() => model.clearAllFilters()}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </Button>\n </div>\n )}\n\n {creatingFolder && (\n <div className=\"flex flex-col gap-1\">\n <Input\n value={creatingFolder.draft}\n className=\"h-7 text-sm md:text-sm\"\n autoFocus\n placeholder={creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: creatingFolder.parent }) : t('tree.folder.placeholder')}\n aria-label={t('tree.folder.name')}\n aria-invalid={!!creatingFolder.error}\n onChange={event => model.setFolderDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') void model.commitCreateFolder()\n else if (event.key === 'Escape') model.cancelCreateFolder()\n }}\n onBlur={() => model.cancelCreateFolder()}\n />\n {creatingFolder.error && <p className=\"px-0.5 text-xs text-destructive\" role=\"alert\">{creatingFolder.error}</p>}\n </div>\n )}\n\n {crumbs.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-0.5 text-xs\">\n <Button size=\"xs\" variant=\"ghost\" title={t('tree.focus.leave')} aria-label={t('tree.focus.leave')} onClick={() => model.defocus()}>\n <Icon name=\"i-lucide-corner-up-left\" />\n {focusDocument?.label ?? t('tree.focus.root')}\n </Button>\n {crumbs.map((crumb, index) => (\n <Fragment key={crumb.key}>\n <Icon name=\"i-lucide-chevron-right\" className=\"size-3 shrink-0 text-muted-foreground\" />\n {index === crumbs.length - 1\n ? <span className=\"min-w-0 truncate px-1.5 font-medium\" aria-current=\"location\">{crumb.label}</span>\n : <Button size=\"xs\" variant=\"ghost\" onClick={() => model.focus(crumb.key)}>{crumb.label}</Button>}\n </Fragment>\n ))}\n </div>\n )}\n\n {!state.tree && !state.virtual\n ? <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{state.tree === null ? t('tree.loadFailed') : t('tree.loading')}</p>\n : renderItems(view.items, 0)}\n\n {view.noMatches && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.noMatches')}</p>}\n {view.isEmpty && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.empty')}</p>}\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocumentTree.js","names":[],"sources":["../../../src/tree/react/DocumentTree.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { type DragEvent, Fragment, type KeyboardEvent, type ReactNode, useRef } from 'react'\nimport { BLOCK_MIME, blockTransferOf, readBlockTransfer, writeBlockTransfer } from '../../core/block-transfer'\nimport { findBlockById } from '../../core/schema/block-ids'\nimport { Icon } from '../../react/Icon'\nimport { useEditorI18n } from '../../react/use-editor-i18n'\nimport { useEditorSettings } from '../../react/settings'\nimport { Button } from '../../react/ui/button'\nimport { Collapsible, CollapsibleContent } from '../../react/ui/collapsible'\nimport { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '../../react/ui/dropdown-menu'\nimport { Input } from '../../react/ui/input'\nimport { Popover, PopoverContent, PopoverTrigger } from '../../react/ui/popover'\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../react/ui/tooltip'\nimport { cn } from '../../react/ui/utils'\nimport type { DocumentStore } from '../store'\nimport { basketById } from '../../features/composites'\nimport { DRAG_MIME, focusCrumbs, itemBasket, type TreeItem } from '../tree-items'\nimport { rowOf } from '../tree-model'\nimport { useTreeModel } from './use-tree-model'\n\n// File browser of a document store: a „Dokumente“ root with folders and documents. It only renders the `TreeModel` (see\n// `tree-model.ts`), which owns what is open, filtered, selected, dragged and renamed; the Vue twin renders the same model.\n\n/** Props of `DocumentTree`. */\nexport interface DocumentTreeProps {\n /** Where documents and folders live; the host implements it (see `DocumentStore`). */\n store: DocumentStore\n /** Editor of the open document (its outline is listed under the document); `undefined` while none is mounted. */\n editor: Editor | undefined\n /** The folder new documents, folders and host imports go to (controlled). Omit it to let the tree keep it. Reset to `''` when it is deleted or moved away. */\n selectedFolder?: string\n onSelectedFolderChange?: (folder: string) => void\n /** Host actions on a document row, rendered next to its delete button (e.g. a download link). */\n documentActions?: (document: { id: string, title: string }) => ReactNode\n className?: string\n}\n\nconst BASKET_OPTION = 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm text-foreground transition-colors duration-150 hover:bg-muted focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary'\n\nconst REVEAL_ON_HOVER = 'opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100'\nconst isOutlineItem = (item: TreeItem) => item.type === 'typedBlock' || item.type === 'heading'\n\n/** The label with the part the title filter matched highlighted. */\nfunction Label({ label, match }: { label: string, match: readonly [number, number] | null }) {\n if (!match) return <>{label}</>\n const [at, length] = match\n return (\n <>\n {label.slice(0, at)}\n <mark className=\"rounded-sm bg-primary/20 text-inherit\">{label.slice(at, at + length)}</mark>\n {label.slice(at + length)}\n </>\n )\n}\n\n/** The file browser of a document store: folders and documents with their drag and drop, the open document's outline below it. */\nexport function DocumentTree({ store, editor, selectedFolder, onSelectedFolderChange, documentActions, className }: DocumentTreeProps) {\n const { t } = useEditorI18n()\n const { update } = useEditorSettings()\n const binding = useTreeModel({ store, editor, selectedFolder, onSelectedFolderChange })\n const treeElement = useRef<HTMLUListElement>(null)\n if (!binding) return null\n const { model, state } = binding\n // Mirrors the model's `isVirtual` rule: the file root only takes effect for a composite document that is open. Without\n // it the button is disabled (and says why) instead of letting the setting silently fall back to the folders.\n const canUseFileRoot = state.hasEditor && state.structure === 'structured'\n const { view, creatingFolder } = state\n\n /** The basket filter button needs a live editor that offers baskets. */\n const showBasketFilter = state.hasEditor && state.baskets.length > 0\n /** The chosen baskets the open editor offers (an unknown id never filters, see `TreeModel`). */\n const shownBaskets = state.basketFilter.filter(id => basketById(state.baskets, id))\n /** The breadcrumb of the focus: the document (first crumb, leaves focus) and the Typed blocks down to the focused one. */\n const { document: focusDocument, crumbs } = focusCrumbs(state.focusPath)\n\n // Folders and the root toggle through the row click; documents and outline entries (whose row click opens or reveals)\n // toggle through their chevron.\n function onChevronClick(event: { stopPropagation: () => void }, item: TreeItem) {\n if (item.type === 'root' || item.type === 'folder') return\n event.stopPropagation()\n model.toggle(item.key)\n }\n\n function onRowKeyDown(event: KeyboardEvent<HTMLDivElement>, item: TreeItem) {\n if (event.target !== event.currentTarget) return\n const { expandable, open } = rowOf(state, item, t)\n if (event.key === 'Enter' || event.key === ' ') model.activate(item)\n else if (event.key === 'ArrowRight' && expandable && !open) model.setOpen(item.key, true)\n else if (event.key === 'ArrowLeft' && expandable && open) model.setOpen(item.key, false)\n else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const rows = [...(treeElement.current?.querySelectorAll<HTMLElement>('[role=\"treeitem\"]') ?? [])]\n rows[rows.indexOf(event.currentTarget) + (event.key === 'ArrowDown' ? 1 : -1)]?.focus()\n } else return\n event.preventDefault()\n }\n\n // --- drag & drop -----------------------------------------------------------------------------------------------\n\n function onDragStart(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const payload = model.dragStart(item)\n if (!payload) return event.preventDefault()\n event.dataTransfer.setData(DRAG_MIME, JSON.stringify(payload))\n // A Typed block row also carries the block itself, so another surface or tree can take a copy.\n const found = payload.type === 'typedBlock' && editor && !editor.isDestroyed ? findBlockById(editor.state.doc, payload.blockId) : null\n const transfer = found && editor ? blockTransferOf(editor, found.pos) : null\n if (transfer) writeBlockTransfer(event.dataTransfer, transfer)\n event.dataTransfer.effectAllowed = transfer ? 'copyMove' : 'move'\n }\n\n /** A drag of this tree's own rows is running (a foreign drag is not). */\n const ownDrag = !!state.drag && state.drag.source.type !== 'foreign-block'\n\n function onDragOver(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n const foreign = !ownDrag && event.dataTransfer.types.includes(BLOCK_MIME)\n const verdict = foreign ? model.foreignDragOver(item) : model.dragOver(item)\n if (!verdict) return\n // Only an allowed target takes the drop; over a refused one the browser shows „not allowed“ and the row shows why.\n if (verdict.ok) event.preventDefault()\n event.dataTransfer.dropEffect = verdict.ok ? (foreign ? 'copy' : 'move') : 'none'\n }\n\n function onDragLeave(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) model.dragLeave(item)\n }\n\n function onDrop(event: DragEvent<HTMLDivElement>, item: TreeItem) {\n if (ownDrag) {\n event.preventDefault()\n void model.drop(item)\n return\n }\n const payload = readBlockTransfer(event.dataTransfer)\n if (!payload) return\n event.preventDefault()\n void model.dropForeign(item, payload, event.shiftKey, event.clientX, event.clientY)\n }\n\n // --- rendering -------------------------------------------------------------------------------------------------\n\n const filterMenu = (item: TreeItem) => {\n const { filterKinds, hiddenKinds } = rowOf(state, item, t)\n return (\n <DropdownMenuContent align=\"start\" className=\"w-auto\">\n <DropdownMenuLabel>{t('tree.filter.show')}</DropdownMenuLabel>\n {filterKinds.map(kind => (\n <DropdownMenuCheckboxItem\n key={kind.id}\n checked={!hiddenKinds.includes(kind.id)}\n onCheckedChange={visible => model.setKindHidden(item.key, kind.id, !visible)}\n // Stay open: several kinds are usually toggled in one go.\n onSelect={event => event.preventDefault()}\n >\n <Icon name={kind.icon} />\n {kind.label}\n </DropdownMenuCheckboxItem>\n ))}\n {hiddenKinds.length > 0 && (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={() => model.clearFilter(item.key)}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n )\n }\n\n const renderRow = (item: TreeItem, level: number) => {\n const row = rowOf(state, item, t)\n const add = model.addMenu(item)\n const filtered = row.hiddenKinds.length > 0\n const refused = row.drop && !row.drop.ok ? row.drop.reason : null\n const basket = itemBasket(item, state.baskets)\n return (\n <>\n <div\n role=\"treeitem\"\n tabIndex={0}\n aria-level={level + 1}\n aria-expanded={row.expandable ? row.open : undefined}\n aria-selected={row.active ? true : undefined}\n className={cn(\n 'group flex w-full cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-sm select-none outline-none hover:bg-muted/60 focus-visible:ring-1 focus-visible:ring-ring',\n row.active && 'bg-primary/10 font-medium text-primary',\n row.selected && 'bg-muted',\n row.drop?.ok && 'ring-1 ring-primary',\n refused && 'bg-destructive/10 ring-1 ring-destructive',\n row.dimmed && 'opacity-60',\n )}\n draggable={model.isDraggable(item)}\n onClick={() => model.activate(item)}\n onKeyDown={event => onRowKeyDown(event, item)}\n onDragStart={event => onDragStart(event, item)}\n onDragOver={event => onDragOver(event, item)}\n onDragLeave={event => onDragLeave(event, item)}\n onDrop={event => onDrop(event, item)}\n onDragEnd={() => model.dragEnd()}\n >\n {row.expandable\n ? (\n <span className=\"flex shrink-0\" onClick={event => onChevronClick(event, item)}>\n <Icon name=\"i-lucide-chevron-right\" className={cn('size-3.5 text-muted-foreground transition-transform', row.open && 'rotate-90')} />\n </span>\n )\n : isOutlineItem(item) && <span className=\"size-3.5 shrink-0\" />}\n <Icon name={item.icon} style={item.tone ? { color: item.tone } : undefined} />\n\n {row.renaming\n ? (\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <Input\n value={row.renaming.draft}\n className=\"h-6 min-w-0 px-2 text-xs md:text-xs\"\n autoFocus\n aria-label={t('tree.folder.name')}\n aria-invalid={!!row.renaming.error}\n onChange={event => model.setRenameDraft(event.target.value)}\n onFocus={event => event.currentTarget.select()}\n onClick={event => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === 'Enter') void model.commitRename()\n else if (event.key === 'Escape') model.cancelRename()\n }}\n onBlur={() => void model.blurRename()}\n />\n {row.renaming.error && <p className=\"text-xs text-destructive\" role=\"alert\" onClick={event => event.stopPropagation()}>{row.renaming.error}</p>}\n </div>\n )\n : (\n <>\n {basket && (\n <span\n className=\"size-2 shrink-0 rounded-full\"\n style={{ backgroundColor: basket.color }}\n title={basket.label}\n aria-label={basket.label}\n role=\"img\"\n data-basket-dot\n />\n )}\n <span className={cn('min-w-0 flex-1 truncate', item.labelFromBody && 'italic text-muted-foreground')} title={item.label} data-label-from-body={item.labelFromBody || undefined}><Label label={item.label} match={row.match} /></span>\n {item.hasSummary && <Icon name=\"i-lucide-text-quote\" className=\"size-3.5 text-muted-foreground\" />}\n {item.sourceMissing && <span className=\"shrink-0\" title={t('composites.derivation.sourceMissing')} data-source-missing><Icon name=\"i-lucide-unlink\" className=\"size-3.5 text-(--sk-warning)\" /></span>}\n <div className=\"flex shrink-0\" onClick={event => event.stopPropagation()}>\n {row.showFilter && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"icon-xs\"\n variant={filtered ? 'secondary' : 'ghost'}\n className={cn(!filtered && REVEAL_ON_HOVER)}\n title={t('tree.filter.title')}\n aria-label={t('tree.filter.title')}\n >\n <Icon name=\"i-lucide-filter\" />\n </Button>\n </DropdownMenuTrigger>\n {filterMenu(item)}\n </DropdownMenu>\n )}\n <div className=\"flex opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 has-data-[state=open]:opacity-100\">\n {item.type === 'typedBlock' && item.blockId && (\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.focus.enter')} aria-label={t('tree.focus.enter')} onClick={() => model.focus(item.key)}>\n <Icon name=\"i-lucide-focus\" />\n </Button>\n )}\n {model.canMerge(item) && (\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.merge')} aria-label={t('tree.merge')} onClick={() => model.merge(item)}>\n <Icon name=\"i-lucide-merge\" />\n </Button>\n )}\n {add.length > 0 && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button size=\"icon-xs\" variant=\"ghost\" title={t('tree.add')} aria-label={t('tree.add')}>\n <Icon name=\"i-lucide-plus\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-auto\" onCloseAutoFocus={event => event.preventDefault()}>\n {add.map((group, index) => (\n <div key={index}>\n {index > 0 && <DropdownMenuSeparator />}\n {group.map(entry => (\n <DropdownMenuItem key={entry.label} onSelect={entry.run}>\n <Icon name={entry.icon} />\n {entry.label}\n </DropdownMenuItem>\n ))}\n </div>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n {item.type === 'document' && documentActions?.({ id: item.id!, title: item.label })}\n {(item.type === 'document' || item.type === 'folder') && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"text-destructive hover:text-destructive\"\n aria-label={t('tree.delete')}\n disabled={row.removing}\n onClick={() => void model.remove(item)}\n >\n <Icon name={row.removing ? 'i-lucide-loader-circle' : 'i-lucide-trash-2'} className={cn(row.removing && 'animate-spin')} />\n </Button>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n {refused && <p className=\"px-2 pt-0.5 text-xs text-destructive\" role=\"status\">{refused}</p>}\n </>\n )\n }\n\n const renderItems = (list: readonly TreeItem[], level: number): ReactNode => (\n <ul role={level === 0 ? 'tree' : 'group'} aria-label={level === 0 ? t('tree.aria') : undefined} ref={level === 0 ? treeElement : undefined} className={cn('m-0 list-none p-0', level > 0 && 'ml-3 border-l border-border pl-1')}>\n {list.map(item => (\n <li key={item.key} role=\"none\">\n <Collapsible open={rowOf(state, item, t).open}>\n {renderRow(item, level)}\n {item.children?.length ? <CollapsibleContent>{renderItems(item.children, level + 1)}</CollapsibleContent> : null}\n </Collapsible>\n </li>\n ))}\n </ul>\n )\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n <div className=\"flex items-center gap-1\">\n <div className=\"relative min-w-0 flex-1\">\n <Icon name=\"i-lucide-search\" className=\"pointer-events-none absolute top-1/2 left-2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={state.filterText}\n className=\"h-7 pr-7 pl-7 text-sm md:text-sm\"\n placeholder={t('tree.filterText.placeholder')}\n aria-label={t('tree.filterText.placeholder')}\n onChange={event => model.setFilterText(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Escape') model.setFilterText('')\n }}\n />\n {state.filterText && (\n <Button\n size=\"icon-xs\"\n variant=\"ghost\"\n className=\"absolute top-1/2 right-1 -translate-y-1/2\"\n aria-label={t('tree.filterText.clear')}\n onClick={() => model.setFilterText('')}\n >\n <Icon name=\"i-lucide-x\" />\n </Button>\n )}\n </div>\n {showBasketFilter && (\n // A popover, not a dropdown menu: a menu item cannot show a dot in an arbitrary CSS colour.\n <Popover>\n <PopoverTrigger asChild>\n <Button\n size=\"icon-xs\"\n variant={shownBaskets.length ? 'secondary' : 'ghost'}\n title={t('tree.basketFilter.label')}\n aria-label={t('tree.basketFilter.label')}\n >\n <Icon name=\"i-lucide-paint-bucket\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent side=\"bottom\" align=\"end\" sideOffset={4} className=\"w-44 gap-0 p-1\" onCloseAutoFocus={event => event.preventDefault()}>\n <div role=\"menu\" aria-label={t('tree.basketFilter.label')}>\n {state.baskets.map((basket) => {\n const shown = shownBaskets.includes(basket.id)\n return (\n <button\n key={basket.id}\n type=\"button\"\n role=\"menuitemcheckbox\"\n aria-checked={shown}\n className={BASKET_OPTION}\n onClick={() => model.setBasketShown(basket.id, !shown)}\n >\n <span className=\"size-2.5 shrink-0 rounded-full\" style={{ backgroundColor: basket.color }} />\n <span className=\"truncate\">{basket.label}</span>\n {shown && <Icon name=\"i-lucide-check\" className=\"ml-auto size-3.5 shrink-0\" />}\n </button>\n )\n })}\n {shownBaskets.length > 0 && (\n <button type=\"button\" role=\"menuitem\" className={BASKET_OPTION} onClick={() => model.clearBasketFilter()}>\n <Icon name=\"i-lucide-filter-x\" className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{t('tree.basketFilter.reset')}</span>\n </button>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )}\n </div>\n\n <div className=\"flex items-center justify-end\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n {/* A disabled button swallows pointer events, so the tooltip hangs on the wrapper. */}\n <span className=\"inline-flex\">\n <Button\n size=\"xs\"\n variant={state.virtual ? 'secondary' : 'ghost'}\n disabled={!canUseFileRoot}\n aria-pressed={state.virtual}\n onClick={() => update((draft) => { draft.blocks.treeRoot = draft.blocks.treeRoot === 'file' ? 'folders' : 'file' })}\n >\n <Icon name={state.virtual ? 'i-lucide-file-stack' : 'i-lucide-folder-tree'} />\n {t(state.virtual ? 'tree.root.file' : 'tree.root.folders')}\n </Button>\n </span>\n </TooltipTrigger>\n {!canUseFileRoot && <TooltipContent>{t('tree.root.unavailable')}</TooltipContent>}\n </Tooltip>\n </TooltipProvider>\n </div>\n\n {view.hasKindFilters && (\n <div className=\"flex items-center\">\n <Button size=\"xs\" variant=\"secondary\" onClick={() => model.clearAllFilters()}>\n <Icon name=\"i-lucide-filter-x\" />\n {t('tree.filter.reset')}\n </Button>\n </div>\n )}\n\n {creatingFolder && (\n <div className=\"flex flex-col gap-1\">\n <Input\n value={creatingFolder.draft}\n className=\"h-7 text-sm md:text-sm\"\n autoFocus\n placeholder={creatingFolder.parent ? t('tree.folder.placeholderIn', { parent: creatingFolder.parent }) : t('tree.folder.placeholder')}\n aria-label={t('tree.folder.name')}\n aria-invalid={!!creatingFolder.error}\n onChange={event => model.setFolderDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') void model.commitCreateFolder()\n else if (event.key === 'Escape') model.cancelCreateFolder()\n }}\n onBlur={() => model.cancelCreateFolder()}\n />\n {creatingFolder.error && <p className=\"px-0.5 text-xs text-destructive\" role=\"alert\">{creatingFolder.error}</p>}\n </div>\n )}\n\n {crumbs.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-0.5 text-xs\">\n <Button size=\"xs\" variant=\"ghost\" title={t('tree.focus.leave')} aria-label={t('tree.focus.leave')} onClick={() => model.defocus()}>\n <Icon name=\"i-lucide-corner-up-left\" />\n {focusDocument?.label ?? t('tree.focus.root')}\n </Button>\n {crumbs.map((crumb, index) => (\n <Fragment key={crumb.key}>\n <Icon name=\"i-lucide-chevron-right\" className=\"size-3 shrink-0 text-muted-foreground\" />\n {index === crumbs.length - 1\n ? <span className=\"min-w-0 truncate px-1.5 font-medium\" aria-current=\"location\">{crumb.label}</span>\n : <Button size=\"xs\" variant=\"ghost\" onClick={() => model.focus(crumb.key)}>{crumb.label}</Button>}\n </Fragment>\n ))}\n </div>\n )}\n\n {!state.tree && !state.virtual\n ? <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{state.tree === null ? t('tree.loadFailed') : t('tree.loading')}</p>\n : renderItems(view.items, 0)}\n\n {view.noMatches && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.noMatches')}</p>}\n {view.isEmpty && <p className=\"px-2.5 py-1.5 text-sm text-muted-foreground\">{t('tree.empty')}</p>}\n </div>\n )\n}\n"],"mappings":"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"}
|
package/dist/tree/tree-host.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { findBlockById } from "../core/schema/block-ids.js";
|
|
2
|
-
import { compositeBasketsOf, compositeJSON
|
|
2
|
+
import { blockTypesOf, compositeBasketsOf, compositeJSON } from "../features/composites/nodes.js";
|
|
3
3
|
import { revealBlockInEditor } from "../outline/heading-outline.js";
|
|
4
4
|
import { pickFiles } from "../features/media/upload.js";
|
|
5
5
|
import { compositeFocusOf } from "../features/composites/focus.js";
|
|
6
6
|
import { moveBlockInto, moveIntoVerdict } from "../features/composites/outline.js";
|
|
7
7
|
import { readOutline } from "./tree-items.js";
|
|
8
8
|
//#region src/tree/tree-host.ts
|
|
9
|
-
/** The model's view of `editor`: its
|
|
9
|
+
/** The model's view of `editor`: its Block types, outline and move verdicts (worded in the language of `t`), read live. Create it once per editor. */
|
|
10
10
|
function treeEditorOf(editor, t) {
|
|
11
11
|
return {
|
|
12
|
-
kinds:
|
|
12
|
+
kinds: blockTypesOf(editor),
|
|
13
13
|
baskets: compositeBasketsOf(editor),
|
|
14
14
|
outline: (structure) => readOutline(editor, structure, t),
|
|
15
15
|
moveVerdict: (id, targetId) => editor.isDestroyed ? {
|
|
@@ -44,7 +44,7 @@ function treeEditorOf(editor, t) {
|
|
|
44
44
|
function compositeInsertPos(editor, blockId) {
|
|
45
45
|
const { doc } = editor.state;
|
|
46
46
|
const target = blockId ? findBlockById(doc, blockId) : null;
|
|
47
|
-
return target?.node.type.name === "
|
|
47
|
+
return target?.node.type.name === "typedBlock" ? target.pos + target.node.nodeSize - 1 : doc.content.size;
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Runs the model's commands against the host's store and the editor. Every `store` method reports its own failure
|
|
@@ -98,7 +98,7 @@ function createTreeExecutor(host) {
|
|
|
98
98
|
});
|
|
99
99
|
return !!editor;
|
|
100
100
|
}
|
|
101
|
-
case "
|
|
101
|
+
case "createFolderBlock": {
|
|
102
102
|
const editor = liveEditor();
|
|
103
103
|
if (!editor) return false;
|
|
104
104
|
const pos = compositeInsertPos(editor, command.targetId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-host.js","names":[],"sources":["../../src/tree/tree-host.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { findBlockById } from '../core/schema'\nimport { compositeBasketsOf, compositeFocusOf, compositeJSON, compositeKindsOf, moveBlockInto, moveIntoVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { pickFiles } from '../features/media'\nimport { revealBlockInEditor } from '../outline'\nimport type { DocumentStore } from './store'\nimport type { TreeCommand, TreeEditor, TreeExecutor } from './tree-model'\nimport { readOutline } from './tree-items'\n\n// What connects a `TreeModel` to the world, shared by the Vue and the React `DocumentTree`: the open editor as the model\n// sees it, and the executor of the commands the model emits.\n\n/** The model's view of `editor`: its Baustein kinds, outline and move verdicts (worded in the language of `t`), read live. Create it once per editor. */\nexport function treeEditorOf(editor: Editor, t: Translate): TreeEditor {\n return {\n kinds: compositeKindsOf(editor),\n baskets: compositeBasketsOf(editor),\n outline: structure => readOutline(editor, structure, t),\n moveVerdict: (id, targetId) => (editor.isDestroyed ? { ok: false, reason: t('tree.drop.editorClosed') } : moveIntoVerdict(editor, id, targetId, t)),\n canTransfer: () => !editor.isDestroyed && editor.storage.blockDropTarget !== undefined && typeof editor.commands.transferBlock === 'function',\n focusedId: () => (editor.isDestroyed ? null : compositeFocusOf(editor.state)),\n canMerge: (id) => {\n const found = editor.isDestroyed ? undefined : findBlockById(editor.state.doc, id)\n return !!found && editor.can().mergeCompositeWithNext(found.pos)\n },\n onChange(listener) {\n // A focus change is a transaction without a document change (the focus chip, a block's „Fokussieren“), so `update` does not see it.\n let focused = editor.isDestroyed ? null : compositeFocusOf(editor.state)\n const onTransaction = () => {\n if (editor.isDestroyed) return\n const now = compositeFocusOf(editor.state)\n if (now === focused) return\n focused = now\n listener()\n }\n editor.on('update', listener)\n editor.on('transaction', onTransaction)\n return () => {\n editor.off('update', listener)\n editor.off('transaction', onTransaction)\n }\n },\n }\n}\n\n/** What the executor needs from its framework binding. Every function reads the current value (props change). */\nexport interface TreeHost {\n store: () => DocumentStore\n editor: () => Editor | undefined\n /** The framework's file import (`useFileImport`). */\n importFiles: (editor: Editor, files: File[], pos: number) => Promise<unknown>\n /** Shows a warning (a toast). */\n warn: (title: string, description: string) => void\n /** Shows that a document was deleted, with an undo. */\n deleted: (title: string, undo: () => void) => void\n}\n\n/** The open composite file: end of composite `blockId`'s body, else (`null`) the end of the document. */\nfunction compositeInsertPos(editor: Editor, blockId: string | null): number {\n const { doc } = editor.state\n const target = blockId ? findBlockById(doc, blockId) : null\n return target?.node.type.name === 'composite' ? target.pos + target.node.nodeSize - 1 : doc.content.size\n}\n\n/**\n * Runs the model's commands against the host's store and the editor. Every `store` method reports its own failure\n * before it rejects, so a rejection only ends the command (`false`); the executor itself never rejects.\n */\nexport function createTreeExecutor(host: TreeHost): TreeExecutor {\n const succeeded = () => true\n const failed = () => false\n const liveEditor = () => {\n const editor = host.editor()\n return editor && !editor.isDestroyed ? editor : undefined\n }\n\n async function removeDocument(id: string, folder: string) {\n // The store leaves an open document and waits for its page teardown (which destroys its collaboration provider)\n // before it deletes.\n const store = host.store()\n const removed = await store.remove(id, folder).catch(() => undefined)\n if (!removed) return false\n host.deleted(removed.title, () => {\n void removed.restore().then(restored => store.open(restored.id)).catch(() => undefined)\n })\n return true\n }\n\n async function importIntoComposite(targetId: string | null) {\n const files = await pickFiles('*/*')\n const editor = liveEditor()\n if (!files.length || !editor) return false\n await host.importFiles(editor, files, compositeInsertPos(editor, targetId))\n return true\n }\n\n return (command: TreeCommand) => {\n const store = host.store()\n switch (command.type) {\n case 'openDocument':\n return store.open(command.id).then(succeeded, failed)\n case 'createDocument':\n return store.create(undefined, command.folder).then(async (doc) => {\n await store.open(doc.id).catch(() => undefined)\n return true\n }, failed)\n case 'moveDocument':\n return store.move(command.id, command.folder).then(succeeded, failed)\n case 'removeDocument':\n return removeDocument(command.id, command.folder)\n case 'createFolder':\n return store.createFolder(command.path).then(succeeded, failed)\n case 'moveFolder':\n return store.moveFolder(command.from, command.to).then(succeeded, failed)\n case 'removeFolder':\n // The server refuses non-empty folders; the toast then reads „Ordner ist nicht leer“.\n return store.removeFolder(command.path).then(succeeded, failed)\n case 'moveBlock': {\n const editor = liveEditor()\n return !!editor && moveBlockInto(editor, command.id, command.targetId)\n }\n case 'revealBlock': {\n const editor = liveEditor()\n if (editor) revealBlockInEditor(editor, { id: command.id, pos: command.pos })\n return !!editor\n }\n case 'createBausteinFolder': {\n const editor = liveEditor()\n if (!editor) return false\n const pos = compositeInsertPos(editor, command.targetId)\n // The caret goes into the new composite's title: one level in, then the text inside it.\n editor.chain().insertContentAt(pos, compositeJSON('folder')).setTextSelection(pos + 2).focus().scrollIntoView().run()\n return true\n }\n case 'importFiles':\n return importIntoComposite(command.targetId)\n case 'transferBlock': {\n const editor = liveEditor()\n // An editor without the transfer feature has no such command: the drop is refused (`false`), never thrown.\n if (!editor || typeof editor.commands.transferBlock !== 'function') return false\n return editor.commands.transferBlock({ payload: command.payload, pos: compositeInsertPos(editor, command.targetId), menu: command.menu, x: command.x, y: command.y })\n }\n case 'warn':\n host.warn(command.title, command.description)\n return true\n case 'focusBlock': {\n const editor = liveEditor()\n return !!editor && editor.commands.focusComposite(command.id)\n }\n case 'mergeBlock': {\n const editor = liveEditor()\n if (!editor) return false\n const found = findBlockById(editor.state.doc, command.id)\n return !!found && editor.commands.mergeCompositeWithNext(found.pos)\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tree-host.js","names":[],"sources":["../../src/tree/tree-host.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { findBlockById } from '../core/schema'\nimport { compositeBasketsOf, compositeFocusOf, compositeJSON, blockTypesOf, moveBlockInto, moveIntoVerdict } from '../features/composites'\nimport type { Translate } from '../i18n'\nimport { pickFiles } from '../features/media'\nimport { revealBlockInEditor } from '../outline'\nimport type { DocumentStore } from './store'\nimport type { TreeCommand, TreeEditor, TreeExecutor } from './tree-model'\nimport { readOutline } from './tree-items'\n\n// What connects a `TreeModel` to the world, shared by the Vue and the React `DocumentTree`: the open editor as the model\n// sees it, and the executor of the commands the model emits.\n\n/** The model's view of `editor`: its Block types, outline and move verdicts (worded in the language of `t`), read live. Create it once per editor. */\nexport function treeEditorOf(editor: Editor, t: Translate): TreeEditor {\n return {\n kinds: blockTypesOf(editor),\n baskets: compositeBasketsOf(editor),\n outline: structure => readOutline(editor, structure, t),\n moveVerdict: (id, targetId) => (editor.isDestroyed ? { ok: false, reason: t('tree.drop.editorClosed') } : moveIntoVerdict(editor, id, targetId, t)),\n canTransfer: () => !editor.isDestroyed && editor.storage.blockDropTarget !== undefined && typeof editor.commands.transferBlock === 'function',\n focusedId: () => (editor.isDestroyed ? null : compositeFocusOf(editor.state)),\n canMerge: (id) => {\n const found = editor.isDestroyed ? undefined : findBlockById(editor.state.doc, id)\n return !!found && editor.can().mergeCompositeWithNext(found.pos)\n },\n onChange(listener) {\n // A focus change is a transaction without a document change (the focus chip, a block's „Fokussieren“), so `update` does not see it.\n let focused = editor.isDestroyed ? null : compositeFocusOf(editor.state)\n const onTransaction = () => {\n if (editor.isDestroyed) return\n const now = compositeFocusOf(editor.state)\n if (now === focused) return\n focused = now\n listener()\n }\n editor.on('update', listener)\n editor.on('transaction', onTransaction)\n return () => {\n editor.off('update', listener)\n editor.off('transaction', onTransaction)\n }\n },\n }\n}\n\n/** What the executor needs from its framework binding. Every function reads the current value (props change). */\nexport interface TreeHost {\n store: () => DocumentStore\n editor: () => Editor | undefined\n /** The framework's file import (`useFileImport`). */\n importFiles: (editor: Editor, files: File[], pos: number) => Promise<unknown>\n /** Shows a warning (a toast). */\n warn: (title: string, description: string) => void\n /** Shows that a document was deleted, with an undo. */\n deleted: (title: string, undo: () => void) => void\n}\n\n/** The open composite file: end of composite `blockId`'s body, else (`null`) the end of the document. */\nfunction compositeInsertPos(editor: Editor, blockId: string | null): number {\n const { doc } = editor.state\n const target = blockId ? findBlockById(doc, blockId) : null\n return target?.node.type.name === 'typedBlock' ? target.pos + target.node.nodeSize - 1 : doc.content.size\n}\n\n/**\n * Runs the model's commands against the host's store and the editor. Every `store` method reports its own failure\n * before it rejects, so a rejection only ends the command (`false`); the executor itself never rejects.\n */\nexport function createTreeExecutor(host: TreeHost): TreeExecutor {\n const succeeded = () => true\n const failed = () => false\n const liveEditor = () => {\n const editor = host.editor()\n return editor && !editor.isDestroyed ? editor : undefined\n }\n\n async function removeDocument(id: string, folder: string) {\n // The store leaves an open document and waits for its page teardown (which destroys its collaboration provider)\n // before it deletes.\n const store = host.store()\n const removed = await store.remove(id, folder).catch(() => undefined)\n if (!removed) return false\n host.deleted(removed.title, () => {\n void removed.restore().then(restored => store.open(restored.id)).catch(() => undefined)\n })\n return true\n }\n\n async function importIntoComposite(targetId: string | null) {\n const files = await pickFiles('*/*')\n const editor = liveEditor()\n if (!files.length || !editor) return false\n await host.importFiles(editor, files, compositeInsertPos(editor, targetId))\n return true\n }\n\n return (command: TreeCommand) => {\n const store = host.store()\n switch (command.type) {\n case 'openDocument':\n return store.open(command.id).then(succeeded, failed)\n case 'createDocument':\n return store.create(undefined, command.folder).then(async (doc) => {\n await store.open(doc.id).catch(() => undefined)\n return true\n }, failed)\n case 'moveDocument':\n return store.move(command.id, command.folder).then(succeeded, failed)\n case 'removeDocument':\n return removeDocument(command.id, command.folder)\n case 'createFolder':\n return store.createFolder(command.path).then(succeeded, failed)\n case 'moveFolder':\n return store.moveFolder(command.from, command.to).then(succeeded, failed)\n case 'removeFolder':\n // The server refuses non-empty folders; the toast then reads „Ordner ist nicht leer“.\n return store.removeFolder(command.path).then(succeeded, failed)\n case 'moveBlock': {\n const editor = liveEditor()\n return !!editor && moveBlockInto(editor, command.id, command.targetId)\n }\n case 'revealBlock': {\n const editor = liveEditor()\n if (editor) revealBlockInEditor(editor, { id: command.id, pos: command.pos })\n return !!editor\n }\n case 'createFolderBlock': {\n const editor = liveEditor()\n if (!editor) return false\n const pos = compositeInsertPos(editor, command.targetId)\n // The caret goes into the new composite's title: one level in, then the text inside it.\n editor.chain().insertContentAt(pos, compositeJSON('folder')).setTextSelection(pos + 2).focus().scrollIntoView().run()\n return true\n }\n case 'importFiles':\n return importIntoComposite(command.targetId)\n case 'transferBlock': {\n const editor = liveEditor()\n // An editor without the transfer feature has no such command: the drop is refused (`false`), never thrown.\n if (!editor || typeof editor.commands.transferBlock !== 'function') return false\n return editor.commands.transferBlock({ payload: command.payload, pos: compositeInsertPos(editor, command.targetId), menu: command.menu, x: command.x, y: command.y })\n }\n case 'warn':\n host.warn(command.title, command.description)\n return true\n case 'focusBlock': {\n const editor = liveEditor()\n return !!editor && editor.commands.focusComposite(command.id)\n }\n case 'mergeBlock': {\n const editor = liveEditor()\n if (!editor) return false\n const found = findBlockById(editor.state.doc, command.id)\n return !!found && editor.commands.mergeCompositeWithNext(found.pos)\n }\n }\n }\n}\n"],"mappings":"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"}
|