@ecsplico/editor 2.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.
Files changed (278) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +4 -3
  3. package/dist/adapters/anchors.d.ts +80 -0
  4. package/dist/adapters/anchors.js +270 -0
  5. package/dist/adapters/anchors.js.map +1 -0
  6. package/dist/adapters/comments/client.d.ts +64 -0
  7. package/dist/adapters/comments/client.js +536 -0
  8. package/dist/adapters/comments/client.js.map +1 -0
  9. package/dist/adapters/comments/drizzle.d.ts +430 -0
  10. package/dist/adapters/comments/drizzle.js +236 -0
  11. package/dist/adapters/comments/drizzle.js.map +1 -0
  12. package/dist/adapters/comments/errors.d.ts +9 -0
  13. package/dist/adapters/comments/errors.js +19 -0
  14. package/dist/adapters/comments/errors.js.map +1 -0
  15. package/dist/adapters/comments/json.d.ts +19 -0
  16. package/dist/adapters/comments/json.js +94 -0
  17. package/dist/adapters/comments/json.js.map +1 -0
  18. package/dist/adapters/comments/record.js +78 -0
  19. package/dist/adapters/comments/record.js.map +1 -0
  20. package/dist/adapters/comments/server.d.ts +23 -0
  21. package/dist/adapters/comments/server.js +102 -0
  22. package/dist/adapters/comments/server.js.map +1 -0
  23. package/dist/adapters/comments/types.d.ts +109 -0
  24. package/dist/adapters/documents.js +108 -0
  25. package/dist/adapters/documents.js.map +1 -0
  26. package/dist/adapters/drizzle.d.ts +293 -0
  27. package/dist/adapters/drizzle.js +157 -0
  28. package/dist/adapters/drizzle.js.map +1 -0
  29. package/dist/adapters/files.js +30 -0
  30. package/dist/adapters/files.js.map +1 -0
  31. package/dist/adapters/forge.d.ts +58 -0
  32. package/dist/adapters/forge.js +91 -0
  33. package/dist/adapters/forge.js.map +1 -0
  34. package/dist/adapters/index.d.ts +13 -0
  35. package/dist/adapters/index.js +0 -0
  36. package/dist/adapters/json.d.ts +24 -0
  37. package/dist/adapters/json.js +128 -0
  38. package/dist/adapters/json.js.map +1 -0
  39. package/dist/adapters/nitro.d.ts +61 -0
  40. package/dist/adapters/nitro.js +212 -0
  41. package/dist/adapters/nitro.js.map +1 -0
  42. package/dist/adapters/routes.js +16 -0
  43. package/dist/adapters/routes.js.map +1 -0
  44. package/dist/adapters/shared.d.ts +37 -0
  45. package/dist/adapters/shared.js +32 -0
  46. package/dist/adapters/shared.js.map +1 -0
  47. package/dist/adapters/vue.d.ts +67 -0
  48. package/dist/adapters/vue.js +187 -0
  49. package/dist/adapters/vue.js.map +1 -0
  50. package/dist/code/CodeSurface.vue.d.ts +3 -3
  51. package/dist/core/doc-anchors.d.ts +5 -0
  52. package/dist/core/doc-anchors.js +4 -3
  53. package/dist/core/doc-anchors.js.map +1 -1
  54. package/dist/core/editor-ui.js +57 -1
  55. package/dist/core/editor-ui.js.map +1 -1
  56. package/dist/core/surface-session.js +139 -0
  57. package/dist/core/surface-session.js.map +1 -0
  58. package/dist/document/DocumentSurface.d.ts +1 -1
  59. package/dist/document/DocumentSurface.vue.d.ts +3 -3
  60. package/dist/features/ai/agent-op-review.d.ts +2 -2
  61. package/dist/features/ai/agent-op-review.js.map +1 -1
  62. package/dist/features/ai/agent-ops.d.ts +1 -1
  63. package/dist/features/ai/agent-ops.js +9 -6
  64. package/dist/features/ai/agent-ops.js.map +1 -1
  65. package/dist/features/ai/ai-apply.d.ts +1 -1
  66. package/dist/features/ai/ai-review.js +10 -1
  67. package/dist/features/ai/ai-review.js.map +1 -1
  68. package/dist/features/ai/components/AiSelectionMenu.js +78 -252
  69. package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
  70. package/dist/features/ai/components/AiSelectionMenu.vue.d.ts +2 -1
  71. package/dist/features/ai/components/AiSidebar.js +2 -2
  72. package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
  73. package/dist/features/ai/components/DocumentChatOps.js +1 -0
  74. package/dist/features/ai/components/DocumentChatOps.js.map +1 -1
  75. package/dist/features/ai/components/ModelQuickSwitch.js +1 -1
  76. package/dist/features/ai/components/SettingsAiTab.js +1 -1
  77. package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
  78. package/dist/features/ai/document-chat.d.ts +3 -2
  79. package/dist/features/ai/document-chat.js +16 -19
  80. package/dist/features/ai/document-chat.js.map +1 -1
  81. package/dist/features/ai/index.d.ts +5 -3
  82. package/dist/features/ai/index.js +4 -2
  83. package/dist/features/ai/protocol/agent-ops.d.ts +22 -17
  84. package/dist/features/ai/protocol/agent-ops.js +65 -39
  85. package/dist/features/ai/protocol/agent-ops.js.map +1 -1
  86. package/dist/features/ai/protocol/block-lines.d.ts +89 -0
  87. package/dist/features/ai/protocol/block-lines.js +177 -0
  88. package/dist/features/ai/protocol/block-lines.js.map +1 -0
  89. package/dist/features/ai/protocol/chat.js +10 -10
  90. package/dist/features/ai/protocol/chat.js.map +1 -1
  91. package/dist/features/ai/protocol/index.d.ts +3 -2
  92. package/dist/features/ai/protocol/index.js +3 -2
  93. package/dist/features/ai/react/AiSelectionMenu.d.ts +3 -2
  94. package/dist/features/ai/react/AiSelectionMenu.js +64 -255
  95. package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
  96. package/dist/features/ai/react/AiSidebar.js +2 -2
  97. package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
  98. package/dist/features/ai/react/DocumentChatOps.js +1 -0
  99. package/dist/features/ai/react/DocumentChatOps.js.map +1 -1
  100. package/dist/features/ai/react/ModelQuickSwitch.js +1 -1
  101. package/dist/features/ai/react/SettingsAiTab.js +1 -1
  102. package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
  103. package/dist/features/ai/react/use-ai-selection-flow.js +34 -0
  104. package/dist/features/ai/react/use-ai-selection-flow.js.map +1 -0
  105. package/dist/features/ai/selection-flow.d.ts +133 -0
  106. package/dist/features/ai/selection-flow.js +283 -0
  107. package/dist/features/ai/selection-flow.js.map +1 -0
  108. package/dist/features/ai/use-ai-assistant.d.ts +1 -1
  109. package/dist/features/ai/use-document-chat.d.ts +1 -1
  110. package/dist/features/authors/components/AuthorColorsPanel.js +1 -1
  111. package/dist/features/authors/feature.js +1 -1
  112. package/dist/features/authors/index.js +1 -1
  113. package/dist/features/authors/react/AuthorColorsPanel.js +1 -1
  114. package/dist/features/comments/components/CommentBubble.js +1 -1
  115. package/dist/features/comments/components/CommentComposer.js +1 -1
  116. package/dist/features/comments/components/CommentThreadView.js +5 -2
  117. package/dist/features/comments/components/CommentThreadView.js.map +1 -1
  118. package/dist/features/comments/components/CommentsMargin.js +108 -230
  119. package/dist/features/comments/components/CommentsMargin.js.map +1 -1
  120. package/dist/features/comments/components/CommentsOverlay.js +1 -1
  121. package/dist/features/comments/feature.js +1 -1
  122. package/dist/features/comments/index.d.ts +4 -2
  123. package/dist/features/comments/index.js +4 -2
  124. package/dist/features/comments/margin-controller.d.ts +44 -0
  125. package/dist/features/comments/margin-controller.js +132 -0
  126. package/dist/features/comments/margin-controller.js.map +1 -0
  127. package/dist/features/comments/margin-layout.d.ts +55 -0
  128. package/dist/features/comments/margin-layout.js +79 -0
  129. package/dist/features/comments/margin-layout.js.map +1 -0
  130. package/dist/features/comments/model.d.ts +8 -0
  131. package/dist/features/comments/model.js +60 -34
  132. package/dist/features/comments/model.js.map +1 -1
  133. package/dist/features/comments/react/CommentBubble.js +1 -1
  134. package/dist/features/comments/react/CommentComposer.js +1 -1
  135. package/dist/features/comments/react/CommentThreadView.js +2 -2
  136. package/dist/features/comments/react/CommentThreadView.js.map +1 -1
  137. package/dist/features/comments/react/CommentsMargin.d.ts +0 -5
  138. package/dist/features/comments/react/CommentsMargin.js +85 -251
  139. package/dist/features/comments/react/CommentsMargin.js.map +1 -1
  140. package/dist/features/comments/react/CommentsOverlay.js +1 -1
  141. package/dist/features/comments/react/use-comments.js +1 -1
  142. package/dist/features/comments/react/use-margin-layout.js +45 -0
  143. package/dist/features/comments/react/use-margin-layout.js.map +1 -0
  144. package/dist/features/comments/react.d.ts +1 -1
  145. package/dist/features/comments/schema.d.ts +23 -0
  146. package/dist/features/comments/schema.js.map +1 -1
  147. package/dist/features/comments/snapshot-equality.js +40 -0
  148. package/dist/features/comments/snapshot-equality.js.map +1 -0
  149. package/dist/features/comments/thread-anchor.js +33 -0
  150. package/dist/features/comments/thread-anchor.js.map +1 -0
  151. package/dist/features/comments/use-comments.d.ts +3 -1
  152. package/dist/features/comments/use-comments.js +1 -0
  153. package/dist/features/comments/use-comments.js.map +1 -1
  154. package/dist/features/comments/use-margin-layout.js +29 -0
  155. package/dist/features/comments/use-margin-layout.js.map +1 -0
  156. package/dist/features/comments/vue.d.ts +1 -1
  157. package/dist/features/composites/accepted-children.d.ts +21 -0
  158. package/dist/features/composites/accepted-children.js +126 -0
  159. package/dist/features/composites/accepted-children.js.map +1 -0
  160. package/dist/features/composites/components/CompositeDetails.js +1 -1
  161. package/dist/features/composites/components/CompositeInspector.js +38 -164
  162. package/dist/features/composites/components/CompositeInspector.js.map +1 -1
  163. package/dist/features/composites/feature.d.ts +2 -1
  164. package/dist/features/composites/feature.js +8 -2
  165. package/dist/features/composites/feature.js.map +1 -1
  166. package/dist/features/composites/index.d.ts +5 -3
  167. package/dist/features/composites/index.js +8 -6
  168. package/dist/features/composites/inspector-model.d.ts +97 -0
  169. package/dist/features/composites/inspector-model.js +255 -0
  170. package/dist/features/composites/inspector-model.js.map +1 -0
  171. package/dist/features/composites/kinds.d.ts +11 -0
  172. package/dist/features/composites/kinds.js +17 -2
  173. package/dist/features/composites/kinds.js.map +1 -1
  174. package/dist/features/composites/outline.d.ts +18 -5
  175. package/dist/features/composites/outline.js +44 -12
  176. package/dist/features/composites/outline.js.map +1 -1
  177. package/dist/features/composites/react/CompositeDetails.js +1 -1
  178. package/dist/features/composites/react/CompositeInspector.d.ts +1 -1
  179. package/dist/features/composites/react/CompositeInspector.js +21 -180
  180. package/dist/features/composites/react/CompositeInspector.js.map +1 -1
  181. package/dist/features/composites/react/CompositeStatusMenu.d.ts +1 -1
  182. package/dist/features/composites/react/CompositeView.js +1 -1
  183. package/dist/features/composites/react/use-composite-inspector.js +29 -0
  184. package/dist/features/composites/react/use-composite-inspector.js.map +1 -0
  185. package/dist/features/composites/schema.d.ts +2 -2
  186. package/dist/features/composites/schema.js +2 -2
  187. package/dist/features/composites/schema.js.map +1 -1
  188. package/dist/features/composites/use-composite-anchors.d.ts +1 -1
  189. package/dist/features/composites/use-composite-inspector.js +41 -0
  190. package/dist/features/composites/use-composite-inspector.js.map +1 -0
  191. package/dist/features/composites/use-composite-rows.d.ts +1 -1
  192. package/dist/features/media/index.d.ts +1 -1
  193. package/dist/features/media/vue.d.ts +1 -1
  194. package/dist/features/mentions/index.d.ts +3 -3
  195. package/dist/features/mentions/index.js +3 -3
  196. package/dist/features/mentions/schema.d.ts +1 -1
  197. package/dist/features/mentions/schema.js +1 -1
  198. package/dist/features/themes/index.d.ts +1 -1
  199. package/dist/features/themes/index.js +1 -1
  200. package/dist/features/transcript/feature.js +1 -1
  201. package/dist/features/transcript/index.js +2 -2
  202. package/dist/i18n/de/ai.js +2 -0
  203. package/dist/i18n/de/ai.js.map +1 -1
  204. package/dist/i18n/de/composites.js +8 -1
  205. package/dist/i18n/de/composites.js.map +1 -1
  206. package/dist/i18n/de/tree.js +14 -1
  207. package/dist/i18n/de/tree.js.map +1 -1
  208. package/dist/i18n/de.d.ts +22 -0
  209. package/dist/i18n/en/ai.js +2 -0
  210. package/dist/i18n/en/ai.js.map +1 -1
  211. package/dist/i18n/en/composites.js +8 -1
  212. package/dist/i18n/en/composites.js.map +1 -1
  213. package/dist/i18n/en/tree.js +14 -1
  214. package/dist/i18n/en/tree.js.map +1 -1
  215. package/dist/integrations/composite-ai/composite-fill.js +2 -2
  216. package/dist/integrations/composite-ai/composite-summary.js +2 -2
  217. package/dist/integrations/composite-ai/index.js +2 -2
  218. package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
  219. package/dist/markdown/MarkdownSurface.d.ts +1 -1
  220. package/dist/markdown/MarkdownSurface.js +2 -2
  221. package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
  222. package/dist/markdown/index.d.ts +2 -2
  223. package/dist/nuxt/runtime/server/routes/collab/[id].js +1 -1
  224. package/dist/react/EditorSurface.d.ts +1 -1
  225. package/dist/react/EditorSurface.js +43 -149
  226. package/dist/react/EditorSurface.js.map +1 -1
  227. package/dist/react/EditorToolbar.js +3 -40
  228. package/dist/react/EditorToolbar.js.map +1 -1
  229. package/dist/schema/advanced.js +2 -2
  230. package/dist/server/adapter.d.ts +2 -2
  231. package/dist/server/collab.js +1 -1
  232. package/dist/server/handlers/ai.d.ts +4 -2
  233. package/dist/server/handlers/ai.js +11 -9
  234. package/dist/server/handlers/ai.js.map +1 -1
  235. package/dist/server/handlers/context.d.ts +1 -1
  236. package/dist/server/handlers/editor.d.ts +1 -1
  237. package/dist/server/handlers/editor.js +2 -2
  238. package/dist/server/host.d.ts +1 -1
  239. package/dist/server/index.d.ts +2 -2
  240. package/dist/settings/index.d.ts +1 -1
  241. package/dist/styles/tokens.css +1 -1
  242. package/dist/tree/DocumentTree.js +159 -387
  243. package/dist/tree/DocumentTree.js.map +1 -1
  244. package/dist/tree/drop-rules.js +37 -0
  245. package/dist/tree/drop-rules.js.map +1 -0
  246. package/dist/tree/react/DocumentTree.d.ts +2 -2
  247. package/dist/tree/react/DocumentTree.js +135 -397
  248. package/dist/tree/react/DocumentTree.js.map +1 -1
  249. package/dist/tree/react/use-tree-model.js +129 -0
  250. package/dist/tree/react/use-tree-model.js.map +1 -0
  251. package/dist/tree/react.js +1 -1
  252. package/dist/tree/tree-host.js +100 -0
  253. package/dist/tree/tree-host.js.map +1 -0
  254. package/dist/tree/tree-items.js +22 -43
  255. package/dist/tree/tree-items.js.map +1 -1
  256. package/dist/tree/tree-model.js +580 -0
  257. package/dist/tree/tree-model.js.map +1 -0
  258. package/dist/tree/tree-rows.js +77 -0
  259. package/dist/tree/tree-rows.js.map +1 -0
  260. package/dist/tree/use-tree-model.js +94 -0
  261. package/dist/tree/use-tree-model.js.map +1 -0
  262. package/dist/tree/vue.js +1 -1
  263. package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
  264. package/dist/vue/SimpleEditor.vue.d.ts +2 -2
  265. package/dist/vue/components/EditorSurface.js +27 -87
  266. package/dist/vue/components/EditorSurface.js.map +1 -1
  267. package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
  268. package/dist/vue/components/EditorToolbar.js +7 -44
  269. package/dist/vue/components/EditorToolbar.js.map +1 -1
  270. package/dist/vue/context.d.ts +1 -1
  271. package/dist/vue/context.js +1 -1
  272. package/dist/vue/context.js.map +1 -1
  273. package/dist/vue/use-editor-ui.d.ts +1 -1
  274. package/dist/vue/use-notifier.d.ts +1 -1
  275. package/docs/integration.md +97 -4
  276. package/docs/llms.md +9 -1
  277. package/docs/usage.md +10 -5
  278. package/package.json +67 -5
