@ecsplico/editor 1.0.0 → 2.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 +37 -0
- package/README.md +7 -7
- package/dist/adapters/anchors.d.ts +80 -0
- package/dist/adapters/anchors.js +270 -0
- package/dist/adapters/anchors.js.map +1 -0
- package/dist/adapters/comments/client.d.ts +64 -0
- package/dist/adapters/comments/client.js +536 -0
- package/dist/adapters/comments/client.js.map +1 -0
- package/dist/adapters/comments/drizzle.d.ts +430 -0
- package/dist/adapters/comments/drizzle.js +236 -0
- package/dist/adapters/comments/drizzle.js.map +1 -0
- package/dist/adapters/comments/errors.d.ts +9 -0
- package/dist/adapters/comments/errors.js +19 -0
- package/dist/adapters/comments/errors.js.map +1 -0
- package/dist/adapters/comments/json.d.ts +19 -0
- package/dist/adapters/comments/json.js +94 -0
- package/dist/adapters/comments/json.js.map +1 -0
- package/dist/adapters/comments/record.js +78 -0
- package/dist/adapters/comments/record.js.map +1 -0
- package/dist/adapters/comments/server.d.ts +23 -0
- package/dist/adapters/comments/server.js +102 -0
- package/dist/adapters/comments/server.js.map +1 -0
- package/dist/adapters/comments/types.d.ts +109 -0
- package/dist/adapters/documents.js +108 -0
- package/dist/adapters/documents.js.map +1 -0
- package/dist/adapters/drizzle.d.ts +293 -0
- package/dist/adapters/drizzle.js +157 -0
- package/dist/adapters/drizzle.js.map +1 -0
- package/dist/adapters/files.js +30 -0
- package/dist/adapters/files.js.map +1 -0
- package/dist/adapters/forge.d.ts +58 -0
- package/dist/adapters/forge.js +91 -0
- package/dist/adapters/forge.js.map +1 -0
- package/dist/adapters/index.d.ts +13 -0
- package/dist/adapters/index.js +0 -0
- package/dist/adapters/json.d.ts +24 -0
- package/dist/adapters/json.js +128 -0
- package/dist/adapters/json.js.map +1 -0
- package/dist/adapters/nitro.d.ts +61 -0
- package/dist/adapters/nitro.js +212 -0
- package/dist/adapters/nitro.js.map +1 -0
- package/dist/adapters/routes.js +16 -0
- package/dist/adapters/routes.js.map +1 -0
- package/dist/adapters/shared.d.ts +37 -0
- package/dist/adapters/shared.js +32 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/vue.d.ts +67 -0
- package/dist/adapters/vue.js +187 -0
- package/dist/adapters/vue.js.map +1 -0
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/core/doc-anchors.d.ts +5 -0
- package/dist/core/doc-anchors.js +4 -3
- package/dist/core/doc-anchors.js.map +1 -1
- package/dist/core/editor-ui.js +57 -1
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/notify.d.ts +1 -1
- package/dist/core/notify.js.map +1 -1
- package/dist/core/surface-session.js +139 -0
- package/dist/core/surface-session.js.map +1 -0
- package/dist/document/DocumentSurface.d.ts +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/agent-op-review.d.ts +2 -2
- package/dist/features/ai/agent-op-review.js.map +1 -1
- package/dist/features/ai/agent-ops.d.ts +1 -1
- package/dist/features/ai/agent-ops.js +9 -6
- package/dist/features/ai/agent-ops.js.map +1 -1
- package/dist/features/ai/ai-apply.d.ts +1 -1
- package/dist/features/ai/ai-review.js +10 -1
- package/dist/features/ai/ai-review.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +78 -252
- package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.vue.d.ts +2 -1
- package/dist/features/ai/components/AiSidebar.js +2 -2
- package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/components/DocumentChatOps.js +1 -0
- package/dist/features/ai/components/DocumentChatOps.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.js +1 -1
- package/dist/features/ai/components/SettingsAiTab.js +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/document-chat.d.ts +3 -2
- package/dist/features/ai/document-chat.js +16 -19
- package/dist/features/ai/document-chat.js.map +1 -1
- package/dist/features/ai/index.d.ts +5 -3
- package/dist/features/ai/index.js +4 -2
- package/dist/features/ai/protocol/agent-ops.d.ts +22 -17
- package/dist/features/ai/protocol/agent-ops.js +65 -39
- package/dist/features/ai/protocol/agent-ops.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.d.ts +89 -0
- package/dist/features/ai/protocol/block-lines.js +177 -0
- package/dist/features/ai/protocol/block-lines.js.map +1 -0
- package/dist/features/ai/protocol/chat.js +10 -10
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -2
- package/dist/features/ai/protocol/index.js +3 -2
- package/dist/features/ai/react/AiSelectionMenu.d.ts +3 -2
- package/dist/features/ai/react/AiSelectionMenu.js +64 -255
- package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/react/AiSidebar.js +2 -2
- package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/react/DocumentChatOps.js +1 -0
- package/dist/features/ai/react/DocumentChatOps.js.map +1 -1
- package/dist/features/ai/react/ModelQuickSwitch.js +1 -1
- package/dist/features/ai/react/SettingsAiTab.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/react/use-ai-selection-flow.js +34 -0
- package/dist/features/ai/react/use-ai-selection-flow.js.map +1 -0
- package/dist/features/ai/selection-flow.d.ts +133 -0
- package/dist/features/ai/selection-flow.js +283 -0
- package/dist/features/ai/selection-flow.js.map +1 -0
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-document-chat.d.ts +1 -1
- package/dist/features/authors/components/AuthorColorsPanel.js +1 -1
- package/dist/features/authors/feature.js +1 -1
- package/dist/features/authors/index.js +1 -1
- package/dist/features/authors/react/AuthorColorsPanel.js +1 -1
- package/dist/features/comments/components/CommentBubble.js +1 -1
- package/dist/features/comments/components/CommentComposer.js +1 -1
- package/dist/features/comments/components/CommentThreadView.js +5 -2
- package/dist/features/comments/components/CommentThreadView.js.map +1 -1
- package/dist/features/comments/components/CommentsMargin.js +108 -230
- package/dist/features/comments/components/CommentsMargin.js.map +1 -1
- package/dist/features/comments/components/CommentsOverlay.js +1 -1
- package/dist/features/comments/feature.js +1 -1
- package/dist/features/comments/index.d.ts +4 -2
- package/dist/features/comments/index.js +4 -2
- package/dist/features/comments/margin-controller.d.ts +44 -0
- package/dist/features/comments/margin-controller.js +132 -0
- package/dist/features/comments/margin-controller.js.map +1 -0
- package/dist/features/comments/margin-layout.d.ts +55 -0
- package/dist/features/comments/margin-layout.js +79 -0
- package/dist/features/comments/margin-layout.js.map +1 -0
- package/dist/features/comments/model.d.ts +8 -0
- package/dist/features/comments/model.js +60 -34
- package/dist/features/comments/model.js.map +1 -1
- package/dist/features/comments/react/CommentBubble.js +1 -1
- package/dist/features/comments/react/CommentComposer.js +1 -1
- package/dist/features/comments/react/CommentThreadView.js +2 -2
- package/dist/features/comments/react/CommentThreadView.js.map +1 -1
- package/dist/features/comments/react/CommentsMargin.d.ts +0 -5
- package/dist/features/comments/react/CommentsMargin.js +85 -251
- package/dist/features/comments/react/CommentsMargin.js.map +1 -1
- package/dist/features/comments/react/CommentsOverlay.js +1 -1
- package/dist/features/comments/react/use-comments.js +1 -1
- package/dist/features/comments/react/use-margin-layout.js +45 -0
- package/dist/features/comments/react/use-margin-layout.js.map +1 -0
- package/dist/features/comments/react.d.ts +1 -1
- package/dist/features/comments/schema.d.ts +23 -0
- package/dist/features/comments/schema.js.map +1 -1
- package/dist/features/comments/snapshot-equality.js +40 -0
- package/dist/features/comments/snapshot-equality.js.map +1 -0
- package/dist/features/comments/thread-anchor.js +33 -0
- package/dist/features/comments/thread-anchor.js.map +1 -0
- package/dist/features/comments/use-comments.d.ts +3 -1
- package/dist/features/comments/use-comments.js +1 -0
- package/dist/features/comments/use-comments.js.map +1 -1
- package/dist/features/comments/use-margin-layout.js +29 -0
- package/dist/features/comments/use-margin-layout.js.map +1 -0
- package/dist/features/comments/vue.d.ts +1 -1
- package/dist/features/composites/accepted-children.d.ts +21 -0
- package/dist/features/composites/accepted-children.js +126 -0
- package/dist/features/composites/accepted-children.js.map +1 -0
- package/dist/features/composites/components/CompositeDetails.js +1 -1
- package/dist/features/composites/components/CompositeInspector.js +38 -164
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/feature.d.ts +2 -1
- package/dist/features/composites/feature.js +8 -2
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/index.d.ts +5 -3
- package/dist/features/composites/index.js +8 -6
- package/dist/features/composites/inspector-model.d.ts +97 -0
- package/dist/features/composites/inspector-model.js +255 -0
- package/dist/features/composites/inspector-model.js.map +1 -0
- package/dist/features/composites/kinds.d.ts +11 -0
- package/dist/features/composites/kinds.js +17 -2
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/outline.d.ts +18 -5
- package/dist/features/composites/outline.js +44 -12
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.js +1 -1
- package/dist/features/composites/react/CompositeInspector.d.ts +1 -1
- package/dist/features/composites/react/CompositeInspector.js +21 -180
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.d.ts +1 -1
- package/dist/features/composites/react/CompositeView.js +1 -1
- package/dist/features/composites/react/use-composite-inspector.js +29 -0
- package/dist/features/composites/react/use-composite-inspector.js.map +1 -0
- package/dist/features/composites/schema.d.ts +2 -2
- package/dist/features/composites/schema.js +2 -2
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/use-composite-anchors.d.ts +1 -1
- package/dist/features/composites/use-composite-inspector.js +41 -0
- package/dist/features/composites/use-composite-inspector.js.map +1 -0
- package/dist/features/composites/use-composite-rows.d.ts +1 -1
- package/dist/features/media/index.d.ts +1 -1
- package/dist/features/media/vue.d.ts +1 -1
- package/dist/features/mentions/index.d.ts +3 -3
- package/dist/features/mentions/index.js +3 -3
- package/dist/features/mentions/schema.d.ts +1 -1
- package/dist/features/mentions/schema.js +1 -1
- package/dist/features/themes/index.d.ts +1 -1
- package/dist/features/themes/index.js +1 -1
- package/dist/features/transcript/feature.js +1 -1
- package/dist/features/transcript/index.js +2 -2
- package/dist/i18n/de/ai.js +2 -0
- package/dist/i18n/de/ai.js.map +1 -1
- package/dist/i18n/de/common.js +1 -2
- package/dist/i18n/de/common.js.map +1 -1
- package/dist/i18n/de/composites.js +8 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/tree.js +14 -1
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +22 -1
- package/dist/i18n/en/ai.js +2 -0
- package/dist/i18n/en/ai.js.map +1 -1
- package/dist/i18n/en/common.js +1 -2
- package/dist/i18n/en/common.js.map +1 -1
- package/dist/i18n/en/composites.js +8 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/tree.js +14 -1
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/integrations/composite-ai/composite-fill.js +2 -2
- package/dist/integrations/composite-ai/composite-summary.js +2 -2
- package/dist/integrations/composite-ai/index.js +2 -2
- package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
- package/dist/markdown/MarkdownSurface.d.ts +1 -1
- package/dist/markdown/MarkdownSurface.js +2 -2
- package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
- package/dist/markdown/index.d.ts +2 -2
- package/dist/nuxt/module.d.ts +0 -6
- package/dist/nuxt/module.js +3 -5
- package/dist/nuxt/module.js.map +1 -1
- package/dist/nuxt/runtime/install-context.d.ts +2 -3
- package/dist/nuxt/runtime/install-context.js +2 -3
- package/dist/nuxt/runtime/install-context.js.map +1 -1
- package/dist/nuxt/runtime/plugins/editor-context-nuxt-ui.client.ts +1 -1
- package/dist/nuxt/runtime/server/routes/collab/[id].js +1 -1
- package/dist/react/EditorSurface.d.ts +1 -1
- package/dist/react/EditorSurface.js +43 -149
- package/dist/react/EditorSurface.js.map +1 -1
- package/dist/react/EditorToolbar.js +3 -40
- package/dist/react/EditorToolbar.js.map +1 -1
- package/dist/react/styles/ui-vars.css +2 -2
- package/dist/schema/advanced.js +2 -2
- package/dist/server/adapter.d.ts +2 -2
- package/dist/server/collab.js +1 -1
- package/dist/server/handlers/ai.d.ts +4 -2
- package/dist/server/handlers/ai.js +11 -9
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +1 -1
- package/dist/server/handlers/editor.d.ts +1 -1
- package/dist/server/handlers/editor.js +2 -2
- package/dist/server/host.d.ts +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/settings/index.d.ts +1 -1
- package/dist/styles/ai.css +1 -1
- package/dist/styles/authors.css +1 -1
- package/dist/styles/block-ui.css +1 -1
- package/dist/styles/collab.css +1 -1
- package/dist/styles/comments.css +1 -1
- package/dist/styles/core.css +2 -2
- package/dist/styles/media.css +1 -1
- package/dist/styles/mentions.css +1 -1
- package/dist/styles/themes.css +1 -1
- package/dist/styles/tokens.css +1 -1
- package/dist/styles/transcript.css +1 -1
- package/dist/tree/DocumentTree.js +159 -387
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/drop-rules.js +37 -0
- package/dist/tree/drop-rules.js.map +1 -0
- package/dist/tree/react/DocumentTree.d.ts +2 -2
- package/dist/tree/react/DocumentTree.js +135 -397
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/react/use-tree-model.js +129 -0
- package/dist/tree/react/use-tree-model.js.map +1 -0
- package/dist/tree/react.js +1 -1
- package/dist/tree/tree-host.js +100 -0
- package/dist/tree/tree-host.js.map +1 -0
- package/dist/tree/tree-items.js +22 -43
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +580 -0
- package/dist/tree/tree-model.js.map +1 -0
- package/dist/tree/tree-rows.js +77 -0
- package/dist/tree/tree-rows.js.map +1 -0
- package/dist/tree/use-tree-model.js +94 -0
- package/dist/tree/use-tree-model.js.map +1 -0
- package/dist/tree/vue.js +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
- package/dist/vue/SimpleEditor.vue.d.ts +2 -2
- package/dist/vue/components/EditorSurface.js +27 -87
- package/dist/vue/components/EditorSurface.js.map +1 -1
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/dist/vue/components/EditorToolbar.js +7 -44
- package/dist/vue/components/EditorToolbar.js.map +1 -1
- package/dist/vue/context.d.ts +4 -4
- package/dist/vue/context.js +3 -2
- package/dist/vue/context.js.map +1 -1
- package/dist/vue/kit/types.d.ts +1 -1
- package/dist/vue/kit/use-ui-kit.d.ts +2 -2
- package/dist/vue/kit/use-ui-kit.js +3 -3
- package/dist/vue/kit/use-ui-kit.js.map +1 -1
- package/dist/vue/use-editor-ui.d.ts +1 -1
- package/dist/vue/use-notifier.d.ts +1 -1
- package/docs/integration.md +98 -5
- package/docs/llms.md +10 -2
- package/docs/usage.md +10 -5
- package/package.json +70 -23
- package/dist/nuxt/runtime/plugins/editor-context-primevue.client.ts +0 -8
- package/dist/vue/kit/primevue/Alert.js +0 -49
- package/dist/vue/kit/primevue/Alert.js.map +0 -1
- package/dist/vue/kit/primevue/Avatar.js +0 -54
- package/dist/vue/kit/primevue/Avatar.js.map +0 -1
- package/dist/vue/kit/primevue/Badge.js +0 -41
- package/dist/vue/kit/primevue/Badge.js.map +0 -1
- package/dist/vue/kit/primevue/Button.js +0 -112
- package/dist/vue/kit/primevue/Button.js.map +0 -1
- package/dist/vue/kit/primevue/Chip.js +0 -38
- package/dist/vue/kit/primevue/Chip.js.map +0 -1
- package/dist/vue/kit/primevue/DropdownMenu.js +0 -98
- package/dist/vue/kit/primevue/DropdownMenu.js.map +0 -1
- package/dist/vue/kit/primevue/FieldGroup.js +0 -22
- package/dist/vue/kit/primevue/FieldGroup.js.map +0 -1
- package/dist/vue/kit/primevue/FormField.js +0 -56
- package/dist/vue/kit/primevue/FormField.js.map +0 -1
- package/dist/vue/kit/primevue/Icon.js +0 -23
- package/dist/vue/kit/primevue/Icon.js.map +0 -1
- package/dist/vue/kit/primevue/Input.js +0 -96
- package/dist/vue/kit/primevue/Input.js.map +0 -1
- package/dist/vue/kit/primevue/InputMenu.js +0 -122
- package/dist/vue/kit/primevue/InputMenu.js.map +0 -1
- package/dist/vue/kit/primevue/Modal.js +0 -59
- package/dist/vue/kit/primevue/Modal.js.map +0 -1
- package/dist/vue/kit/primevue/Popover.js +0 -135
- package/dist/vue/kit/primevue/Popover.js.map +0 -1
- package/dist/vue/kit/primevue/RadioGroup.js +0 -58
- package/dist/vue/kit/primevue/RadioGroup.js.map +0 -1
- package/dist/vue/kit/primevue/Select.js +0 -68
- package/dist/vue/kit/primevue/Select.js.map +0 -1
- package/dist/vue/kit/primevue/SelectMenu.js +0 -94
- package/dist/vue/kit/primevue/SelectMenu.js.map +0 -1
- package/dist/vue/kit/primevue/Separator.js +0 -16
- package/dist/vue/kit/primevue/Separator.js.map +0 -1
- package/dist/vue/kit/primevue/Switch.js +0 -46
- package/dist/vue/kit/primevue/Switch.js.map +0 -1
- package/dist/vue/kit/primevue/Table.js +0 -54
- package/dist/vue/kit/primevue/Table.js.map +0 -1
- package/dist/vue/kit/primevue/Tabs.js +0 -49
- package/dist/vue/kit/primevue/Tabs.js.map +0 -1
- package/dist/vue/kit/primevue/Textarea.js +0 -55
- package/dist/vue/kit/primevue/Textarea.js.map +0 -1
- package/dist/vue/kit/primevue/Tooltip.js +0 -43
- package/dist/vue/kit/primevue/Tooltip.js.map +0 -1
- package/dist/vue/kit/primevue/Tree.js +0 -75
- package/dist/vue/kit/primevue/Tree.js.map +0 -1
- package/dist/vue/kit/primevue/select-options.js +0 -55
- package/dist/vue/kit/primevue/select-options.js.map +0 -1
- package/dist/vue/kit/primevue/shared.js +0 -49
- package/dist/vue/kit/primevue/shared.js.map +0 -1
- package/dist/vue/kit/primevue/trigger.js +0 -25
- package/dist/vue/kit/primevue/trigger.js.map +0 -1
- package/dist/vue/kit/primevue.css +0 -86
- package/dist/vue/kit/primevue.d.ts +0 -33
- package/dist/vue/kit/primevue.js +0 -96
- package/dist/vue/kit/primevue.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { commentsState } from "../comments-plugin.js";
|
|
1
2
|
import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
|
|
2
3
|
import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
|
|
3
|
-
import { commentsState } from "../comments-plugin.js";
|
|
4
4
|
import { useComments } from "../use-comments.js";
|
|
5
5
|
import { createBlock, createElementVNode, createVNode, defineComponent, openBlock, unref, withCtx } from "vue";
|
|
6
6
|
import { BubbleMenu } from "@tiptap/vue-3/menus";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { COMMENT_LIMITS } from "../schema.js";
|
|
1
2
|
import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
|
|
2
3
|
import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
|
|
3
|
-
import { COMMENT_LIMITS } from "../schema.js";
|
|
4
4
|
import { createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, unref } from "vue";
|
|
5
5
|
//#region src/features/comments/components/CommentComposer.vue
|
|
6
6
|
const _hoisted_1 = ["title"];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { COMMENT_LIMITS } from "../schema.js";
|
|
1
2
|
import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
|
|
2
3
|
import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
|
|
3
|
-
import { COMMENT_LIMITS } from "../schema.js";
|
|
4
4
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, ref, renderList, toDisplayString, unref, withCtx } from "vue";
|
|
5
5
|
//#region src/features/comments/components/CommentThreadView.vue
|
|
6
6
|
const _hoisted_1 = {
|
|
@@ -117,7 +117,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
117
117
|
}
|
|
118
118
|
return (_ctx, _cache) => {
|
|
119
119
|
return openBlock(), createElementBlock("div", {
|
|
120
|
-
class: normalizeClass(["flex flex-col text-sm", __props.dense ? "gap-1.5" : ["gap-3 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] p-3", {
|
|
120
|
+
class: normalizeClass(["flex flex-col text-sm", __props.dense ? "gap-1.5" : ["gap-3 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] p-3", {
|
|
121
|
+
"ring-2 ring-[var(--sk-primary)]": __props.active,
|
|
122
|
+
"opacity-70": __props.orphan
|
|
123
|
+
}]]),
|
|
121
124
|
onClick: _cache[7] || (_cache[7] = ($event) => emit("click"))
|
|
122
125
|
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(entries.value, (entry) => {
|
|
123
126
|
return openBlock(), createElementBlock("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentThreadView.js","names":["_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createElementVNode","_createVNode","_unref","_normalizeStyle","_toDisplayString","_createTextVNode","_createCommentVNode","_createBlock","_withCtx"],"sources":["../../../../src/features/comments/components/CommentThreadView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { DropdownItem } from '../../../vue/kit/types'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { computed, ref } from 'vue'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { AuthorIdentity } from '../../../core'\nimport { COMMENT_LIMITS, type CommentAuthor, type CommentThread } from '../schema'\nimport type { CommentsStore } from '../use-comments'\n\n/**\n * One comment thread as a card: root comment (with the commented text, or „Textstelle gelöscht“ for an orphan),\n * replies, a reply field. Everyone may resolve/reopen; editing and deleting are offered on the user's own comments\n * only. Layout-independent: mutations go through the `comments` store; a click on the card emits `click` (layouts\n * activate and reveal the thread). `dense`: compact, frameless rendering for a popover (name and time on one line,\n * one-line quote, icon-only send button).\n */\nconst props = defineProps<{\n thread: CommentThread\n user: AuthorIdentity | null\n active: boolean\n orphan: boolean\n comments: CommentsStore\n dense?: boolean\n}>()\nconst emit = defineEmits<{ click: [] }>()\nconst { Avatar: UAvatar, Badge: UBadge, Button: UButton, DropdownMenu: UDropdownMenu, Textarea: UTextarea } = useUiKit()\nconst { t, locale } = useEditorI18n()\n\n/** The root comment (`replyId` null) and the replies, rendered with the same row layout. */\ninterface Entry { replyId: string | null, author: CommentAuthor, text: string, createdAt: string, editedAt?: string }\n\nconst entries = computed<Entry[]>(() => [\n { replyId: null, author: props.thread.author, text: props.thread.text, createdAt: props.thread.createdAt, editedAt: props.thread.editedAt },\n ...props.thread.replies.map(r => ({ replyId: r.id, author: r.author, text: r.text, createdAt: r.createdAt, editedAt: r.editedAt })),\n])\n\nconst TIME_FORMAT = new Intl.DateTimeFormat(locale, { dateStyle: 'short', timeStyle: 'short' })\nconst formatTime = (iso: string) => TIME_FORMAT.format(new Date(iso))\nconst initials = (name: string) => name.slice(0, 2).toUpperCase()\n\n/** The entry being edited and its new text. */\nconst editing = ref<{ replyId: string | null, text: string } | null>(null)\nconst replyText = ref('')\n\nfunction menuItems(entry: Entry): DropdownItem[][] {\n const own = !!props.user && entry.author.id === props.user.id\n const groups: DropdownItem[][] = []\n if (own) {\n groups.push([\n { label: t('comments.edit'), icon: 'i-lucide-pencil', onSelect: () => (editing.value = { replyId: entry.replyId, text: entry.text }) },\n {\n label: t('comments.delete'),\n icon: 'i-lucide-trash-2',\n color: 'error',\n onSelect: () => entry.replyId ? props.comments.removeReply(props.thread.id, entry.replyId) : props.comments.remove(props.thread.id),\n },\n ])\n }\n if (entry.replyId === null) {\n const resolved = props.thread.resolved\n groups.push([{\n label: resolved ? t('comments.reopen') : t('comments.resolved'),\n icon: resolved ? 'i-lucide-rotate-ccw' : 'i-lucide-check',\n onSelect: () => props.comments.setResolved(props.thread.id, !resolved),\n }])\n }\n return groups\n}\n\nfunction saveEdit() {\n const value = editing.value\n if (!value?.text.trim()) return\n props.comments.edit(props.thread.id, value.replyId, value.text)\n editing.value = null\n}\n\nfunction sendReply() {\n if (!replyText.value.trim()) return\n props.comments.reply(props.thread.id, replyText.value)\n replyText.value = ''\n}\n\n/** Mod-Enter runs `submit`, Esc `cancel` (when given). */\nfunction onKeydown(event: KeyboardEvent, submit: () => void, cancel?: () => void) {\n if (event.isComposing) return\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n submit()\n }\n else if (event.key === 'Escape' && cancel) {\n event.preventDefault()\n cancel()\n }\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-col text-sm\"\n :class=\"dense ? 'gap-1.5' : ['gap-3 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] p-3', { 'ring-2 ring-[var(--sk-primary)]': active }]\"\n @click=\"emit('click')\"\n >\n <div\n v-for=\"entry in entries\"\n :key=\"entry.replyId ?? 'root'\"\n class=\"flex flex-col\"\n :class=\"[dense ? 'gap-0.5' : 'gap-1', entry.replyId ? (dense ? 'ml-2 border-l border-[var(--sk-border)] pl-2' : 'ml-4 border-l border-[var(--sk-border)] pl-3') : '']\"\n >\n <div class=\"flex items-center\" :class=\"dense ? 'gap-1.5' : 'gap-2'\">\n <UAvatar\n :size=\"dense ? '2xs' : 'xs'\"\n :text=\"initials(entry.author.name)\"\n :alt=\"entry.author.name\"\n :style=\"{ backgroundColor: entry.author.color }\"\n />\n <div v-if=\"dense\" class=\"flex min-w-0 flex-1 items-baseline gap-1.5 text-xs\">\n <span class=\"truncate font-medium\">{{ entry.author.name }}</span>\n <span class=\"shrink-0 text-[var(--sk-muted)]\">\n {{ formatTime(entry.createdAt) }}<template v-if=\"entry.editedAt\">\n {{ t('comments.edited') }}\n </template>\n </span>\n </div>\n <div v-else class=\"min-w-0 flex-1\">\n <div class=\"truncate font-medium\">\n {{ entry.author.name }}\n </div>\n <div class=\"text-xs text-[var(--sk-muted)]\">\n {{ formatTime(entry.createdAt) }}<template v-if=\"entry.editedAt\">\n {{ t('comments.edited') }}\n </template>\n </div>\n </div>\n <UDropdownMenu v-if=\"menuItems(entry).length\" :items=\"menuItems(entry)\" :content=\"{ align: 'end' }\">\n <UButton size=\"xs\" color=\"neutral\" variant=\"ghost\" icon=\"i-lucide-ellipsis-vertical\" :aria-label=\"t('comments.actions')\" />\n </UDropdownMenu>\n </div>\n\n <template v-if=\"entry.replyId === null\">\n <UBadge v-if=\"orphan\" :size=\"dense ? 'sm' : 'md'\" color=\"neutral\" variant=\"subtle\" :label=\"t('comments.orphan')\" class=\"self-start\" />\n <p\n v-else-if=\"thread.quote\"\n class=\"border-l-2 border-[var(--sk-warning)] pl-2 text-[var(--sk-muted)] italic\"\n :class=\"dense ? 'truncate text-xs' : 'line-clamp-2'\"\n :title=\"dense ? thread.quote : undefined\"\n >\n {{ thread.quote }}\n </p>\n </template>\n\n <div v-if=\"editing && editing.replyId === entry.replyId\" class=\"flex flex-col\" :class=\"dense ? 'gap-1.5' : 'gap-2'\">\n <UTextarea\n v-model=\"editing.text\"\n autofocus\n autoresize\n :size=\"dense ? 'xs' : 'md'\"\n :rows=\"1\"\n :maxrows=\"8\"\n :maxlength=\"COMMENT_LIMITS.text\"\n :aria-label=\"t('comments.editLabel')\"\n class=\"w-full\"\n @keydown=\"onKeydown($event, saveEdit, () => (editing = null))\"\n />\n <div class=\"flex justify-end\" :class=\"dense ? 'gap-1' : 'gap-2'\">\n <UButton size=\"xs\" color=\"neutral\" variant=\"ghost\" :label=\"t('comments.cancel')\" @click=\"editing = null\" />\n <UButton size=\"xs\" :label=\"t('comments.save')\" :disabled=\"!editing.text.trim()\" @click=\"saveEdit\" />\n </div>\n </div>\n <p v-else class=\"break-words whitespace-pre-wrap\">\n {{ entry.text }}\n </p>\n </div>\n\n <div v-if=\"dense\" class=\"flex items-end gap-1\">\n <UTextarea\n v-model=\"replyText\"\n autoresize\n size=\"xs\"\n :rows=\"1\"\n :maxrows=\"4\"\n :maxlength=\"COMMENT_LIMITS.text\"\n :placeholder=\"t('comments.reply.placeholder')\"\n :aria-label=\"t('comments.reply.label')\"\n class=\"min-w-0 flex-1\"\n @keydown=\"onKeydown($event, sendReply)\"\n />\n <UButton size=\"xs\" color=\"neutral\" variant=\"soft\" icon=\"i-lucide-send\" :aria-label=\"t('comments.reply.submit')\" :disabled=\"!replyText.trim()\" @click=\"sendReply\" />\n </div>\n <div v-else class=\"flex items-end gap-2\">\n <UTextarea\n v-model=\"replyText\"\n autoresize\n :rows=\"1\"\n :maxrows=\"6\"\n :maxlength=\"COMMENT_LIMITS.text\"\n :placeholder=\"t('comments.reply.placeholder')\"\n :aria-label=\"t('comments.reply.label')\"\n class=\"min-w-0 flex-1\"\n @keydown=\"onKeydown($event, sendReply)\"\n />\n <UButton size=\"sm\" color=\"neutral\" variant=\"soft\" :label=\"t('comments.reply.submit')\" :disabled=\"!replyText.trim()\" @click=\"sendReply\" />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommentThreadView.js","names":["_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_createElementVNode","_createVNode","_unref","_normalizeStyle","_toDisplayString","_createTextVNode","_createCommentVNode","_createBlock","_withCtx"],"sources":["../../../../src/features/comments/components/CommentThreadView.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { DropdownItem } from '../../../vue/kit/types'\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport { computed, ref } from 'vue'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport type { AuthorIdentity } from '../../../core'\nimport { COMMENT_LIMITS, type CommentAuthor, type CommentThread } from '../schema'\nimport type { CommentsStore } from '../use-comments'\n\n/**\n * One comment thread as a card: root comment (with the commented text, or „Textstelle gelöscht“ for an orphan),\n * replies, a reply field. Everyone may resolve/reopen; editing and deleting are offered on the user's own comments\n * only. Layout-independent: mutations go through the `comments` store; a click on the card emits `click` (layouts\n * activate and reveal the thread). `dense`: compact, frameless rendering for a popover (name and time on one line,\n * one-line quote, icon-only send button).\n */\nconst props = defineProps<{\n thread: CommentThread\n user: AuthorIdentity | null\n active: boolean\n orphan: boolean\n comments: CommentsStore\n dense?: boolean\n}>()\nconst emit = defineEmits<{ click: [] }>()\nconst { Avatar: UAvatar, Badge: UBadge, Button: UButton, DropdownMenu: UDropdownMenu, Textarea: UTextarea } = useUiKit()\nconst { t, locale } = useEditorI18n()\n\n/** The root comment (`replyId` null) and the replies, rendered with the same row layout. */\ninterface Entry { replyId: string | null, author: CommentAuthor, text: string, createdAt: string, editedAt?: string }\n\nconst entries = computed<Entry[]>(() => [\n { replyId: null, author: props.thread.author, text: props.thread.text, createdAt: props.thread.createdAt, editedAt: props.thread.editedAt },\n ...props.thread.replies.map(r => ({ replyId: r.id, author: r.author, text: r.text, createdAt: r.createdAt, editedAt: r.editedAt })),\n])\n\nconst TIME_FORMAT = new Intl.DateTimeFormat(locale, { dateStyle: 'short', timeStyle: 'short' })\nconst formatTime = (iso: string) => TIME_FORMAT.format(new Date(iso))\nconst initials = (name: string) => name.slice(0, 2).toUpperCase()\n\n/** The entry being edited and its new text. */\nconst editing = ref<{ replyId: string | null, text: string } | null>(null)\nconst replyText = ref('')\n\nfunction menuItems(entry: Entry): DropdownItem[][] {\n const own = !!props.user && entry.author.id === props.user.id\n const groups: DropdownItem[][] = []\n if (own) {\n groups.push([\n { label: t('comments.edit'), icon: 'i-lucide-pencil', onSelect: () => (editing.value = { replyId: entry.replyId, text: entry.text }) },\n {\n label: t('comments.delete'),\n icon: 'i-lucide-trash-2',\n color: 'error',\n onSelect: () => entry.replyId ? props.comments.removeReply(props.thread.id, entry.replyId) : props.comments.remove(props.thread.id),\n },\n ])\n }\n if (entry.replyId === null) {\n const resolved = props.thread.resolved\n groups.push([{\n label: resolved ? t('comments.reopen') : t('comments.resolved'),\n icon: resolved ? 'i-lucide-rotate-ccw' : 'i-lucide-check',\n onSelect: () => props.comments.setResolved(props.thread.id, !resolved),\n }])\n }\n return groups\n}\n\nfunction saveEdit() {\n const value = editing.value\n if (!value?.text.trim()) return\n props.comments.edit(props.thread.id, value.replyId, value.text)\n editing.value = null\n}\n\nfunction sendReply() {\n if (!replyText.value.trim()) return\n props.comments.reply(props.thread.id, replyText.value)\n replyText.value = ''\n}\n\n/** Mod-Enter runs `submit`, Esc `cancel` (when given). */\nfunction onKeydown(event: KeyboardEvent, submit: () => void, cancel?: () => void) {\n if (event.isComposing) return\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n submit()\n }\n else if (event.key === 'Escape' && cancel) {\n event.preventDefault()\n cancel()\n }\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-col text-sm\"\n :class=\"dense ? 'gap-1.5' : ['gap-3 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] p-3', { 'ring-2 ring-[var(--sk-primary)]': active, 'opacity-70': orphan }]\"\n @click=\"emit('click')\"\n >\n <div\n v-for=\"entry in entries\"\n :key=\"entry.replyId ?? 'root'\"\n class=\"flex flex-col\"\n :class=\"[dense ? 'gap-0.5' : 'gap-1', entry.replyId ? (dense ? 'ml-2 border-l border-[var(--sk-border)] pl-2' : 'ml-4 border-l border-[var(--sk-border)] pl-3') : '']\"\n >\n <div class=\"flex items-center\" :class=\"dense ? 'gap-1.5' : 'gap-2'\">\n <UAvatar\n :size=\"dense ? '2xs' : 'xs'\"\n :text=\"initials(entry.author.name)\"\n :alt=\"entry.author.name\"\n :style=\"{ backgroundColor: entry.author.color }\"\n />\n <div v-if=\"dense\" class=\"flex min-w-0 flex-1 items-baseline gap-1.5 text-xs\">\n <span class=\"truncate font-medium\">{{ entry.author.name }}</span>\n <span class=\"shrink-0 text-[var(--sk-muted)]\">\n {{ formatTime(entry.createdAt) }}<template v-if=\"entry.editedAt\">\n {{ t('comments.edited') }}\n </template>\n </span>\n </div>\n <div v-else class=\"min-w-0 flex-1\">\n <div class=\"truncate font-medium\">\n {{ entry.author.name }}\n </div>\n <div class=\"text-xs text-[var(--sk-muted)]\">\n {{ formatTime(entry.createdAt) }}<template v-if=\"entry.editedAt\">\n {{ t('comments.edited') }}\n </template>\n </div>\n </div>\n <UDropdownMenu v-if=\"menuItems(entry).length\" :items=\"menuItems(entry)\" :content=\"{ align: 'end' }\">\n <UButton size=\"xs\" color=\"neutral\" variant=\"ghost\" icon=\"i-lucide-ellipsis-vertical\" :aria-label=\"t('comments.actions')\" />\n </UDropdownMenu>\n </div>\n\n <template v-if=\"entry.replyId === null\">\n <UBadge v-if=\"orphan\" :size=\"dense ? 'sm' : 'md'\" color=\"neutral\" variant=\"subtle\" :label=\"t('comments.orphan')\" class=\"self-start\" />\n <p\n v-else-if=\"thread.quote\"\n class=\"border-l-2 border-[var(--sk-warning)] pl-2 text-[var(--sk-muted)] italic\"\n :class=\"dense ? 'truncate text-xs' : 'line-clamp-2'\"\n :title=\"dense ? thread.quote : undefined\"\n >\n {{ thread.quote }}\n </p>\n </template>\n\n <div v-if=\"editing && editing.replyId === entry.replyId\" class=\"flex flex-col\" :class=\"dense ? 'gap-1.5' : 'gap-2'\">\n <UTextarea\n v-model=\"editing.text\"\n autofocus\n autoresize\n :size=\"dense ? 'xs' : 'md'\"\n :rows=\"1\"\n :maxrows=\"8\"\n :maxlength=\"COMMENT_LIMITS.text\"\n :aria-label=\"t('comments.editLabel')\"\n class=\"w-full\"\n @keydown=\"onKeydown($event, saveEdit, () => (editing = null))\"\n />\n <div class=\"flex justify-end\" :class=\"dense ? 'gap-1' : 'gap-2'\">\n <UButton size=\"xs\" color=\"neutral\" variant=\"ghost\" :label=\"t('comments.cancel')\" @click=\"editing = null\" />\n <UButton size=\"xs\" :label=\"t('comments.save')\" :disabled=\"!editing.text.trim()\" @click=\"saveEdit\" />\n </div>\n </div>\n <p v-else class=\"break-words whitespace-pre-wrap\">\n {{ entry.text }}\n </p>\n </div>\n\n <div v-if=\"dense\" class=\"flex items-end gap-1\">\n <UTextarea\n v-model=\"replyText\"\n autoresize\n size=\"xs\"\n :rows=\"1\"\n :maxrows=\"4\"\n :maxlength=\"COMMENT_LIMITS.text\"\n :placeholder=\"t('comments.reply.placeholder')\"\n :aria-label=\"t('comments.reply.label')\"\n class=\"min-w-0 flex-1\"\n @keydown=\"onKeydown($event, sendReply)\"\n />\n <UButton size=\"xs\" color=\"neutral\" variant=\"soft\" icon=\"i-lucide-send\" :aria-label=\"t('comments.reply.submit')\" :disabled=\"!replyText.trim()\" @click=\"sendReply\" />\n </div>\n <div v-else class=\"flex items-end gap-2\">\n <UTextarea\n v-model=\"replyText\"\n autoresize\n :rows=\"1\"\n :maxrows=\"6\"\n :maxlength=\"COMMENT_LIMITS.text\"\n :placeholder=\"t('comments.reply.placeholder')\"\n :aria-label=\"t('comments.reply.label')\"\n class=\"min-w-0 flex-1\"\n @keydown=\"onKeydown($event, sendReply)\"\n />\n <UButton size=\"sm\" color=\"neutral\" variant=\"soft\" :label=\"t('comments.reply.submit')\" :disabled=\"!replyText.trim()\" @click=\"sendReply\" />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { DRAFT_CARD, marginCards } from "../margin-layout.js";
|
|
1
2
|
import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
|
|
2
3
|
import { useEditorSettings } from "../../../vue/settings.js";
|
|
3
4
|
import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
|
|
4
5
|
import { useComments } from "../use-comments.js";
|
|
5
6
|
import _sfc_main$1 from "./CommentComposer.js";
|
|
6
7
|
import _sfc_main$2 from "./CommentThreadView.js";
|
|
7
|
-
import {
|
|
8
|
+
import { useMarginLayout } from "../use-margin-layout.js";
|
|
9
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, ref, renderList, toDisplayString, unref, vShow, withDirectives } from "vue";
|
|
8
10
|
//#region src/features/comments/components/CommentsMargin.vue
|
|
9
11
|
const _hoisted_1 = ["aria-label"];
|
|
10
12
|
const _hoisted_2 = {
|
|
@@ -16,11 +18,12 @@ const _hoisted_4 = { class: "shrink-0 font-medium" };
|
|
|
16
18
|
const _hoisted_5 = { class: "min-w-0 flex-1 truncate text-[var(--sk-muted)]" };
|
|
17
19
|
const _hoisted_6 = {
|
|
18
20
|
key: 0,
|
|
21
|
+
class: "shrink-0 text-xs text-[var(--sk-fg-dimmed)] italic"
|
|
22
|
+
};
|
|
23
|
+
const _hoisted_7 = {
|
|
24
|
+
key: 1,
|
|
19
25
|
class: "shrink-0 text-xs text-[var(--sk-fg-dimmed)]"
|
|
20
26
|
};
|
|
21
|
-
const GAP = 8;
|
|
22
|
-
const DRAFT = "__draft";
|
|
23
|
-
/** Draft and open threads with a text range, in document order. */
|
|
24
27
|
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
25
28
|
__name: "CommentsMargin",
|
|
26
29
|
props: {
|
|
@@ -34,135 +37,36 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
34
37
|
setup(__props) {
|
|
35
38
|
/**
|
|
36
39
|
* Comments margin („Randspalte“): a 300px column the host places inside its scroll container right of the editor,
|
|
37
|
-
* so it scrolls with the text.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* so it scrolls with the text. Layout „Aktiver Faden“ (`layoutMargin`): the active thread's card sits exactly at the
|
|
41
|
+
* top of its text's line and expands, the others collapse to one line and give way up and down; without an active
|
|
42
|
+
* thread the cards stack top-down at their lines. The draft composer takes part like an active card. An orphaned thread
|
|
43
|
+
* (its text was deleted) is greyed out and sits where the text was; resolved threads („Erledigt“) sit at the bottom
|
|
44
|
+
* behind a toggle. Shown only while `open` (the host binds the surface's `commentsOpen` model) and the user's comments
|
|
45
|
+
* layout is `margin`; the layout is recomputed whenever the column becomes visible again.
|
|
42
46
|
*/
|
|
43
47
|
const props = __props;
|
|
44
48
|
const { settings } = useEditorSettings();
|
|
45
49
|
const { t } = useEditorI18n();
|
|
46
50
|
const enabled = computed(() => settings.value.editing.commentsLayout === "margin");
|
|
47
51
|
const comments = useComments(() => enabled.value ? props.editor : void 0);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
/** Draft and open threads in document order, each at its text (an orphan where its text was). */
|
|
53
|
+
const cards = computed(() => marginCards({
|
|
54
|
+
draft: comments.draft.value,
|
|
55
|
+
open: comments.open.value,
|
|
56
|
+
ranges: comments.ranges.value,
|
|
57
|
+
orphanAt: comments.orphanAt.value
|
|
58
|
+
}));
|
|
59
|
+
/** The card that owns its line: the draft composer, else the active thread. */
|
|
60
|
+
const activeId = computed(() => comments.draft.value ? DRAFT_CARD : comments.active.value);
|
|
61
|
+
const { state: layout, setColumn, setResolvedSection, setCard } = useMarginLayout({
|
|
62
|
+
editor: () => props.editor,
|
|
63
|
+
container: () => props.container,
|
|
64
|
+
source: {
|
|
65
|
+
cards: () => cards.value,
|
|
66
|
+
activeId: () => activeId.value
|
|
63
67
|
}
|
|
64
|
-
return items.sort((a, b) => a.from - b.from);
|
|
65
68
|
});
|
|
66
|
-
const orphans = computed(() => comments.open.value.filter((thread) => comments.isOrphan(thread.id)));
|
|
67
|
-
const column = ref();
|
|
68
|
-
const resolvedSection = ref();
|
|
69
69
|
const showResolved = ref(false);
|
|
70
|
-
const tops = ref({});
|
|
71
|
-
const resolvedTop = ref(0);
|
|
72
|
-
const minHeight = ref(0);
|
|
73
|
-
const cards = /* @__PURE__ */ new Map();
|
|
74
|
-
const observed = /* @__PURE__ */ new WeakSet();
|
|
75
|
-
let frame = 0;
|
|
76
|
-
const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => schedule());
|
|
77
|
-
function observe(el) {
|
|
78
|
-
if (!resizeObserver || observed.has(el)) return;
|
|
79
|
-
observed.add(el);
|
|
80
|
-
resizeObserver.observe(el);
|
|
81
|
-
}
|
|
82
|
-
function unobserve(el) {
|
|
83
|
-
resizeObserver?.unobserve(el);
|
|
84
|
-
observed.delete(el);
|
|
85
|
-
}
|
|
86
|
-
function setCard(key, el) {
|
|
87
|
-
if (!(el instanceof HTMLElement)) return;
|
|
88
|
-
cards.set(key, el);
|
|
89
|
-
observe(el);
|
|
90
|
-
}
|
|
91
|
-
function schedule() {
|
|
92
|
-
if (frame || typeof requestAnimationFrame === "undefined") return;
|
|
93
|
-
frame = requestAnimationFrame(relayout);
|
|
94
|
-
}
|
|
95
|
-
function relayout() {
|
|
96
|
-
frame = 0;
|
|
97
|
-
const editor = props.editor;
|
|
98
|
-
const col = column.value;
|
|
99
|
-
if (!editor || editor.isDestroyed || !col || !col.getClientRects().length) return;
|
|
100
|
-
const live = /* @__PURE__ */ new Set([...positioned.value.map((i) => i.key), ...orphans.value.map((thread) => thread.id)]);
|
|
101
|
-
for (const [key, el] of cards) {
|
|
102
|
-
if (live.has(key)) continue;
|
|
103
|
-
unobserve(el);
|
|
104
|
-
cards.delete(key);
|
|
105
|
-
}
|
|
106
|
-
const base = col.getBoundingClientRect().top;
|
|
107
|
-
const size = editor.state.doc.content.size;
|
|
108
|
-
const next = {};
|
|
109
|
-
let bottom = -8;
|
|
110
|
-
for (const item of positioned.value) {
|
|
111
|
-
let desired = bottom + GAP;
|
|
112
|
-
try {
|
|
113
|
-
desired = editor.view.coordsAtPos(Math.min(item.from, size)).top - base;
|
|
114
|
-
} catch {}
|
|
115
|
-
const top = Math.max(desired, bottom + GAP, 0);
|
|
116
|
-
next[item.key] = top;
|
|
117
|
-
bottom = top + (cards.get(item.key)?.offsetHeight ?? 0);
|
|
118
|
-
}
|
|
119
|
-
for (const thread of orphans.value) {
|
|
120
|
-
const top = bottom + GAP;
|
|
121
|
-
next[thread.id] = top;
|
|
122
|
-
bottom = top + (cards.get(thread.id)?.offsetHeight ?? 0);
|
|
123
|
-
}
|
|
124
|
-
tops.value = next;
|
|
125
|
-
resolvedTop.value = bottom < 0 ? 0 : bottom + 16;
|
|
126
|
-
minHeight.value = resolvedTop.value + (resolvedSection.value?.offsetHeight ?? 0);
|
|
127
|
-
}
|
|
128
|
-
watch(() => props.editor, (editor, _previous, onCleanup) => {
|
|
129
|
-
if (!editor) return;
|
|
130
|
-
editor.on("transaction", schedule);
|
|
131
|
-
schedule();
|
|
132
|
-
onCleanup(() => editor.off("transaction", schedule));
|
|
133
|
-
}, { immediate: true });
|
|
134
|
-
watch(() => props.container, (container, _previous, onCleanup) => {
|
|
135
|
-
if (!container || !resizeObserver) return;
|
|
136
|
-
resizeObserver.observe(container);
|
|
137
|
-
onCleanup(() => resizeObserver.unobserve(container));
|
|
138
|
-
}, { immediate: true });
|
|
139
|
-
watch(column, (el, previous) => {
|
|
140
|
-
if (previous) unobserve(previous);
|
|
141
|
-
if (el) observe(el);
|
|
142
|
-
schedule();
|
|
143
|
-
});
|
|
144
|
-
watch(resolvedSection, (el, previous) => {
|
|
145
|
-
if (previous) unobserve(previous);
|
|
146
|
-
if (el) observe(el);
|
|
147
|
-
schedule();
|
|
148
|
-
});
|
|
149
|
-
watch([
|
|
150
|
-
positioned,
|
|
151
|
-
orphans,
|
|
152
|
-
() => comments.active.value,
|
|
153
|
-
() => comments.resolved.value.length,
|
|
154
|
-
showResolved,
|
|
155
|
-
enabled
|
|
156
|
-
], () => nextTick(schedule));
|
|
157
|
-
onMounted(() => {
|
|
158
|
-
window.addEventListener("resize", schedule);
|
|
159
|
-
schedule();
|
|
160
|
-
});
|
|
161
|
-
onBeforeUnmount(() => {
|
|
162
|
-
window.removeEventListener("resize", schedule);
|
|
163
|
-
resizeObserver?.disconnect();
|
|
164
|
-
if (frame) cancelAnimationFrame(frame);
|
|
165
|
-
});
|
|
166
70
|
const { Avatar: UAvatar, Button: UButton } = useUiKit();
|
|
167
71
|
const initials = (name) => name.slice(0, 2).toUpperCase();
|
|
168
72
|
const excerpt = (text) => text.length > 60 ? `${text.slice(0, 60)}…` : text;
|
|
@@ -170,118 +74,92 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
170
74
|
function select(id) {
|
|
171
75
|
if (comments.active.value !== id) comments.activate(id, { reveal: true });
|
|
172
76
|
}
|
|
173
|
-
const cardStyle = (key) => ({ top: `${tops.value[key] ?? 0}px` });
|
|
174
77
|
return (_ctx, _cache) => {
|
|
175
78
|
return withDirectives((openBlock(), createElementBlock("div", {
|
|
176
|
-
|
|
177
|
-
ref: column,
|
|
79
|
+
ref: unref(setColumn),
|
|
178
80
|
class: "relative w-[300px] shrink-0",
|
|
179
|
-
style: normalizeStyle({ minHeight: `${minHeight
|
|
81
|
+
style: normalizeStyle({ minHeight: `${unref(layout).minHeight}px` }),
|
|
180
82
|
"aria-label": unref(t)("comments.title")
|
|
181
|
-
}, [!unref(comments).available.value ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString(unref(t)("comments.unavailable")), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}, [!item.thread ? (openBlock(), createBlock(_sfc_main$1, {
|
|
190
|
-
key: 0,
|
|
191
|
-
quote: unref(comments).draftQuote.value,
|
|
192
|
-
class: "shadow-md",
|
|
193
|
-
onSubmit: _cache[0] || (_cache[0] = ($event) => unref(comments).add($event)),
|
|
194
|
-
onCancel: _cache[1] || (_cache[1] = ($event) => unref(comments).cancelDraft())
|
|
195
|
-
}, null, 8, ["quote"])) : unref(comments).active.value === item.thread.id ? (openBlock(), createBlock(_sfc_main$2, {
|
|
196
|
-
key: 1,
|
|
197
|
-
thread: item.thread,
|
|
198
|
-
user: unref(comments).user.value,
|
|
199
|
-
active: true,
|
|
200
|
-
orphan: false,
|
|
201
|
-
comments: unref(comments),
|
|
202
|
-
class: "shadow-md",
|
|
203
|
-
onClick: ($event) => select(item.thread.id)
|
|
204
|
-
}, null, 8, [
|
|
205
|
-
"thread",
|
|
206
|
-
"user",
|
|
207
|
-
"comments",
|
|
208
|
-
"onClick"
|
|
209
|
-
])) : (openBlock(), createElementBlock("button", {
|
|
210
|
-
key: 2,
|
|
211
|
-
type: "button",
|
|
212
|
-
class: "flex w-full items-center gap-2 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] px-2 py-1.5 text-left text-sm hover:bg-[var(--sk-bg-elevated)]",
|
|
213
|
-
onClick: ($event) => select(item.thread.id)
|
|
214
|
-
}, [
|
|
215
|
-
createVNode(unref(UAvatar), {
|
|
216
|
-
size: "2xs",
|
|
217
|
-
text: initials(item.thread.author.name),
|
|
218
|
-
alt: item.thread.author.name,
|
|
219
|
-
style: normalizeStyle({ backgroundColor: item.thread.author.color })
|
|
220
|
-
}, null, 8, [
|
|
221
|
-
"text",
|
|
222
|
-
"alt",
|
|
223
|
-
"style"
|
|
224
|
-
]),
|
|
225
|
-
createElementVNode("span", _hoisted_4, toDisplayString(item.thread.author.name), 1),
|
|
226
|
-
createElementVNode("span", _hoisted_5, toDisplayString(excerpt(item.thread.text)), 1),
|
|
227
|
-
item.thread.replies.length ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(item.thread.replies.length), 1)) : createCommentVNode("v-if", true)
|
|
228
|
-
], 8, _hoisted_3))], 6);
|
|
229
|
-
}), 128)),
|
|
230
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(orphans.value, (thread) => {
|
|
231
|
-
return openBlock(), createElementBlock("div", {
|
|
232
|
-
key: thread.id,
|
|
233
|
-
ref_for: true,
|
|
234
|
-
ref: (el) => setCard(thread.id, el),
|
|
235
|
-
class: normalizeClass(["absolute inset-x-0", { invisible: tops.value[thread.id] === void 0 }]),
|
|
236
|
-
style: normalizeStyle(cardStyle(thread.id))
|
|
237
|
-
}, [createVNode(_sfc_main$2, {
|
|
238
|
-
thread,
|
|
239
|
-
user: unref(comments).user.value,
|
|
240
|
-
active: unref(comments).active.value === thread.id,
|
|
241
|
-
orphan: true,
|
|
242
|
-
comments: unref(comments),
|
|
243
|
-
onClick: ($event) => select(thread.id)
|
|
244
|
-
}, null, 8, [
|
|
245
|
-
"thread",
|
|
246
|
-
"user",
|
|
247
|
-
"active",
|
|
248
|
-
"comments",
|
|
249
|
-
"onClick"
|
|
250
|
-
])], 6);
|
|
251
|
-
}), 128)),
|
|
252
|
-
unref(comments).resolved.value.length ? (openBlock(), createElementBlock("div", {
|
|
83
|
+
}, [!unref(comments).available.value ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString(unref(t)("comments.unavailable")), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [(openBlock(true), createElementBlock(Fragment, null, renderList(cards.value, (card) => {
|
|
84
|
+
return openBlock(), createElementBlock("div", {
|
|
85
|
+
key: card.id,
|
|
86
|
+
ref_for: true,
|
|
87
|
+
ref: (el) => unref(setCard)(card.id, el),
|
|
88
|
+
class: normalizeClass(["absolute inset-x-0 transition-[top] duration-100", { invisible: !unref(layout).tops.has(card.id) }]),
|
|
89
|
+
style: normalizeStyle({ top: `${unref(layout).tops.get(card.id) ?? 0}px` })
|
|
90
|
+
}, [!card.thread ? (openBlock(), createBlock(_sfc_main$1, {
|
|
253
91
|
key: 0,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}, [
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
92
|
+
quote: unref(comments).draftQuote.value,
|
|
93
|
+
class: "shadow-md",
|
|
94
|
+
onSubmit: _cache[0] || (_cache[0] = ($event) => unref(comments).add($event)),
|
|
95
|
+
onCancel: _cache[1] || (_cache[1] = ($event) => unref(comments).cancelDraft())
|
|
96
|
+
}, null, 8, ["quote"])) : activeId.value === card.id ? (openBlock(), createBlock(_sfc_main$2, {
|
|
97
|
+
key: 1,
|
|
98
|
+
thread: card.thread,
|
|
99
|
+
user: unref(comments).user.value,
|
|
100
|
+
active: true,
|
|
101
|
+
orphan: card.orphan,
|
|
102
|
+
comments: unref(comments),
|
|
103
|
+
class: "shadow-md",
|
|
104
|
+
onClick: ($event) => select(card.id)
|
|
105
|
+
}, null, 8, [
|
|
106
|
+
"thread",
|
|
107
|
+
"user",
|
|
108
|
+
"orphan",
|
|
109
|
+
"comments",
|
|
110
|
+
"onClick"
|
|
111
|
+
])) : (openBlock(), createElementBlock("button", {
|
|
112
|
+
key: 2,
|
|
113
|
+
type: "button",
|
|
114
|
+
class: normalizeClass(["flex w-full items-center gap-2 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] px-2 py-1.5 text-left text-sm hover:bg-[var(--sk-bg-elevated)]", { "opacity-70": card.orphan }]),
|
|
115
|
+
onClick: ($event) => select(card.id)
|
|
116
|
+
}, [
|
|
117
|
+
createVNode(unref(UAvatar), {
|
|
118
|
+
size: "2xs",
|
|
119
|
+
text: initials(card.thread.author.name),
|
|
120
|
+
alt: card.thread.author.name,
|
|
121
|
+
style: normalizeStyle({ backgroundColor: card.thread.author.color })
|
|
275
122
|
}, null, 8, [
|
|
276
|
-
"
|
|
277
|
-
"
|
|
278
|
-
"
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
123
|
+
"text",
|
|
124
|
+
"alt",
|
|
125
|
+
"style"
|
|
126
|
+
]),
|
|
127
|
+
createElementVNode("span", _hoisted_4, toDisplayString(card.thread.author.name), 1),
|
|
128
|
+
createElementVNode("span", _hoisted_5, toDisplayString(excerpt(card.thread.text)), 1),
|
|
129
|
+
card.orphan ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(unref(t)("comments.orphan")), 1)) : createCommentVNode("v-if", true),
|
|
130
|
+
card.thread.replies.length ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(card.thread.replies.length), 1)) : createCommentVNode("v-if", true)
|
|
131
|
+
], 10, _hoisted_3))], 6);
|
|
132
|
+
}), 128)), unref(comments).resolved.value.length ? (openBlock(), createElementBlock("div", {
|
|
133
|
+
key: 0,
|
|
134
|
+
ref: unref(setResolvedSection),
|
|
135
|
+
class: "absolute inset-x-0 flex flex-col gap-2",
|
|
136
|
+
style: normalizeStyle({ top: `${unref(layout).resolvedTop}px` })
|
|
137
|
+
}, [createVNode(unref(UButton), {
|
|
138
|
+
color: "neutral",
|
|
139
|
+
variant: "ghost",
|
|
140
|
+
size: "sm",
|
|
141
|
+
label: unref(t)("comments.resolvedCount", { n: unref(comments).resolved.value.length }),
|
|
142
|
+
"trailing-icon": showResolved.value ? "i-lucide-chevron-up" : "i-lucide-chevron-down",
|
|
143
|
+
class: "self-start",
|
|
144
|
+
onClick: _cache[2] || (_cache[2] = ($event) => showResolved.value = !showResolved.value)
|
|
145
|
+
}, null, 8, ["label", "trailing-icon"]), showResolved.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(unref(comments).resolved.value, (thread) => {
|
|
146
|
+
return openBlock(), createBlock(_sfc_main$2, {
|
|
147
|
+
key: thread.id,
|
|
148
|
+
thread,
|
|
149
|
+
user: unref(comments).user.value,
|
|
150
|
+
active: unref(comments).active.value === thread.id,
|
|
151
|
+
orphan: unref(comments).isOrphan(thread.id),
|
|
152
|
+
comments: unref(comments),
|
|
153
|
+
onClick: ($event) => select(thread.id)
|
|
154
|
+
}, null, 8, [
|
|
155
|
+
"thread",
|
|
156
|
+
"user",
|
|
157
|
+
"active",
|
|
158
|
+
"orphan",
|
|
159
|
+
"comments",
|
|
160
|
+
"onClick"
|
|
161
|
+
]);
|
|
162
|
+
}), 128)) : createCommentVNode("v-if", true)], 4)) : createCommentVNode("v-if", true)], 64))], 12, _hoisted_1)), [[vShow, __props.open && enabled.value]]);
|
|
285
163
|
};
|
|
286
164
|
}
|
|
287
165
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommentsMargin.js","names":["_withDirectives","_openBlock","_createElementBlock","_normalizeStyle","_unref","_toDisplayString","_Fragment","_renderList","_normalizeClass","_createBlock","CommentComposer","CommentThreadView","_createVNode","_createElementVNode","_createCommentVNode","_vShow"],"sources":["../../../../src/features/comments/components/CommentsMargin.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Comments margin („Randspalte“): a 300px column the host places inside its scroll container right of the editor,\n * so it scrolls with the text. Cards sit absolutely at the height of their text (top-down in document order, pushed\n * down so they never overlap); the active thread is expanded, the others collapse to one line. The draft composer\n * takes part in the layout, orphaned threads stack after the positioned cards and resolved threads („Erledigt“)\n * sit at the bottom behind a toggle. Shown only while `open` (the host binds the surface's `commentsOpen` model) and the\n * user's comments layout is `margin`; the layout is recomputed whenever the column becomes visible again.\n */\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport type { Editor } from '@tiptap/vue-3'\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport type { CommentThread as Thread } from '../schema'\nimport { useComments } from '../use-comments'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport CommentComposer from './CommentComposer.vue'\nimport CommentThreadView from './CommentThreadView.vue'\n\nconst props = withDefaults(defineProps<{\n editor: Editor | undefined\n /** The editor's scroll container, which the column sits in: its size changes re-run the layout. */\n container: HTMLElement | null\n /** Whether the column is shown (the surface's `commentsOpen` model). */\n open?: boolean\n}>(), { open: true })\n\nconst { settings } = useEditorSettings()\nconst { t } = useEditorI18n()\nconst enabled = computed(() => settings.value.editing.commentsLayout === 'margin')\n// Idle (no Y.Doc observer) while another layout shows the comments.\nconst comments = useComments(() => enabled.value ? props.editor : undefined)\n\nconst GAP = 8\nconst DRAFT = '__draft'\n\ninterface Item { key: string, from: number, thread: Thread | null }\n\n/** Draft and open threads with a text range, in document order. */\nconst positioned = computed<Item[]>(() => {\n const items: Item[] = []\n const draft = comments.draft.value\n if (draft) items.push({ key: DRAFT, from: draft.from, thread: null })\n for (const thread of comments.open.value) {\n const range = comments.ranges.value.get(thread.id)\n if (range) items.push({ key: thread.id, from: range.from, thread })\n }\n return items.sort((a, b) => a.from - b.from)\n})\nconst orphans = computed(() => comments.open.value.filter(thread => comments.isOrphan(thread.id)))\n\nconst column = ref<HTMLElement>()\nconst resolvedSection = ref<HTMLElement>()\nconst showResolved = ref(false)\nconst tops = ref<Record<string, number>>({})\nconst resolvedTop = ref(0)\nconst minHeight = ref(0)\n\nconst cards = new Map<string, HTMLElement>()\nconst observed = new WeakSet<Element>()\nlet frame = 0\n\nconst resizeObserver = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => schedule())\n\nfunction observe(el: Element) {\n if (!resizeObserver || observed.has(el)) return\n observed.add(el)\n resizeObserver.observe(el)\n}\n\nfunction unobserve(el: Element) {\n resizeObserver?.unobserve(el)\n observed.delete(el)\n}\n\nfunction setCard(key: string, el: unknown) {\n if (!(el instanceof HTMLElement)) return\n cards.set(key, el)\n observe(el)\n}\n\nfunction schedule() {\n if (frame || typeof requestAnimationFrame === 'undefined') return\n frame = requestAnimationFrame(relayout)\n}\n\nfunction relayout() {\n frame = 0\n const editor = props.editor\n const col = column.value\n // Hidden (`v-show`) columns have no layout box; the column's own ResizeObserver re-runs this once shown.\n if (!editor || editor.isDestroyed || !col || !col.getClientRects().length) return\n const live = new Set([...positioned.value.map(i => i.key), ...orphans.value.map(thread => thread.id)])\n for (const [key, el] of cards) {\n if (live.has(key)) continue\n unobserve(el)\n cards.delete(key)\n }\n\n const base = col.getBoundingClientRect().top\n const size = editor.state.doc.content.size\n const next: Record<string, number> = {}\n let bottom = -GAP\n for (const item of positioned.value) {\n let desired = bottom + GAP\n try {\n desired = editor.view.coordsAtPos(Math.min(item.from, size)).top - base\n }\n catch {\n // Position not rendered (yet): stack it below the previous card.\n }\n const top = Math.max(desired, bottom + GAP, 0)\n next[item.key] = top\n bottom = top + (cards.get(item.key)?.offsetHeight ?? 0)\n }\n for (const thread of orphans.value) {\n const top = bottom + GAP\n next[thread.id] = top\n bottom = top + (cards.get(thread.id)?.offsetHeight ?? 0)\n }\n tops.value = next\n resolvedTop.value = bottom < 0 ? 0 : bottom + 2 * GAP\n minHeight.value = resolvedTop.value + (resolvedSection.value?.offsetHeight ?? 0)\n}\n\nwatch(() => props.editor, (editor, _previous, onCleanup) => {\n if (!editor) return\n editor.on('transaction', schedule)\n schedule()\n onCleanup(() => editor.off('transaction', schedule))\n}, { immediate: true })\n\nwatch(() => props.container, (container, _previous, onCleanup) => {\n if (!container || !resizeObserver) return\n resizeObserver.observe(container)\n onCleanup(() => resizeObserver.unobserve(container))\n}, { immediate: true })\n\n// The column itself: its size flips between zero and real when the host toggles it with `v-show`.\nwatch(column, (el, previous) => {\n if (previous) unobserve(previous)\n if (el) observe(el)\n schedule()\n})\n\nwatch(resolvedSection, (el, previous) => {\n if (previous) unobserve(previous)\n if (el) observe(el)\n schedule()\n})\n\n// Cards appearing/disappearing or expanding: lay out once the DOM has them.\nwatch([positioned, orphans, () => comments.active.value, () => comments.resolved.value.length, showResolved, enabled], () => nextTick(schedule))\n\nonMounted(() => {\n window.addEventListener('resize', schedule)\n schedule()\n})\nonBeforeUnmount(() => {\n window.removeEventListener('resize', schedule)\n resizeObserver?.disconnect()\n if (frame) cancelAnimationFrame(frame)\n})\n\nconst { Avatar: UAvatar, Button: UButton } = useUiKit()\nconst initials = (name: string) => name.slice(0, 2).toUpperCase()\nconst excerpt = (text: string) => text.length > 60 ? `${text.slice(0, 60)}…` : text\n\n/** Clicking a card activates (expands) it and reveals its text; clicks inside the already active card do nothing. */\nfunction select(id: string) {\n if (comments.active.value !== id) comments.activate(id, { reveal: true })\n}\n\nconst cardStyle = (key: string) => ({ top: `${tops.value[key] ?? 0}px` })\n</script>\n\n<template>\n <div\n v-show=\"open && enabled\"\n ref=\"column\"\n class=\"relative w-[300px] shrink-0\"\n :style=\"{ minHeight: `${minHeight}px` }\"\n :aria-label=\"t('comments.title')\"\n >\n <p v-if=\"!comments.available.value\" class=\"p-3 text-sm text-[var(--sk-muted)]\">\n {{ t('comments.unavailable') }}\n </p>\n\n <template v-else>\n <div\n v-for=\"item in positioned\"\n :key=\"item.key\"\n :ref=\"el => setCard(item.key, el)\"\n class=\"absolute inset-x-0 transition-[top] duration-100\"\n :class=\"{ invisible: tops[item.key] === undefined }\"\n :style=\"cardStyle(item.key)\"\n >\n <CommentComposer\n v-if=\"!item.thread\"\n :quote=\"comments.draftQuote.value\"\n class=\"shadow-md\"\n @submit=\"comments.add($event)\"\n @cancel=\"comments.cancelDraft()\"\n />\n <CommentThreadView\n v-else-if=\"comments.active.value === item.thread.id\"\n :thread=\"item.thread\"\n :user=\"comments.user.value\"\n :active=\"true\"\n :orphan=\"false\"\n :comments=\"comments\"\n class=\"shadow-md\"\n @click=\"select(item.thread.id)\"\n />\n <button\n v-else\n type=\"button\"\n class=\"flex w-full items-center gap-2 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] px-2 py-1.5 text-left text-sm hover:bg-[var(--sk-bg-elevated)]\"\n @click=\"select(item.thread.id)\"\n >\n <UAvatar\n size=\"2xs\"\n :text=\"initials(item.thread.author.name)\"\n :alt=\"item.thread.author.name\"\n :style=\"{ backgroundColor: item.thread.author.color }\"\n />\n <span class=\"shrink-0 font-medium\">{{ item.thread.author.name }}</span>\n <span class=\"min-w-0 flex-1 truncate text-[var(--sk-muted)]\">{{ excerpt(item.thread.text) }}</span>\n <span v-if=\"item.thread.replies.length\" class=\"shrink-0 text-xs text-[var(--sk-fg-dimmed)]\">{{ item.thread.replies.length }}</span>\n </button>\n </div>\n\n <div\n v-for=\"thread in orphans\"\n :key=\"thread.id\"\n :ref=\"el => setCard(thread.id, el)\"\n class=\"absolute inset-x-0\"\n :class=\"{ invisible: tops[thread.id] === undefined }\"\n :style=\"cardStyle(thread.id)\"\n >\n <CommentThreadView\n :thread=\"thread\"\n :user=\"comments.user.value\"\n :active=\"comments.active.value === thread.id\"\n :orphan=\"true\"\n :comments=\"comments\"\n @click=\"select(thread.id)\"\n />\n </div>\n\n <div\n v-if=\"comments.resolved.value.length\"\n ref=\"resolvedSection\"\n class=\"absolute inset-x-0 flex flex-col gap-2\"\n :style=\"{ top: `${resolvedTop}px` }\"\n >\n <UButton\n color=\"neutral\"\n variant=\"ghost\"\n size=\"sm\"\n :label=\"t('comments.resolvedCount', { n: comments.resolved.value.length })\"\n :trailing-icon=\"showResolved ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'\"\n class=\"self-start\"\n @click=\"showResolved = !showResolved\"\n />\n <template v-if=\"showResolved\">\n <CommentThreadView\n v-for=\"thread in comments.resolved.value\"\n :key=\"thread.id\"\n :thread=\"thread\"\n :user=\"comments.user.value\"\n :active=\"comments.active.value === thread.id\"\n :orphan=\"comments.isOrphan(thread.id)\"\n :comments=\"comments\"\n @click=\"select(thread.id)\"\n />\n </template>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommentsMargin.js","names":["_withDirectives","_openBlock","_createElementBlock","_unref","_normalizeStyle","_toDisplayString","_Fragment","_renderList","_normalizeClass","_createBlock","CommentComposer","CommentThreadView","_createVNode","_createElementVNode","_createCommentVNode","_vShow"],"sources":["../../../../src/features/comments/components/CommentsMargin.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Comments margin („Randspalte“): a 300px column the host places inside its scroll container right of the editor,\n * so it scrolls with the text. Layout „Aktiver Faden“ (`layoutMargin`): the active thread's card sits exactly at the\n * top of its text's line and expands, the others collapse to one line and give way up and down; without an active\n * thread the cards stack top-down at their lines. The draft composer takes part like an active card. An orphaned thread\n * (its text was deleted) is greyed out and sits where the text was; resolved threads („Erledigt“) sit at the bottom\n * behind a toggle. Shown only while `open` (the host binds the surface's `commentsOpen` model) and the user's comments\n * layout is `margin`; the layout is recomputed whenever the column becomes visible again.\n */\nimport { useUiKit } from '../../../vue/kit/use-ui-kit'\nimport type { Editor } from '@tiptap/vue-3'\nimport { computed, ref } from 'vue'\nimport { useEditorSettings } from '../../../vue/settings'\nimport { useEditorI18n } from '../../../vue/use-editor-i18n'\nimport { DRAFT_CARD, marginCards } from '../margin-layout'\nimport { useComments } from '../use-comments'\nimport { useMarginLayout } from '../use-margin-layout'\nimport CommentComposer from './CommentComposer.vue'\nimport CommentThreadView from './CommentThreadView.vue'\n\nconst props = withDefaults(defineProps<{\n editor: Editor | undefined\n /** The editor's scroll container, which the column sits in: its size changes re-run the layout. */\n container: HTMLElement | null\n /** Whether the column is shown (the surface's `commentsOpen` model). */\n open?: boolean\n}>(), { open: true })\n\nconst { settings } = useEditorSettings()\nconst { t } = useEditorI18n()\nconst enabled = computed(() => settings.value.editing.commentsLayout === 'margin')\n// Idle (no Y.Doc observer) while another layout shows the comments.\nconst comments = useComments(() => enabled.value ? props.editor : undefined)\n\n/** Draft and open threads in document order, each at its text (an orphan where its text was). */\nconst cards = computed(() => marginCards({\n draft: comments.draft.value,\n open: comments.open.value,\n ranges: comments.ranges.value,\n orphanAt: comments.orphanAt.value,\n}))\n/** The card that owns its line: the draft composer, else the active thread. */\nconst activeId = computed(() => comments.draft.value ? DRAFT_CARD : comments.active.value)\n\nconst { state: layout, setColumn, setResolvedSection, setCard } = useMarginLayout({\n editor: () => props.editor,\n container: () => props.container,\n source: { cards: () => cards.value, activeId: () => activeId.value },\n})\n\nconst showResolved = ref(false)\n\nconst { Avatar: UAvatar, Button: UButton } = useUiKit()\nconst initials = (name: string) => name.slice(0, 2).toUpperCase()\nconst excerpt = (text: string) => text.length > 60 ? `${text.slice(0, 60)}…` : text\n\n/** Clicking a card activates (expands) it and reveals its text; clicks inside the already active card do nothing. */\nfunction select(id: string) {\n if (comments.active.value !== id) comments.activate(id, { reveal: true })\n}\n</script>\n\n<template>\n <div\n v-show=\"open && enabled\"\n :ref=\"setColumn\"\n class=\"relative w-[300px] shrink-0\"\n :style=\"{ minHeight: `${layout.minHeight}px` }\"\n :aria-label=\"t('comments.title')\"\n >\n <p v-if=\"!comments.available.value\" class=\"p-3 text-sm text-[var(--sk-muted)]\">\n {{ t('comments.unavailable') }}\n </p>\n\n <template v-else>\n <div\n v-for=\"card in cards\"\n :key=\"card.id\"\n :ref=\"el => setCard(card.id, el)\"\n class=\"absolute inset-x-0 transition-[top] duration-100\"\n :class=\"{ invisible: !layout.tops.has(card.id) }\"\n :style=\"{ top: `${layout.tops.get(card.id) ?? 0}px` }\"\n >\n <CommentComposer\n v-if=\"!card.thread\"\n :quote=\"comments.draftQuote.value\"\n class=\"shadow-md\"\n @submit=\"comments.add($event)\"\n @cancel=\"comments.cancelDraft()\"\n />\n <CommentThreadView\n v-else-if=\"activeId === card.id\"\n :thread=\"card.thread\"\n :user=\"comments.user.value\"\n :active=\"true\"\n :orphan=\"card.orphan\"\n :comments=\"comments\"\n class=\"shadow-md\"\n @click=\"select(card.id)\"\n />\n <button\n v-else\n type=\"button\"\n class=\"flex w-full items-center gap-2 rounded-md border border-[var(--sk-border)] bg-[var(--sk-bg)] px-2 py-1.5 text-left text-sm hover:bg-[var(--sk-bg-elevated)]\"\n :class=\"{ 'opacity-70': card.orphan }\"\n @click=\"select(card.id)\"\n >\n <UAvatar\n size=\"2xs\"\n :text=\"initials(card.thread.author.name)\"\n :alt=\"card.thread.author.name\"\n :style=\"{ backgroundColor: card.thread.author.color }\"\n />\n <span class=\"shrink-0 font-medium\">{{ card.thread.author.name }}</span>\n <span class=\"min-w-0 flex-1 truncate text-[var(--sk-muted)]\">{{ excerpt(card.thread.text) }}</span>\n <span v-if=\"card.orphan\" class=\"shrink-0 text-xs text-[var(--sk-fg-dimmed)] italic\">{{ t('comments.orphan') }}</span>\n <span v-if=\"card.thread.replies.length\" class=\"shrink-0 text-xs text-[var(--sk-fg-dimmed)]\">{{ card.thread.replies.length }}</span>\n </button>\n </div>\n\n <div\n v-if=\"comments.resolved.value.length\"\n :ref=\"setResolvedSection\"\n class=\"absolute inset-x-0 flex flex-col gap-2\"\n :style=\"{ top: `${layout.resolvedTop}px` }\"\n >\n <UButton\n color=\"neutral\"\n variant=\"ghost\"\n size=\"sm\"\n :label=\"t('comments.resolvedCount', { n: comments.resolved.value.length })\"\n :trailing-icon=\"showResolved ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'\"\n class=\"self-start\"\n @click=\"showResolved = !showResolved\"\n />\n <template v-if=\"showResolved\">\n <CommentThreadView\n v-for=\"thread in comments.resolved.value\"\n :key=\"thread.id\"\n :thread=\"thread\"\n :user=\"comments.user.value\"\n :active=\"comments.active.value === thread.id\"\n :orphan=\"comments.isOrphan(thread.id)\"\n :comments=\"comments\"\n @click=\"select(thread.id)\"\n />\n </template>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqBA,MAAM,QAAQ;EAQd,MAAM,EAAE,aAAa,kBAAkB;EACvC,MAAM,EAAE,MAAM,cAAc;EAC5B,MAAM,UAAU,eAAe,SAAS,MAAM,QAAQ,mBAAmB,QAAQ;EAEjF,MAAM,WAAW,kBAAkB,QAAQ,QAAQ,MAAM,SAAS,KAAA,CAAS;;EAG3E,MAAM,QAAQ,eAAe,YAAY;GACvC,OAAO,SAAS,MAAM;GACtB,MAAM,SAAS,KAAK;GACpB,QAAQ,SAAS,OAAO;GACxB,UAAU,SAAS,SAAS;EAC9B,CAAC,CAAC;;EAEF,MAAM,WAAW,eAAe,SAAS,MAAM,QAAQ,aAAa,SAAS,OAAO,KAAK;EAEzF,MAAM,EAAE,OAAO,QAAQ,WAAW,oBAAoB,YAAY,gBAAgB;GAChF,cAAc,MAAM;GACpB,iBAAiB,MAAM;GACvB,QAAQ;IAAE,aAAa,MAAM;IAAO,gBAAgB,SAAS;GAAM;EACrE,CAAC;EAED,MAAM,eAAe,IAAI,KAAK;EAE9B,MAAM,EAAE,QAAQ,SAAS,QAAQ,YAAY,SAAS;EACtD,MAAM,YAAY,SAAiB,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY;EAChE,MAAM,WAAW,SAAiB,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;;EAG/E,SAAS,OAAO,IAAY;GAC1B,IAAI,SAAS,OAAO,UAAU,IAAI,SAAS,SAAS,IAAI,EAAE,QAAQ,KAAK,CAAC;EAC1E;;GAIE,OAAAA,gBAAAC,UAAA,GAAAC,mBAsFM,OAAA;IApFH,KAAKC,MAAA,SAAA;IACN,OAAM;IACL,OAAKC,eAAA,EAAA,WAAA,GAAkBD,MAAA,MAAA,CAAM,CAAC,UAAS,IAAA,CAAA;IACvC,cAAYA,MAAA,CAAA,CAAC,CAAA,gBAAA;GAEJ,GAAA,CAAA,CAAAA,MAAA,QAAA,CAAQ,CAAC,UAAU,SAA7BF,UAAA,GAAAC,mBAEI,KAFJ,YAEIG,gBADCF,MAAA,CAAA,CAAC,CAAA,sBAAA,CAAA,GAAA,CAAA,MAGNF,UAAA,GAAAC,mBA0EWI,UAAA,EAAA,KAAA,EAAA,GAAA,EAzETL,UAAA,IAAA,GAAAC,mBA2CMI,UAAA,MAAAC,WA1CW,MAAA,QAAR,SAAI;IADb,OAAAN,UAAA,GAAAC,mBA2CM,OAAA;KAzCH,KAAK,KAAK;;KACV,MAAK,OAAMC,MAAA,OAAA,CAAO,CAAC,KAAK,IAAI,EAAE;KAC/B,OAAKK,eAAA,CAAC,oDAAkD,EAAA,WAAA,CAClCL,MAAA,MAAA,CAAM,CAAC,KAAK,IAAI,KAAK,EAAE,EAAA,CAAA,CAAA;KAC5C,OAAKC,eAAA,EAAA,KAAA,GAAYD,MAAA,MAAA,CAAM,CAAC,KAAK,IAAI,KAAK,EAAE,KAAA,EAAA,IAAA,CAAA;IAGhC,GAAA,CAAA,CAAA,KAAK,UADdF,UAAA,GAAAQ,YAMEC,aAAA;;KAJC,OAAOP,MAAA,QAAA,CAAQ,CAAC,WAAW;KAC5B,OAAM;KACL,UAAM,OAAA,OAAA,OAAA,MAAA,WAAEA,MAAA,QAAA,CAAQ,CAAC,IAAI,MAAM;KAC3B,UAAM,OAAA,OAAA,OAAA,MAAA,WAAEA,MAAA,QAAA,CAAQ,CAAC,YAAW;IAGlB,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,KAAA,SAAA,UAAa,KAAK,MAD/BF,UAAA,GAAAQ,YASEE,aAAA;;KAPC,QAAQ,KAAK;KACb,MAAMR,MAAA,QAAA,CAAQ,CAAC,KAAK;KACpB,QAAQ;KACR,QAAQ,KAAK;KACb,UAAUA,MAAA,QAAA;KACX,OAAM;KACL,UAAK,WAAE,OAAO,KAAK,EAAE;;;;;;;IAExB,CAAA,MAAAF,UAAA,GAAAC,mBAiBS,UAAA;;KAfP,MAAK;KACL,OAAKM,eAAA,CAAC,+JAA6J,EAAA,cAC3I,KAAK,OAAM,CAAA,CAAA;KAClC,UAAK,WAAE,OAAO,KAAK,EAAE;;KAEtBI,YAKET,MAAA,OAAA,GAAA;MAJA,MAAK;MACJ,MAAM,SAAS,KAAK,OAAO,OAAO,IAAI;MACtC,KAAK,KAAK,OAAO,OAAO;MACxB,OAAKC,eAAA,EAAA,iBAAqB,KAAK,OAAO,OAAO,MAAK,CAAA;;;;;;KAErDS,mBAAuE,QAAvE,YAAuER,gBAAjC,KAAK,OAAO,OAAO,IAAI,GAAA,CAAA;KAC7DQ,mBAAmG,QAAnG,YAAmGR,gBAAnC,QAAQ,KAAK,OAAO,IAAI,CAAA,GAAA,CAAA;KAC5E,KAAK,UAAjBJ,UAAA,GAAAC,mBAAqH,QAArH,YAAqHG,gBAA9BF,MAAA,CAAA,CAAC,CAAA,iBAAA,CAAA,GAAA,CAAA,KAAAW,mBAAA,QAAA,IAAA;KAC5E,KAAK,OAAO,QAAQ,UAAhCb,UAAA,GAAAC,mBAAmI,QAAnI,YAAmIG,gBAApC,KAAK,OAAO,QAAQ,MAAM,GAAA,CAAA,KAAAS,mBAAA,QAAA,IAAA;;GAKrH,CAAA,GAAA,GAAA,IAAAX,MAAA,QAAA,CAAQ,CAAC,SAAS,MAAM,UADhCF,UAAA,GAAAC,mBA2BM,OAAA;;IAzBH,KAAKC,MAAA,kBAAA;IACN,OAAM;IACL,OAAKC,eAAA,EAAA,KAAA,GAAYD,MAAA,MAAA,CAAM,CAAC,YAAW,IAAA,CAAA;GAEpC,GAAA,CAAAS,YAQET,MAAA,OAAA,GAAA;IAPA,OAAM;IACN,SAAQ;IACR,MAAK;IACJ,OAAOA,MAAA,CAAA,CAAC,CAAA,0BAAA,EAAA,GAAgCA,MAAA,QAAA,CAAQ,CAAC,SAAS,MAAM,OAAM,CAAA;IACtE,iBAAe,aAAA,QAAY,wBAAA;IAC5B,OAAM;IACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,aAAA,QAAY,CAAI,aAAA;GAEV,GAAA,MAAA,GAAA,CAAA,SAAA,eAAA,CAAA,GAAA,aAAA,SACdF,UAAA,IAAA,GAAAC,mBASEI,UAAA,EAAA,KAAA,EAAA,GAAAC,WARiBJ,MAAA,QAAA,CAAQ,CAAC,SAAS,QAA5B,WAAM;IADf,OAAAF,UAAA,GAAAQ,YASEE,aAAA;KAPC,KAAK,OAAO;KACJ;KACR,MAAMR,MAAA,QAAA,CAAQ,CAAC,KAAK;KACpB,QAAQA,MAAA,QAAA,CAAQ,CAAC,OAAO,UAAU,OAAO;KACzC,QAAQA,MAAA,QAAA,CAAQ,CAAC,SAAS,OAAO,EAAE;KACnC,UAAUA,MAAA,QAAA;KACV,UAAK,WAAE,OAAO,OAAO,EAAE;;;;;;;;;GAhFxB,CAAA,GAAA,GAAA,KAAA,mBAAA,QAAA,IAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,QAAA,IAAA,CAAA,GAAA,EAAA,EAAA,GAAA,IAAA,UAAA,IAAA,CAAA,CAAAY,OAAA,QAAA,QAAQ,QAAA,KAAO,CAAA,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { cancelCommentDraft, commentsKey, setActiveComment } from "../comments-plugin.js";
|
|
1
2
|
import { useEditorSettings } from "../../../vue/settings.js";
|
|
2
3
|
import { useEditorUi } from "../../../vue/use-editor-ui.js";
|
|
3
|
-
import { cancelCommentDraft, commentsKey, setActiveComment } from "../comments-plugin.js";
|
|
4
4
|
import _sfc_main$1 from "./CommentBubble.js";
|
|
5
5
|
import _sfc_main$2 from "./CommentsPopover.js";
|
|
6
6
|
import { Fragment, createElementBlock, createVNode, defineComponent, openBlock, watch } from "vue";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Comments, commentsKey } from "./comments-plugin.js";
|
|
2
1
|
import { CommentMark } from "./schema.js";
|
|
2
|
+
import { Comments, commentsKey } from "./comments-plugin.js";
|
|
3
3
|
import { provideCommentsModel } from "./model.js";
|
|
4
4
|
import { commentsShortcuts } from "./shortcuts.js";
|
|
5
5
|
import { Extension } from "@tiptap/core";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { COMMENTS_LAYOUTS, CommentsLayout } from "../../settings/values.js";
|
|
2
|
-
import { COMMENTS_FIELD, COMMENT_LIMITS, CommentAuthor, CommentMark, CommentReply, CommentThread, commentsSchemaExtensions } from "./schema.js";
|
|
2
|
+
import { COMMENTS_FIELD, COMMENT_LIMITS, CommentAnchor, CommentAuthor, CommentMark, CommentReply, CommentThread, commentsSchemaExtensions } from "./schema.js";
|
|
3
3
|
import { CommentsActions, CommentsModel, CommentsModelFactory, CommentsSnapshot, DocRange, commentsModelOf, createCommentsModel, createEditorCommentsModel } from "./model.js";
|
|
4
4
|
import { CommentsFeatureOptions, CommentsUi, commentsFeatureCore } from "./feature.js";
|
|
5
5
|
import { Comments, CommentsMeta, CommentsOptions, CommentsState, cancelCommentDraft, commentsAvailable, commentsCanComment, commentsDoc, commentsKey, commentsState, commentsUser, setActiveComment, startCommentDraft } from "./comments-plugin.js";
|
|
6
|
-
|
|
6
|
+
import { DRAFT_CARD, MARGIN_GAP, MarginCard, MarginEntry, MarginLayout, layoutMargin, marginCards } from "./margin-layout.js";
|
|
7
|
+
import { MarginController, MarginSource, MarginState, createMarginController } from "./margin-controller.js";
|
|
8
|
+
export { COMMENTS_FIELD, COMMENTS_LAYOUTS, COMMENT_LIMITS, type CommentAnchor, type CommentAuthor, CommentMark, type CommentReply, type CommentThread, Comments, type CommentsActions, type CommentsFeatureOptions, type CommentsLayout, type CommentsMeta, type CommentsModel, type CommentsModelFactory, type CommentsOptions, type CommentsSnapshot, type CommentsState, type CommentsUi, DRAFT_CARD, type DocRange, MARGIN_GAP, type MarginCard, type MarginController, type MarginEntry, type MarginLayout, type MarginSource, type MarginState, cancelCommentDraft, commentsAvailable, commentsCanComment, commentsDoc, commentsFeatureCore, commentsKey, commentsModelOf, commentsSchemaExtensions, commentsState, commentsUser, createCommentsModel, createEditorCommentsModel, createMarginController, layoutMargin, marginCards, setActiveComment, startCommentDraft };
|