collabmd 0.1.44 → 0.1.46

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 (291) hide show
  1. package/README.md +39 -19
  2. package/bin/collabmd.js +27 -1
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js +1 -0
  4. package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
  5. package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +41 -0
  6. package/dist/client/assets/{chunk-CZQZEIFA-DT3N9fo7.js → chunk-YGJVY4SA-CjSe7Bx7.js} +2 -2
  7. package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
  8. package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
  9. package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +7 -0
  10. package/dist/client/assets/dist-B5HTH2BO.js +1 -0
  11. package/dist/client/assets/{dist-DhLvC8DS.js → dist-B5Jf1SLZ.js} +2 -2
  12. package/dist/client/assets/{dist-CnL7Y2sM.js → dist-BDt3rz7l.js} +1 -1
  13. package/dist/client/assets/dist-BEwK4csK.js +1 -0
  14. package/dist/client/assets/dist-BKW4PALF.js +1 -0
  15. package/dist/client/assets/dist-BWcHHOx-.js +1 -0
  16. package/dist/client/assets/{dist-CYZmbokb.js → dist-BalScOwZ.js} +9 -9
  17. package/dist/client/assets/dist-Bf8f4HmM.js +6 -0
  18. package/dist/client/assets/dist-BjHJDtyU.js +1 -0
  19. package/dist/client/assets/dist-Bm6pf9Bl.js +1 -0
  20. package/dist/client/assets/dist-BoVBdSjk.js +1 -0
  21. package/dist/client/assets/dist-CC5IrQMG.js +1 -0
  22. package/dist/client/assets/{dist-C11hHqv_.js → dist-CCP_AKPG.js} +12 -12
  23. package/dist/client/assets/dist-CZgOmxK6.js +1 -0
  24. package/dist/client/assets/{dist-D2AQ-76g.js → dist-Cqhv5LAk.js} +1 -1
  25. package/dist/client/assets/{dist-MvcV4190.js → dist-D-Z6FLem.js} +1 -1
  26. package/dist/client/assets/dist-DLMiIsNC.js +11 -0
  27. package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZKu9Cs8.js} +1 -1
  28. package/dist/client/assets/dist-DaYdhcVs.js +1 -0
  29. package/dist/client/assets/dist-FWJsgGYI.js +1 -0
  30. package/dist/client/assets/dist-Kh2COra_.js +1 -0
  31. package/dist/client/assets/{dist-JYEDRrPT.js → dist-PDZ3bSqm.js} +1 -1
  32. package/dist/client/assets/{dist-Drp8uu5J.js → dist-TjMh8n1u.js} +1 -1
  33. package/dist/client/assets/{dist-DGb27Dl6.js → dist-dtAdgL39.js} +1 -1
  34. package/dist/client/assets/dist-jv4WdPnK.js +9 -0
  35. package/dist/client/assets/{dist-DK5Gs2wa.js → dist-rGBmqxC7.js} +1 -1
  36. package/dist/client/assets/{dist-DCzuX1AF.js → dist-shrl6K_0.js} +1 -1
  37. package/dist/client/assets/dist-tOaegDYS2.js +1 -0
  38. package/dist/client/assets/dist-ule2n6ak.js +1 -0
  39. package/dist/client/assets/dist-xElaxbia.js +1 -0
  40. package/dist/client/assets/drawio-editor-FzY1cVw8.js +9 -0
  41. package/dist/client/assets/drawioEditor-BjiKJqlc.js +2 -0
  42. package/dist/client/assets/editor-session-Cm9ekeNo.js +54 -0
  43. package/dist/client/assets/embedded-editor-base-CwTkk911.css +1 -0
  44. package/dist/client/assets/{en-GADW4C6J-DQ6zyCEM.js → en-7NFCYGKX-BrgYZAEe.js} +1 -1
  45. package/dist/client/assets/{preview-render-profile-BZzGSg6K.js → es-DXGV3RHd.js} +13 -13
  46. package/dist/client/assets/excalidraw-editor-B46zsTFI.js +9 -0
  47. package/dist/client/assets/excalidraw-editor-Bt7gMSoh.css +1 -0
  48. package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
  49. package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +2 -0
  50. package/dist/client/assets/export-page-A7NHawJY.js +39 -0
  51. package/dist/client/assets/exportDocument-B2L8fe34.css +1 -0
  52. package/dist/client/assets/exportDocument-BaqcOAAW.js +2 -0
  53. package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
  54. package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
  55. package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +1 -0
  56. package/dist/client/assets/index-ClbjMBVt.js +2 -0
  57. package/dist/client/assets/index-Pum8ET-5.css +1 -0
  58. package/dist/client/assets/main-B3uPyqic.js +893 -0
  59. package/dist/client/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
  60. package/dist/client/assets/pdf-BL9Pr2D5.js +54 -0
  61. package/dist/client/assets/pdf.worker-CLesOks4.mjs +64857 -0
  62. package/dist/client/assets/pdf_viewer-FVkkThnz.js +551 -0
  63. package/dist/client/assets/preview-diagram-utils-txBCoW8d.js +1 -0
  64. package/dist/client/assets/{preview-render-compiler-BSaQO_p3.js → preview-render-compiler-BD-XhkS2.js} +2 -2
  65. package/dist/client/assets/preview-render-compiler-BKedaMxx.js +1 -0
  66. package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
  67. package/dist/client/assets/{preview-render-worker-B42LWvrD.js → preview-render-worker-CiPqc9n6.js} +20 -20
  68. package/dist/client/assets/prod-Dy20X7eT.js +1 -0
  69. package/dist/client/assets/prod-hnwh7wRM.js +177 -0
  70. package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +12 -0
  71. package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
  72. package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
  73. package/dist/client/assets/{yjs-provider-reset-guard-Cox4nxwu.js → yjs-provider-reset-guard-DE0vINDP.js} +1 -1
  74. package/dist/client/drawio-editor.html +3 -2
  75. package/dist/client/excalidraw-editor.html +3 -2
  76. package/dist/client/export-document.html +4 -3
  77. package/dist/client/index.html +109 -27
  78. package/docker-compose.yml +42 -0
  79. package/package.json +26 -15
  80. package/scripts/local-structurizr-compose.mjs +101 -0
  81. package/src/client/app/drawio-editor-entry.js +0 -3
  82. package/src/client/app/drawio-editor.html +1 -0
  83. package/src/client/app/excalidraw-editor-entry.js +0 -3
  84. package/src/client/app/excalidraw-editor.html +1 -0
  85. package/src/client/app/export-document-entry.js +0 -3
  86. package/src/client/app/export-document.html +1 -0
  87. package/src/client/app/index.html +106 -24
  88. package/src/client/app/main-entry.js +0 -5
  89. package/src/client/application/app-shell/chat-feature.js +180 -124
  90. package/src/client/application/app-shell/comments-feature.js +20 -11
  91. package/src/client/application/app-shell/git-feature.js +16 -9
  92. package/src/client/application/app-shell/presence-feature.js +18 -11
  93. package/src/client/application/app-shell/ui-feature-shell.js +78 -17
  94. package/src/client/application/app-shell/ui-feature-sidebar.js +74 -1
  95. package/src/client/application/app-shell/ui-feature-tab-activity.js +2 -2
  96. package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
  97. package/src/client/application/app-shell/workspace-feature.js +27 -84
  98. package/src/client/application/app-shell-elements.js +9 -1
  99. package/src/client/application/diagram-chrome.js +161 -166
  100. package/src/client/application/diagram-preview-export.js +7 -34
  101. package/src/client/application/diagram-preview-hydrator.js +1 -19
  102. package/src/client/application/markdown-frontmatter.js +0 -2
  103. package/src/client/application/mermaid-preview-hydrator.js +1 -14
  104. package/src/client/application/pdf-preview-controller.js +992 -0
  105. package/src/client/application/plantuml-preview-hydrator.js +0 -10
  106. package/src/client/application/preview-diagram-utils.js +31 -2
  107. package/src/client/application/preview-render-compiler.js +2 -1
  108. package/src/client/application/preview-renderer.js +2 -2
  109. package/src/client/application/quick-switcher-loader.js +3 -1
  110. package/src/client/application/structurizr-preview-controller.js +395 -0
  111. package/src/client/application/workspace-coordinator.js +25 -3
  112. package/src/client/application/workspace-preview-controller.js +91 -5
  113. package/src/client/bootstrap/collabmd-app-shell.js +155 -43
  114. package/src/client/browser-utils.js +44 -0
  115. package/src/client/domain/excalidraw-collaboration.js +0 -2
  116. package/src/client/domain/room.js +38 -0
  117. package/src/client/domain/runtime-paths.js +26 -5
  118. package/src/client/domain/structurizr-language.js +99 -0
  119. package/src/client/domain/vault-paths.js +48 -0
  120. package/src/client/excalidraw-editor.js +833 -51
  121. package/src/client/export/export-pipeline.js +6 -34
  122. package/src/client/infrastructure/auth-client.js +4 -10
  123. package/src/client/infrastructure/browser-notification-port.js +33 -20
  124. package/src/client/infrastructure/browser-preferences-port.js +46 -9
  125. package/src/client/infrastructure/comment-thread-store.js +34 -13
  126. package/src/client/infrastructure/editor-session.js +10 -0
  127. package/src/client/infrastructure/editor-view-adapter.js +133 -5
  128. package/src/client/infrastructure/excalidraw-room-client.js +103 -14
  129. package/src/client/infrastructure/git-api-client.js +5 -0
  130. package/src/client/infrastructure/lobby-presence.js +1 -0
  131. package/src/client/infrastructure/vault-api-client.js +51 -0
  132. package/src/client/infrastructure/workspace-sync-client.js +13 -1
  133. package/src/client/presentation/comment-overview-controller.js +18 -53
  134. package/src/client/presentation/comment-ui/comment-ui-card.js +1 -12
  135. package/src/client/presentation/comment-ui/comment-ui-layout.js +242 -219
  136. package/src/client/presentation/comment-ui/comment-ui-render.js +32 -45
  137. package/src/client/presentation/comment-ui/comment-ui-shared.js +68 -13
  138. package/src/client/presentation/comment-ui-controller.js +5 -1
  139. package/src/client/presentation/drawio-embed-controller.js +9 -102
  140. package/src/client/presentation/drawio-viewer.js +118 -0
  141. package/src/client/presentation/excalidraw-embed-controller.js +74 -8
  142. package/src/client/presentation/file-action-controller.js +134 -0
  143. package/src/client/presentation/file-explorer-controller.js +33 -3
  144. package/src/client/presentation/file-explorer-view.js +59 -34
  145. package/src/client/presentation/file-icon-svg.js +40 -0
  146. package/src/client/presentation/file-tree-state.js +12 -4
  147. package/src/client/presentation/git-diff-view-controller.js +731 -3
  148. package/src/client/presentation/outline-controller.js +86 -0
  149. package/src/client/presentation/quick-switcher-controller.js +257 -74
  150. package/src/client/presentation/quick-switcher-text-search.js +3 -3
  151. package/src/client/presentation/toast-controller.js +3 -0
  152. package/src/client/styles/components/feedback.css +6 -0
  153. package/src/client/styles/components/navigation.css +11 -1
  154. package/src/client/styles/features/collaboration-chat.css +49 -53
  155. package/src/client/styles/features/collaboration-presence.css +9 -7
  156. package/src/client/styles/features/comment-card.css +25 -11
  157. package/src/client/styles/features/comment-overlays.css +0 -4
  158. package/src/client/styles/features/comment-overview.css +44 -21
  159. package/src/client/styles/features/comments-drawer.css +2 -100
  160. package/src/client/styles/features/diagram-preview.css +81 -18
  161. package/src/client/styles/features/diff-special.css +309 -0
  162. package/src/client/styles/features/embedded-preview.css +8 -33
  163. package/src/client/styles/features/quick-switcher.css +8 -0
  164. package/src/client/styles/foundation/themes.css +6 -0
  165. package/src/client/styles/foundation/tokens.css +1 -0
  166. package/src/client/styles/layout/editor-page.css +40 -0
  167. package/src/client/styles/layout/responsive.css +31 -1
  168. package/src/client/styles/layout/shell.css +383 -7
  169. package/src/client/styles/layout/sidebar.css +47 -15
  170. package/src/client/styles/layout/view-modes.css +1 -0
  171. package/src/client/styles/overrides/codemirror.css +217 -15
  172. package/src/client/styles/style.css +1 -0
  173. package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
  174. package/src/domain/comment-threads.js +63 -5
  175. package/src/domain/excalidraw-room-codec.js +6 -4
  176. package/src/domain/file-kind.js +35 -3
  177. package/src/server/application/workspace-reconciliation.js +1 -1
  178. package/src/server/config/env.js +79 -0
  179. package/src/server/create-app-server.js +14 -3
  180. package/src/server/domain/bases/base-definition.js +1 -1
  181. package/src/server/domain/bases/base-expression-runtime.js +1 -4
  182. package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
  183. package/src/server/domain/bases/base-query-service.js +1 -77
  184. package/src/server/domain/bases/base-transform.js +1 -4
  185. package/src/server/domain/collaboration/collaboration-room.js +55 -21
  186. package/src/server/domain/comment-overview.js +25 -8
  187. package/src/server/domain/docx-exporter.js +24 -33
  188. package/src/server/domain/ripgrep-search-service.js +7 -1
  189. package/src/server/domain/workspace-state.js +32 -11
  190. package/src/server/infrastructure/git/diff-service.js +29 -13
  191. package/src/server/infrastructure/git/git-service.js +61 -10
  192. package/src/server/infrastructure/git/history-service.js +96 -29
  193. package/src/server/infrastructure/git/image-mime.js +14 -0
  194. package/src/server/infrastructure/git/path-utils.js +1 -1
  195. package/src/server/infrastructure/git/responses.js +0 -95
  196. package/src/server/infrastructure/git/untracked-files.js +28 -1
  197. package/src/server/infrastructure/github/github-app-client.js +22 -15
  198. package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
  199. package/src/server/infrastructure/http/create-request-handler.js +18 -31
  200. package/src/server/infrastructure/http/create-static-handler.js +5 -2
  201. package/src/server/infrastructure/http/create-structurizr-api-handler.js +145 -0
  202. package/src/server/infrastructure/http/create-vault-api-command-handler.js +70 -8
  203. package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
  204. package/src/server/infrastructure/http/http-errors.js +7 -1
  205. package/src/server/infrastructure/http/http-request-helpers.js +22 -0
  206. package/src/server/infrastructure/persistence/path-utils.js +1 -1
  207. package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
  208. package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
  209. package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +374 -0
  210. package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
  211. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
  212. package/dist/client/assets/bases-preview-controller-2pLHk0uk.js +0 -143
  213. package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
  214. package/dist/client/assets/button-BCEiw4Rw.js +0 -1
  215. package/dist/client/assets/chunk-AXR3VHVW-0GTk_evH.js +0 -41
  216. package/dist/client/assets/comment-threads-D-iKphIp.js +0 -4
  217. package/dist/client/assets/diagram-action-icons-aa_KOv9h.js +0 -36
  218. package/dist/client/assets/dist-BH87JoHx.js +0 -11
  219. package/dist/client/assets/dist-BIkPRZO7.js +0 -6
  220. package/dist/client/assets/dist-BqCsFpx4.js +0 -1
  221. package/dist/client/assets/dist-C2JHCt8U.js +0 -1
  222. package/dist/client/assets/dist-CZjTtmTu.js +0 -1
  223. package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
  224. package/dist/client/assets/dist-CqQEC8gR.js +0 -1
  225. package/dist/client/assets/dist-D4Rzqod1.js +0 -1
  226. package/dist/client/assets/dist-D5sn-mwk.js +0 -9
  227. package/dist/client/assets/dist-D7G-n87v.js +0 -1
  228. package/dist/client/assets/dist-DRqOrNgN.js +0 -1
  229. package/dist/client/assets/dist-DetkMS_4.js +0 -1
  230. package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
  231. package/dist/client/assets/dist-DtbjNB28.js +0 -1
  232. package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
  233. package/dist/client/assets/dist-GrygD2zE.js +0 -1
  234. package/dist/client/assets/dist-HmEpWUow.js +0 -1
  235. package/dist/client/assets/dist-RTa81Km7.js +0 -1
  236. package/dist/client/assets/drawio-editor-DDvFoarP.js +0 -9
  237. package/dist/client/assets/drawio-embed-controller-BD1GTRwn.js +0 -1
  238. package/dist/client/assets/drawioEditor-Bu5grlH5.js +0 -2
  239. package/dist/client/assets/editor-session-DGlpiAsK.js +0 -16
  240. package/dist/client/assets/embedded-editor-base-C9roKYpW.css +0 -1
  241. package/dist/client/assets/excalidraw-editor-BHu6k8d4.js +0 -9
  242. package/dist/client/assets/excalidraw-editor-Cr_184EF.css +0 -1
  243. package/dist/client/assets/excalidraw-embed-controller-y2fenN_g.js +0 -1
  244. package/dist/client/assets/excalidraw-embed-reconciler-DGiagExg.js +0 -1
  245. package/dist/client/assets/excalidraw-scene-BvsQ6mLP.js +0 -176
  246. package/dist/client/assets/excalidrawEditor-4vFE--Uw.js +0 -2
  247. package/dist/client/assets/export-page-Cby8JzPO.js +0 -39
  248. package/dist/client/assets/exportDocument-C9v-bc7I.css +0 -1
  249. package/dist/client/assets/exportDocument-CRHCfsdo.js +0 -2
  250. package/dist/client/assets/file-history-view-controller-BDjEV9xJ.js +0 -112
  251. package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
  252. package/dist/client/assets/git-diff-view-controller-DgdM1aqb.js +0 -150
  253. package/dist/client/assets/git-panel-controller-BDTR2HVp.js +0 -207
  254. package/dist/client/assets/image-XGD7JQR6-XDUWHRpO.js +0 -1
  255. package/dist/client/assets/index-7xOyXDhP.css +0 -1
  256. package/dist/client/assets/index-Bz1KuQ6y.js +0 -2
  257. package/dist/client/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  258. package/dist/client/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  259. package/dist/client/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  260. package/dist/client/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  261. package/dist/client/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  262. package/dist/client/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  263. package/dist/client/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  264. package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  265. package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  266. package/dist/client/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  267. package/dist/client/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  268. package/dist/client/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  269. package/dist/client/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  270. package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  271. package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  272. package/dist/client/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  273. package/dist/client/assets/main-Ci07m9oP.js +0 -152
  274. package/dist/client/assets/preview-diagram-utils-n_bbJA9V.js +0 -1
  275. package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
  276. package/dist/client/assets/quick-switcher-controller-CoiYvllm.js +0 -12
  277. package/dist/client/assets/room-CDrmIdDt.js +0 -1
  278. package/dist/client/assets/runtime-config-CDgeuPWN.js +0 -1
  279. package/dist/client/assets/runtime-config-loader-BHGniHP5.js +0 -1
  280. package/dist/client/assets/runtime-paths-DvpihCEq.js +0 -1
  281. package/dist/client/assets/segmented-control-DJrHSz8M.js +0 -1
  282. package/dist/client/assets/vault-api-client-sLB2bo56.js +0 -1
  283. package/dist/client/assets/vault-paths-CVkuh0j-.js +0 -5
  284. package/src/client/bootstrap/lazy-controller-feature.js +0 -433
  285. package/src/client/infrastructure/backlinks-api-client.js +0 -16
  286. package/src/client/infrastructure/plantuml-api-client.js +0 -19
  287. package/src/client/infrastructure/runtime-config-loader.js +0 -56
  288. package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
  289. package/src/server/infrastructure/git/command-runner.js +0 -49
  290. package/src/server/infrastructure/git/errors.js +0 -8
  291. package/src/server/infrastructure/http/create-esm-proxy-handler.js +0 -130