@@ -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", { "ring-2 ring-[var(--sk-primary)]": __props.active }]]),
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 { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, toDisplayString, unref, vShow, watch, withDirectives } from "vue";
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. Cards sit absolutely at the height of their text (top-down in document order, pushed
38
- * down so they never overlap); the active thread is expanded, the others collapse to one line. The draft composer
39
- * takes part in the layout, orphaned threads stack after the positioned cards and resolved threads („Erledigt“)
40
- * sit at the bottom behind a toggle. Shown only while `open` (the host binds the surface's `commentsOpen` model) and the
41
- * user's comments layout is `margin`; the layout is recomputed whenever the column becomes visible again.
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
- const positioned = computed(() => {
49
- const items = [];
50
- const draft = comments.draft.value;
51
- if (draft) items.push({
52
- key: DRAFT,
53
- from: draft.from,
54
- thread: null
55
- });
56
- for (const thread of comments.open.value) {
57
- const range = comments.ranges.value.get(thread.id);
58
- if (range) items.push({
59
- key: thread.id,
60
- from: range.from,
61
- thread
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
- ref_key: "column",
177
- ref: column,
79
+ ref: unref(setColumn),
178
80
  class: "relative w-[300px] shrink-0",
179
- style: normalizeStyle({ minHeight: `${minHeight.value}px` }),
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
- (openBlock(true), createElementBlock(Fragment, null, renderList(positioned.value, (item) => {
183
- return openBlock(), createElementBlock("div", {
184
- key: item.key,
185
- ref_for: true,
186
- ref: (el) => setCard(item.key, el),
187
- class: normalizeClass(["absolute inset-x-0 transition-[top] duration-100", { invisible: tops.value[item.key] === void 0 }]),
188
- style: normalizeStyle(cardStyle(item.key))
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
- ref_key: "resolvedSection",
255
- ref: resolvedSection,
256
- class: "absolute inset-x-0 flex flex-col gap-2",
257
- style: normalizeStyle({ top: `${resolvedTop.value}px` })
258
- }, [createVNode(unref(UButton), {
259
- color: "neutral",
260
- variant: "ghost",
261
- size: "sm",
262
- label: unref(t)("comments.resolvedCount", { n: unref(comments).resolved.value.length }),
263
- "trailing-icon": showResolved.value ? "i-lucide-chevron-up" : "i-lucide-chevron-down",
264
- class: "self-start",
265
- onClick: _cache[2] || (_cache[2] = ($event) => showResolved.value = !showResolved.value)
266
- }, null, 8, ["label", "trailing-icon"]), showResolved.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(unref(comments).resolved.value, (thread) => {
267
- return openBlock(), createBlock(_sfc_main$2, {
268
- key: thread.id,
269
- thread,
270
- user: unref(comments).user.value,
271
- active: unref(comments).active.value === thread.id,
272
- orphan: unref(comments).isOrphan(thread.id),
273
- comments: unref(comments),
274
- onClick: ($event) => select(thread.id)
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
- "thread",
277
- "user",
278
- "active",
279
- "orphan",
280
- "comments",
281
- "onClick"
282
- ]);
283
- }), 128)) : createCommentVNode("v-if", true)], 4)) : createCommentVNode("v-if", true)
284
- ], 64))], 12, _hoisted_1)), [[vShow, __props.open && enabled.value]]);
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":"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"}
@@ -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
- export { COMMENTS_FIELD, COMMENTS_LAYOUTS, COMMENT_LIMITS, 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, type DocRange, cancelCommentDraft, commentsAvailable, commentsCanComment, commentsDoc, commentsFeatureCore, commentsKey, commentsModelOf, commentsSchemaExtensions, commentsState, commentsUser, createCommentsModel, createEditorCommentsModel, setActiveComment, startCommentDraft };
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 };