@@ -1,9 +1,5 @@
1
1
  import {
2
2
  COMMENT_CONTROL_SLOT_HEIGHT,
3
- COMMENT_PREVIEW_BADGE_MIN_WIDTH,
4
- COMMENT_PREVIEW_RAIL_BREAKPOINT,
5
- COMMENT_PREVIEW_RAIL_MIN_WIDTH,
6
- COMMENT_PREVIEW_RAIL_SLOT_HEIGHT,
7
3
  COMMENT_SELECTION_CHIP_GAP,
8
4
  clamp,
9
5
  createCommentMarkerContent,
@@ -17,59 +13,21 @@ import {
17
13
  toRelativeRect,
18
14
  } from './comment-ui-shared.js';
19
15
 
16
+ function setInteractionClasses(element, isActive, isHovered) {
17
+ element.classList.toggle('is-active', isActive);
18
+ element.classList.toggle('is-hovered', isHovered);
19
+ element.classList.toggle('is-passive', !isActive && !isHovered);
20
+ }
21
+
20
22
  /** @this {any} */
21
23
  function refreshLayout() {
22
- this.renderEditorLayer();
23
- this.renderPreviewLayer();
24
+ const groups = this.getThreadGroups();
25
+ this.renderEditorLayer(groups);
26
+ this.renderPreviewLayer(groups);
24
27
  this.repositionActiveCard();
25
28
  }
26
29
 
27
30
  /** @this {any} */
28
- function syncPreviewRailLayout(maxBubbleWidth = 0) {
29
- if (!this.previewElement) {
30
- return false;
31
- }
32
-
33
- const shouldShowRail = this.supported && maxBubbleWidth > 0;
34
- const nextReserved = 0;
35
- const nextOffset = 0;
36
- let reserved = nextReserved;
37
- let offset = nextOffset;
38
-
39
- if (shouldShowRail) {
40
- const previewStyle = getComputedStyle(this.previewElement);
41
- const currentReserved = Number.parseFloat(
42
- previewStyle.getPropertyValue('--preview-comment-rail-reserved'),
43
- ) || 0;
44
- const currentPaddingRight = Number.parseFloat(previewStyle.paddingRight) || 0;
45
- const railInset = Number.parseFloat(
46
- previewStyle.getPropertyValue('--preview-comment-rail-inset'),
47
- ) || 0;
48
- const basePaddingRight = Math.max(currentPaddingRight - currentReserved, 0);
49
- const requiredRail = Math.max(maxBubbleWidth + railInset - basePaddingRight, 0);
50
- const previewContainerRect = this.previewContainer?.getBoundingClientRect();
51
- const previewRect = this.previewElement.getBoundingClientRect();
52
- const availableRightGutter = previewContainerRect
53
- ? Math.max(previewContainerRect.right - previewRect.right, 0)
54
- : 0;
55
-
56
- offset = Math.min(availableRightGutter, requiredRail);
57
- reserved = Math.max(requiredRail - offset, 0);
58
- }
59
-
60
- const nextReservedValue = `${Math.ceil(reserved)}px`;
61
- const nextOffsetValue = `${Math.floor(offset)}px`;
62
- const didChange = this.previewElement.style.getPropertyValue('--preview-comment-rail-reserved') !== nextReservedValue
63
- || this.previewElement.style.getPropertyValue('--preview-comment-rail-offset') !== nextOffsetValue;
64
-
65
- if (didChange) {
66
- this.previewElement.style.setProperty('--preview-comment-rail-reserved', nextReservedValue);
67
- this.previewElement.style.setProperty('--preview-comment-rail-offset', nextOffsetValue);
68
- }
69
-
70
- return didChange;
71
- }
72
-
73
31
  /** @this {any} */
74
32
  function scheduleLayoutRefresh() {
75
33
  if (this.layoutFrame) {
@@ -96,20 +54,29 @@ function ensureEditorLayer() {
96
54
  }
97
55
 
98
56
  /** @this {any} */
99
- function renderEditorLayer() {
57
+ function renderEditorLayer(groups = this.getThreadGroups()) {
100
58
  const layer = this.ensureEditorLayer();
101
- layer.replaceChildren();
102
59
 
103
60
  if (!this.supported || !this.session) {
61
+ if (layer.childElementCount > 0) {
62
+ layer.replaceChildren();
63
+ }
104
64
  return;
105
65
  }
106
66
 
107
67
  const containerRect = this.editorContainer?.getBoundingClientRect?.();
108
68
  if (!containerRect) {
69
+ if (layer.childElementCount > 0) {
70
+ layer.replaceChildren();
71
+ }
109
72
  return;
110
73
  }
111
74
 
112
- const groups = this.getThreadGroups();
75
+ const existingBadges = new Map(
76
+ Array.from(layer.querySelectorAll('.comment-editor-badge'))
77
+ .map((button) => [button.dataset.commentEditorGroupKey, button]),
78
+ );
79
+ const visibleGroupKeys = new Set();
113
80
  const occupiedTops = [];
114
81
  groups.forEach((group) => {
115
82
  const rect = this.session.getCommentAnchorClientRect?.(group.anchor);
@@ -122,59 +89,82 @@ function renderEditorLayer() {
122
89
  return;
123
90
  }
124
91
 
125
- const button = document.createElement('button');
126
- button.type = 'button';
127
- button.className = 'ui-state-marker ui-state-marker--comment comment-editor-badge';
128
- button.dataset.count = String(group.threads.length);
92
+ let button = existingBadges.get(group.key);
93
+ if (!button) {
94
+ button = document.createElement('button');
95
+ button.type = 'button';
96
+ button.className = 'ui-state-marker ui-state-marker--comment comment-editor-badge';
97
+ button.dataset.commentEditorGroupKey = group.key;
98
+ button.addEventListener('pointerdown', (event) => {
99
+ event.preventDefault();
100
+ });
101
+ button.addEventListener('pointerenter', () => {
102
+ this.updateHoveredEditorGroups([button.dataset.commentEditorGroupKey]);
103
+ });
104
+ button.addEventListener('pointerleave', () => {
105
+ this.updateHoveredEditorGroups([]);
106
+ });
107
+ button.addEventListener('focusin', () => {
108
+ this.updateHoveredEditorGroups([button.dataset.commentEditorGroupKey]);
109
+ });
110
+ button.addEventListener('focusout', () => {
111
+ this.updateHoveredEditorGroups([]);
112
+ });
113
+ button.addEventListener('click', () => {
114
+ const currentGroup = button.commentGroup;
115
+ if (!currentGroup) {
116
+ return;
117
+ }
118
+ this.openThreadGroup(currentGroup, {
119
+ anchor: currentGroup.anchor,
120
+ origin: 'editor',
121
+ sourceRect: button.commentSourceRect,
122
+ });
123
+ });
124
+ layer.appendChild(button);
125
+ }
126
+
127
+ button.commentGroup = group;
128
+ button.commentSourceRect = rect;
129
+ if (button.dataset.count !== String(group.threads.length)) {
130
+ button.dataset.count = String(group.threads.length);
131
+ button.replaceChildren(createCommentMarkerContent(group.threads.length));
132
+ }
129
133
  const isActive = this.activeCard?.groupKey === group.key;
130
134
  const isHovered = this.hoveredEditorGroupKeys.includes(group.key);
131
- button.classList.toggle('is-active', isActive);
132
- button.classList.toggle('is-hovered', isHovered);
133
- button.classList.toggle('is-passive', !isActive && !isHovered);
135
+ setInteractionClasses(button, isActive, isHovered);
134
136
  button.setAttribute('aria-label', `${group.threads.length} comment thread${group.threads.length === 1 ? '' : 's'}`);
135
- button.appendChild(createCommentMarkerContent(group.threads.length));
136
137
  const top = Math.max(relativeRect.top, 8);
137
138
  button.style.top = `${top}px`;
138
139
  button.style.left = `${Math.max(containerRect.width - 36, 8)}px`;
139
140
  button.title = `${group.threads.length} comment${group.threads.length === 1 ? '' : 's'}`;
140
- button.addEventListener('pointerdown', (event) => {
141
- event.preventDefault();
142
- });
143
- button.addEventListener('pointerenter', () => {
144
- this.updateHoveredEditorGroups([group.key]);
145
- });
146
- button.addEventListener('pointerleave', () => {
147
- this.updateHoveredEditorGroups([]);
148
- });
149
- button.addEventListener('focusin', () => {
150
- this.updateHoveredEditorGroups([group.key]);
151
- });
152
- button.addEventListener('focusout', () => {
153
- this.updateHoveredEditorGroups([]);
154
- });
155
- button.addEventListener('click', () => {
156
- this.openThreadGroup(group, {
157
- anchor: group.anchor,
158
- origin: 'editor',
159
- sourceRect: rect,
160
- });
161
- });
162
- layer.appendChild(button);
141
+ visibleGroupKeys.add(group.key);
163
142
  occupiedTops.push(top);
164
143
  });
165
144
 
145
+ existingBadges.forEach((button, groupKey) => {
146
+ if (!visibleGroupKeys.has(groupKey)) {
147
+ button.remove();
148
+ }
149
+ });
150
+
151
+ let button = layer.querySelector('.comment-selection-chip');
152
+
166
153
  if (!this.committedSelectionAnchor || this.activeCard?.mode === 'create') {
154
+ button?.remove();
167
155
  return;
168
156
  }
169
157
 
170
158
  const rect = this.session.getCommentAnchorClientRect?.(this.committedSelectionAnchor);
171
159
  const chipRect = this.session.getSelectionChipClientRect?.(this.committedSelectionAnchor) ?? rect;
172
160
  if (!chipRect) {
161
+ button?.remove();
173
162
  return;
174
163
  }
175
164
 
176
165
  const relativeRect = toRelativeRect(chipRect, containerRect);
177
166
  if (relativeRect.bottom < 0 || relativeRect.top > containerRect.height) {
167
+ button?.remove();
178
168
  return;
179
169
  }
180
170
 
@@ -186,18 +176,20 @@ function renderEditorLayer() {
186
176
  Math.max(containerRect.height - COMMENT_CONTROL_SLOT_HEIGHT, 8),
187
177
  );
188
178
  }
189
- const button = document.createElement('button');
190
- button.type = 'button';
191
- button.className = 'ui-selection-pill ui-selection-pill--comment comment-selection-chip';
192
- button.textContent = 'Comment';
179
+ if (!button) {
180
+ button = document.createElement('button');
181
+ button.type = 'button';
182
+ button.className = 'ui-selection-pill ui-selection-pill--comment comment-selection-chip';
183
+ button.textContent = 'Comment';
184
+ button.addEventListener('pointerdown', (event) => {
185
+ event.preventDefault();
186
+ event.stopPropagation();
187
+ this.openComposerForSelection('editor', button.getBoundingClientRect());
188
+ });
189
+ layer.appendChild(button);
190
+ }
193
191
  button.style.top = `${chipTop}px`;
194
192
  button.style.right = `${COMMENT_SELECTION_CHIP_GAP}px`;
195
- button.addEventListener('pointerdown', (event) => {
196
- event.preventDefault();
197
- event.stopPropagation();
198
- this.openComposerForSelection('editor', button.getBoundingClientRect());
199
- });
200
- layer.appendChild(button);
201
193
  }
202
194
 
203
195
  /** @this {any} */
@@ -217,41 +209,121 @@ function ensurePreviewLayer() {
217
209
  }
218
210
 
219
211
  /** @this {any} */
220
- function renderPreviewLayer() {
212
+ function syncPreviewSelectionButton(previewRect) {
213
+ let button = this.previewLayer?.querySelector('.comment-preview-selection-chip');
214
+ const previewSelection = this.activeCard?.mode !== 'create' ? this.previewSelection : null;
215
+ if (!previewSelection) {
216
+ button?.remove();
217
+ return;
218
+ }
219
+
220
+ const selectionRect = createRectFromRects(Array.from(previewSelection.range?.getClientRects?.() ?? []))
221
+ || previewSelection.rect;
222
+ if (!selectionRect) {
223
+ button?.remove();
224
+ return;
225
+ }
226
+
227
+ if (!button) {
228
+ button = document.createElement('button');
229
+ button.type = 'button';
230
+ button.className = 'ui-chip-button ui-chip-button--comment comment-preview-selection-chip';
231
+ button.textContent = 'Comment';
232
+ button.setAttribute('aria-label', 'Comment on selected preview text');
233
+ button.addEventListener('pointerdown', (event) => {
234
+ event.preventDefault();
235
+ event.stopPropagation();
236
+ });
237
+ button.addEventListener('click', () => {
238
+ this.openComposerForSelection(
239
+ 'preview',
240
+ button.getBoundingClientRect(),
241
+ button.previewSelection,
242
+ );
243
+ });
244
+ this.previewLayer?.appendChild(button);
245
+ }
246
+
247
+ button.previewSelection = previewSelection;
248
+ button.style.left = `${clamp(
249
+ selectionRect.left - previewRect.left,
250
+ 8,
251
+ Math.max(this.previewElement.clientWidth - 88, 8),
252
+ )}px`;
253
+ button.style.top = `${clamp(
254
+ selectionRect.bottom - previewRect.top + 8,
255
+ 8,
256
+ Math.max(this.previewElement.clientHeight - COMMENT_CONTROL_SLOT_HEIGHT, 8),
257
+ )}px`;
258
+ }
259
+
260
+ /** @this {any} */
261
+ function renderPreviewLayer(groups = this.getThreadGroups()) {
221
262
  this.ensurePreviewLayer();
222
- this.previewLayer?.replaceChildren();
223
- this.previewHighlightLayer?.replaceChildren();
224
263
 
225
264
  if (!this.supported || !this.previewElement) {
265
+ if (this.previewLayer?.childElementCount > 0) {
266
+ this.previewLayer.replaceChildren();
267
+ }
268
+ if (this.previewHighlightLayer?.childElementCount > 0) {
269
+ this.previewHighlightLayer.replaceChildren();
270
+ }
226
271
  this.previewHoverRegions = [];
227
- this.syncPreviewRailLayout(0);
228
272
  return;
229
273
  }
230
274
 
231
275
  const previewRect = this.previewElement.getBoundingClientRect();
276
+ const targetContext = {
277
+ diagramShells: Array.from(this.previewElement.querySelectorAll('.mermaid-shell, .plantuml-shell')),
278
+ sourceBlocks: Array.from(this.previewElement.querySelectorAll('[data-source-line]'))
279
+ .filter((element) => isLeafSourceBlock(element)),
280
+ };
281
+ const existingHighlights = new Map(
282
+ Array.from(this.previewHighlightLayer?.querySelectorAll('[data-comment-preview-highlight-key]') ?? [])
283
+ .map((highlight) => [highlight.dataset.commentPreviewHighlightKey, highlight]),
284
+ );
285
+ const visibleHighlightKeys = new Set();
286
+ const renderHighlight = ({ groupKey = '', isActive, isHovered, key, rect }) => {
287
+ let highlight = existingHighlights.get(key);
288
+ if (!highlight) {
289
+ highlight = document.createElement('div');
290
+ highlight.className = 'comment-preview-highlight';
291
+ highlight.dataset.commentPreviewHighlightKey = key;
292
+ this.previewHighlightLayer?.appendChild(highlight);
293
+ }
294
+ if (groupKey) {
295
+ highlight.dataset.commentPreviewGroupKey = groupKey;
296
+ highlight.dataset.commentPreviewGroupKeys = groupKey;
297
+ } else {
298
+ delete highlight.dataset.commentPreviewGroupKey;
299
+ delete highlight.dataset.commentPreviewGroupKeys;
300
+ }
301
+ setInteractionClasses(highlight, isActive, isHovered);
302
+ highlight.style.left = `${rect.left - previewRect.left}px`;
303
+ highlight.style.top = `${rect.top - previewRect.top}px`;
304
+ highlight.style.width = `${rect.width}px`;
305
+ highlight.style.height = `${rect.height}px`;
306
+ visibleHighlightKeys.add(key);
307
+ };
308
+
232
309
  if (this.activeCard?.mode === 'create' && this.activeCard.origin === 'preview') {
233
310
  const selectionRects = Array.from(this.activeCard.previewRange?.getClientRects?.() ?? []);
234
311
  const highlightRects = selectionRects.length > 0
235
312
  ? selectionRects
236
- : [this.resolvePreviewTarget(this.activeCard.anchor)?.bubbleRect].filter(Boolean);
237
- highlightRects.forEach((rect) => {
238
- const highlight = document.createElement('div');
239
- highlight.className = 'comment-preview-highlight is-active';
240
- highlight.style.left = `${rect.left - previewRect.left}px`;
241
- highlight.style.top = `${rect.top - previewRect.top}px`;
242
- highlight.style.width = `${rect.width}px`;
243
- highlight.style.height = `${rect.height}px`;
244
- this.previewHighlightLayer?.appendChild(highlight);
313
+ : [this.resolvePreviewTarget(this.activeCard.anchor, targetContext)?.bubbleRect].filter(Boolean);
314
+ highlightRects.forEach((rect, index) => {
315
+ renderHighlight({
316
+ isActive: true,
317
+ isHovered: false,
318
+ key: `selection:${index}`,
319
+ rect,
320
+ });
245
321
  });
246
322
  }
247
323
 
248
- const groups = this.getThreadGroups();
249
- const occupiedTops = [];
250
324
  const hoverRegions = [];
251
- const showPassiveMarkers = this.shouldRenderPassivePreviewMarkers();
252
- const previewBubbles = [];
253
325
  groups.forEach((group) => {
254
- const target = this.resolvePreviewTarget(group.anchor);
326
+ const target = this.resolvePreviewTarget(group.anchor, targetContext);
255
327
  if (!target?.bubbleRect) {
256
328
  return;
257
329
  }
@@ -263,102 +335,26 @@ function renderPreviewLayer() {
263
335
 
264
336
  const isActive = this.activeCard?.groupKey === group.key;
265
337
  const isHovered = this.hoveredPreviewGroupKeys.includes(group.key);
266
- const isEmphasized = isActive || isHovered;
267
-
268
- target.highlightRects?.forEach((rect) => {
269
- const highlight = document.createElement('div');
270
- highlight.className = 'comment-preview-highlight';
271
- highlight.classList.toggle('is-active', isActive);
272
- highlight.classList.toggle('is-hovered', isHovered);
273
- highlight.classList.toggle('is-passive', !isEmphasized);
274
- highlight.style.left = `${rect.left - previewRect.left}px`;
275
- highlight.style.top = `${rect.top - previewRect.top}px`;
276
- highlight.style.width = `${rect.width}px`;
277
- highlight.style.height = `${rect.height}px`;
278
- this.previewHighlightLayer?.appendChild(highlight);
279
- });
280
-
281
- if (!showPassiveMarkers && !isEmphasized) {
282
- return;
283
- }
284
338
 
285
- const bubble = document.createElement('button');
286
- bubble.type = 'button';
287
- bubble.className = 'ui-state-marker ui-state-marker--comment comment-preview-badge';
288
- bubble.dataset.commentPreviewGroupKeys = group.key;
289
- bubble.classList.toggle('is-active', isActive);
290
- bubble.classList.toggle('is-hovered', isHovered);
291
- bubble.classList.toggle('is-passive', !isEmphasized);
292
- bubble.setAttribute('aria-label', `${group.threads.length} comment thread${group.threads.length === 1 ? '' : 's'}`);
293
- bubble.appendChild(createCommentMarkerContent(group.threads.length));
294
- let bubbleTop = clamp(
295
- target.bubbleRect.top - previewRect.top,
296
- 6,
297
- Math.max(this.previewElement.clientHeight - COMMENT_PREVIEW_RAIL_SLOT_HEIGHT, 6),
298
- );
299
- while (occupiedTops.some((top) => Math.abs(top - bubbleTop) < (COMMENT_PREVIEW_RAIL_SLOT_HEIGHT - 4))) {
300
- bubbleTop = clamp(
301
- bubbleTop + COMMENT_PREVIEW_RAIL_SLOT_HEIGHT,
302
- 6,
303
- Math.max(this.previewElement.clientHeight - COMMENT_PREVIEW_RAIL_SLOT_HEIGHT, 6),
304
- );
305
- }
306
- bubble.style.top = `${bubbleTop}px`;
307
- bubble.title = `${group.threads.length} comment${group.threads.length === 1 ? '' : 's'}`;
308
- bubble.addEventListener('pointerdown', (event) => {
309
- event.preventDefault();
310
- });
311
- bubble.addEventListener('click', () => {
312
- this.openThreadGroup(group, {
313
- anchor: group.anchor,
314
- origin: 'preview',
315
- sourceRect: bubble.getBoundingClientRect(),
339
+ target.highlightRects?.forEach((rect, index) => {
340
+ renderHighlight({
341
+ groupKey: group.key,
342
+ isActive,
343
+ isHovered,
344
+ key: `${group.key}:highlight:${index}`,
345
+ rect,
316
346
  });
317
347
  });
318
- this.previewLayer?.appendChild(bubble);
319
- occupiedTops.push(bubbleTop);
320
- previewBubbles.push(bubble);
321
348
  });
322
349
 
323
- if (this.previewSelection && this.activeCard?.mode !== 'create') {
324
- const previewSelection = this.previewSelection;
325
- const selectionRect = createRectFromRects(Array.from(previewSelection.range?.getClientRects?.() ?? []))
326
- || previewSelection.rect;
327
- if (selectionRect) {
328
- const button = document.createElement('button');
329
- button.type = 'button';
330
- button.className = 'ui-chip-button ui-chip-button--comment comment-preview-selection-chip';
331
- button.textContent = 'Comment';
332
- button.setAttribute('aria-label', 'Comment on selected preview text');
333
- button.style.left = `${clamp(
334
- selectionRect.left - previewRect.left,
335
- 8,
336
- Math.max(this.previewElement.clientWidth - 88, 8),
337
- )}px`;
338
- button.style.top = `${clamp(
339
- selectionRect.bottom - previewRect.top + 8,
340
- 8,
341
- Math.max(this.previewElement.clientHeight - COMMENT_CONTROL_SLOT_HEIGHT, 8),
342
- )}px`;
343
- button.addEventListener('pointerdown', (event) => {
344
- event.preventDefault();
345
- event.stopPropagation();
346
- });
347
- button.addEventListener('click', () => {
348
- this.openComposerForSelection('preview', button.getBoundingClientRect(), previewSelection);
349
- });
350
- this.previewLayer?.appendChild(button);
351
- }
352
- }
350
+ syncPreviewSelectionButton.call(this, previewRect);
353
351
 
352
+ existingHighlights.forEach((highlight, key) => {
353
+ if (!visibleHighlightKeys.has(key)) {
354
+ highlight.remove();
355
+ }
356
+ });
354
357
  this.previewHoverRegions = hoverRegions;
355
- const maxBubbleWidth = previewBubbles.reduce(
356
- (maxWidth, bubble) => Math.max(maxWidth, bubble.offsetWidth || COMMENT_PREVIEW_BADGE_MIN_WIDTH),
357
- 0,
358
- );
359
- if (this.syncPreviewRailLayout(maxBubbleWidth)) {
360
- this.scheduleLayoutRefresh();
361
- }
362
358
  if (this.lastPreviewPointerPosition) {
363
359
  this.updateHoveredPreviewGroups(
364
360
  this.getPreviewGroupKeysAtPoint(this.lastPreviewPointerPosition.x, this.lastPreviewPointerPosition.y),
@@ -373,23 +369,27 @@ function clearPreviewSelection() {
373
369
  }
374
370
 
375
371
  /** @this {any} */
376
- function resolvePreviewTarget(anchor) {
372
+ function resolvePreviewTarget(anchor, { diagramShells = null, sourceBlocks = null } = {}) {
377
373
  if (!this.previewElement || !anchor) {
378
374
  return null;
379
375
  }
380
376
 
381
- const diagramShell = Array.from(this.previewElement.querySelectorAll('.mermaid-shell, .plantuml-shell'))
377
+ const diagramShell = (diagramShells ?? Array.from(
378
+ this.previewElement.querySelectorAll('.mermaid-shell, .plantuml-shell'),
379
+ ))
382
380
  .find((element) => overlapsAnchorRange(element, anchor));
383
381
  if (diagramShell) {
382
+ const rect = diagramShell.getBoundingClientRect();
384
383
  return {
385
- bubbleRect: diagramShell.getBoundingClientRect(),
384
+ bubbleRect: rect,
386
385
  highlightRects: [],
387
- hoverRects: [diagramShell.getBoundingClientRect()],
386
+ hoverRects: [rect],
388
387
  };
389
388
  }
390
389
 
391
- const candidates = Array.from(this.previewElement.querySelectorAll('[data-source-line]'))
392
- .filter((element) => isLeafSourceBlock(element) && overlapsAnchorRange(element, anchor));
390
+ const candidates = (sourceBlocks ?? Array.from(this.previewElement.querySelectorAll('[data-source-line]'))
391
+ .filter((element) => isLeafSourceBlock(element)))
392
+ .filter((element) => overlapsAnchorRange(element, anchor));
393
393
 
394
394
  if (anchor.kind === 'text' && anchor.quote) {
395
395
  const matches = candidates
@@ -411,10 +411,11 @@ function resolvePreviewTarget(anchor) {
411
411
  return null;
412
412
  }
413
413
 
414
+ const rect = fallback.getBoundingClientRect();
414
415
  return {
415
- bubbleRect: fallback.getBoundingClientRect(),
416
+ bubbleRect: rect,
416
417
  highlightRects: [],
417
- hoverRects: [fallback.getBoundingClientRect()],
418
+ hoverRects: [rect],
418
419
  };
419
420
  }
420
421
 
@@ -425,6 +426,9 @@ function getPreviewGroupKeysForTarget(target) {
425
426
  }
426
427
 
427
428
  const keyCarrier = target.closest?.('[data-comment-preview-group-keys]');
429
+ if (keyCarrier?.dataset?.commentPreviewGroupKey) {
430
+ return [keyCarrier.dataset.commentPreviewGroupKey];
431
+ }
428
432
  return serializeGroupKeys(
429
433
  String(keyCarrier?.dataset?.commentPreviewGroupKeys ?? '')
430
434
  .split(/\s+/)
@@ -460,7 +464,7 @@ function updateHoveredPreviewGroups(nextKeys = []) {
460
464
 
461
465
  this.hoveredPreviewGroupKeys = normalizedKeys;
462
466
  this.hoveredPreviewGroupKeysSignature = signature;
463
- this.scheduleLayoutRefresh();
467
+ this.syncHoveredCommentClasses();
464
468
  }
465
469
 
466
470
  /** @this {any} */
@@ -473,20 +477,40 @@ function updateHoveredEditorGroups(nextKeys = []) {
473
477
 
474
478
  this.hoveredEditorGroupKeys = normalizedKeys;
475
479
  this.hoveredEditorGroupKeysSignature = signature;
476
- this.scheduleLayoutRefresh();
480
+ this.syncHoveredCommentClasses();
477
481
  }
478
482
 
479
483
  /** @this {any} */
480
- function syncHoveredPreviewGroupsFromTarget(target) {
481
- this.updateHoveredPreviewGroups(this.getPreviewGroupKeysForTarget(target));
484
+ function syncHoveredCommentClasses() {
485
+ this.editorLayer?.querySelectorAll('.comment-editor-badge').forEach((button) => {
486
+ const groupKey = button.dataset.commentEditorGroupKey;
487
+ setInteractionClasses(
488
+ button,
489
+ this.activeCard?.groupKey === groupKey,
490
+ this.hoveredEditorGroupKeys.includes(groupKey),
491
+ );
492
+ });
493
+
494
+ const previewElements = [
495
+ ...Array.from(this.previewLayer?.querySelectorAll('[data-comment-preview-group-key]') ?? []),
496
+ ...Array.from(this.previewHighlightLayer?.querySelectorAll('[data-comment-preview-group-key]') ?? []),
497
+ ];
498
+ previewElements.forEach((element) => {
499
+ const groupKey = element.dataset.commentPreviewGroupKey;
500
+ setInteractionClasses(
501
+ element,
502
+ this.activeCard?.groupKey === groupKey,
503
+ this.hoveredPreviewGroupKeys.includes(groupKey),
504
+ );
505
+ });
482
506
  }
483
507
 
484
508
  /** @this {any} */
485
- function shouldRenderPassivePreviewMarkers() {
486
- const previewWidth = this.previewContainer?.clientWidth ?? this.previewElement?.clientWidth ?? 0;
487
- return window.innerWidth >= COMMENT_PREVIEW_RAIL_BREAKPOINT && previewWidth >= COMMENT_PREVIEW_RAIL_MIN_WIDTH;
509
+ function syncHoveredPreviewGroupsFromTarget(target) {
510
+ this.updateHoveredPreviewGroups(this.getPreviewGroupKeysForTarget(target));
488
511
  }
489
512
 
513
+ /** @this {any} */
490
514
  export const commentUiLayoutMethods = {
491
515
  clearPreviewSelection,
492
516
  ensureEditorLayer,
@@ -498,9 +522,8 @@ export const commentUiLayoutMethods = {
498
522
  renderPreviewLayer,
499
523
  resolvePreviewTarget,
500
524
  scheduleLayoutRefresh,
501
- shouldRenderPassivePreviewMarkers,
525
+ syncHoveredCommentClasses,
502
526
  syncHoveredPreviewGroupsFromTarget,
503
- syncPreviewRailLayout,
504
527
  updateHoveredEditorGroups,
505
528
  updateHoveredPreviewGroups,
506
529
  };