@opengeni/react 1.3.0 → 1.4.2

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 (240) hide show
  1. package/README.md +29 -1
  2. package/dist/accounts.js +2 -0
  3. package/dist/accounts.js.map +1 -1
  4. package/dist/artifacts-document.js +5 -4
  5. package/dist/artifacts-presentation.js +5 -4
  6. package/dist/artifacts-spreadsheet.js +4 -3
  7. package/dist/artifacts.js +14 -13
  8. package/dist/artifacts.js.map +1 -1
  9. package/dist/browser-viewer-WGJ43YGI.js +17 -0
  10. package/dist/chat-interactive-block-ZFHOKTMS.js +12 -0
  11. package/dist/{chunk-WEHMPOXK.js → chunk-2DDJ6YH6.js} +3 -1
  12. package/dist/{chunk-WEHMPOXK.js.map → chunk-2DDJ6YH6.js.map} +1 -1
  13. package/dist/{chunk-C5X5AJXK.js → chunk-2HVLQ26H.js} +5 -4
  14. package/dist/{chunk-C5X5AJXK.js.map → chunk-2HVLQ26H.js.map} +1 -1
  15. package/dist/{chunk-A4UJB2MM.js → chunk-32CGLXW2.js} +4 -3
  16. package/dist/{chunk-A4UJB2MM.js.map → chunk-32CGLXW2.js.map} +1 -1
  17. package/dist/{chunk-5CVXZA3Y.js → chunk-45NUOSXQ.js} +3 -2
  18. package/dist/{chunk-5CVXZA3Y.js.map → chunk-45NUOSXQ.js.map} +1 -1
  19. package/dist/{chunk-WUISZ3WU.js → chunk-53KJBGPZ.js} +5 -4
  20. package/dist/{chunk-WUISZ3WU.js.map → chunk-53KJBGPZ.js.map} +1 -1
  21. package/dist/{chunk-TMYGL6X5.js → chunk-55DXD22O.js} +73 -7
  22. package/dist/chunk-55DXD22O.js.map +1 -0
  23. package/dist/{chunk-JT4JW7BC.js → chunk-5I4VRWBB.js} +3 -1
  24. package/dist/{chunk-JT4JW7BC.js.map → chunk-5I4VRWBB.js.map} +1 -1
  25. package/dist/{chunk-UK4TINKT.js → chunk-B27E3V37.js} +3 -1
  26. package/dist/{chunk-UK4TINKT.js.map → chunk-B27E3V37.js.map} +1 -1
  27. package/dist/{chunk-J6DBS4T3.js → chunk-BNKV5ZLF.js} +5 -4
  28. package/dist/{chunk-J6DBS4T3.js.map → chunk-BNKV5ZLF.js.map} +1 -1
  29. package/dist/{chunk-YM2DE7BQ.js → chunk-C5BF4L26.js} +13 -12
  30. package/dist/{chunk-YM2DE7BQ.js.map → chunk-C5BF4L26.js.map} +1 -1
  31. package/dist/{chunk-YBXQDQFX.js → chunk-C7CMD56T.js} +6 -5
  32. package/dist/{chunk-YBXQDQFX.js.map → chunk-C7CMD56T.js.map} +1 -1
  33. package/dist/{chunk-UGLEYEMP.js → chunk-CM7LVUKC.js} +3 -1
  34. package/dist/{chunk-UGLEYEMP.js.map → chunk-CM7LVUKC.js.map} +1 -1
  35. package/dist/{chunk-PGSX7UCP.js → chunk-CREMJMVN.js} +1502 -833
  36. package/dist/chunk-CREMJMVN.js.map +1 -0
  37. package/dist/{chunk-JVYQRRC4.js → chunk-CVYCLFMH.js} +3 -1
  38. package/dist/{chunk-JVYQRRC4.js.map → chunk-CVYCLFMH.js.map} +1 -1
  39. package/dist/{chunk-Y2PRMYLC.js → chunk-G3U2A6GS.js} +3 -2
  40. package/dist/{chunk-Y2PRMYLC.js.map → chunk-G3U2A6GS.js.map} +1 -1
  41. package/dist/{chunk-WWEUO2PL.js → chunk-G6YRCIYJ.js} +202 -14
  42. package/dist/chunk-G6YRCIYJ.js.map +1 -0
  43. package/dist/{chunk-B3V6TG77.js → chunk-GC3PH6N7.js} +3 -1
  44. package/dist/{chunk-B3V6TG77.js.map → chunk-GC3PH6N7.js.map} +1 -1
  45. package/dist/{chunk-FZCP527Y.js → chunk-HXYOSSQO.js} +3 -2
  46. package/dist/{chunk-FZCP527Y.js.map → chunk-HXYOSSQO.js.map} +1 -1
  47. package/dist/{chunk-BRM6FVZA.js → chunk-IEE7WCLN.js} +4 -3
  48. package/dist/{chunk-BRM6FVZA.js.map → chunk-IEE7WCLN.js.map} +1 -1
  49. package/dist/{chunk-DYWBUX5Q.js → chunk-IMSUVYQB.js} +6 -5
  50. package/dist/{chunk-DYWBUX5Q.js.map → chunk-IMSUVYQB.js.map} +1 -1
  51. package/dist/{chunk-CY7WK5FD.js → chunk-IUHHPMAX.js} +203 -46
  52. package/dist/chunk-IUHHPMAX.js.map +1 -0
  53. package/dist/{chunk-SGKD2OCN.js → chunk-IYQCWGU5.js} +6 -5
  54. package/dist/{chunk-SGKD2OCN.js.map → chunk-IYQCWGU5.js.map} +1 -1
  55. package/dist/{chunk-J2QWVSQY.js → chunk-JH7V627O.js} +8 -7
  56. package/dist/{chunk-J2QWVSQY.js.map → chunk-JH7V627O.js.map} +1 -1
  57. package/dist/{chunk-EESD3PY3.js → chunk-JHTIMBOL.js} +6 -5
  58. package/dist/{chunk-EESD3PY3.js.map → chunk-JHTIMBOL.js.map} +1 -1
  59. package/dist/{chunk-LNIRAOA3.js → chunk-L2J3PU7A.js} +7 -6
  60. package/dist/{chunk-LNIRAOA3.js.map → chunk-L2J3PU7A.js.map} +1 -1
  61. package/dist/{chunk-ZYRGEKWS.js → chunk-LJUMYUBO.js} +3 -1
  62. package/dist/{chunk-ZYRGEKWS.js.map → chunk-LJUMYUBO.js.map} +1 -1
  63. package/dist/{chunk-4RVYOY4O.js → chunk-LSW2OJVV.js} +4 -3
  64. package/dist/{chunk-4RVYOY4O.js.map → chunk-LSW2OJVV.js.map} +1 -1
  65. package/dist/{chunk-UTRTG6LH.js → chunk-LVS3MH5T.js} +3 -1
  66. package/dist/{chunk-UTRTG6LH.js.map → chunk-LVS3MH5T.js.map} +1 -1
  67. package/dist/chunk-M357MHH3.js +2 -0
  68. package/dist/{chunk-GYVRFZDY.js → chunk-MDIXFKA3.js} +9 -8
  69. package/dist/{chunk-GYVRFZDY.js.map → chunk-MDIXFKA3.js.map} +1 -1
  70. package/dist/{chunk-WRHIZYZF.js → chunk-MEQM4LI3.js} +6 -5
  71. package/dist/{chunk-WRHIZYZF.js.map → chunk-MEQM4LI3.js.map} +1 -1
  72. package/dist/chunk-NX4ADH73.js +14 -0
  73. package/dist/chunk-NX4ADH73.js.map +1 -0
  74. package/dist/{chunk-XCHWDJ22.js → chunk-OGTTUYNT.js} +3 -1
  75. package/dist/{chunk-XCHWDJ22.js.map → chunk-OGTTUYNT.js.map} +1 -1
  76. package/dist/{chunk-GIMRELBW.js → chunk-OYQQJL3J.js} +3 -1
  77. package/dist/{chunk-GIMRELBW.js.map → chunk-OYQQJL3J.js.map} +1 -1
  78. package/dist/{chunk-RSI7XYXL.js → chunk-PBCZL4JP.js} +6 -5
  79. package/dist/{chunk-RSI7XYXL.js.map → chunk-PBCZL4JP.js.map} +1 -1
  80. package/dist/{chunk-I7IWRQLN.js → chunk-PHBFJCEU.js} +17 -11
  81. package/dist/chunk-PHBFJCEU.js.map +1 -0
  82. package/dist/{chunk-3CTRFMAK.js → chunk-PKCAYHDC.js} +5 -4
  83. package/dist/{chunk-3CTRFMAK.js.map → chunk-PKCAYHDC.js.map} +1 -1
  84. package/dist/{chunk-K3HQAKQX.js → chunk-PMV53IMM.js} +3 -1
  85. package/dist/{chunk-K3HQAKQX.js.map → chunk-PMV53IMM.js.map} +1 -1
  86. package/dist/{chunk-YQEAA6LT.js → chunk-QHUE7E4N.js} +3 -2
  87. package/dist/{chunk-YQEAA6LT.js.map → chunk-QHUE7E4N.js.map} +1 -1
  88. package/dist/{chunk-RC6BZ44Q.js → chunk-QMMCJGFI.js} +4 -3
  89. package/dist/{chunk-RC6BZ44Q.js.map → chunk-QMMCJGFI.js.map} +1 -1
  90. package/dist/{chunk-YV4L3ILF.js → chunk-QV3DJVRZ.js} +3 -1
  91. package/dist/{chunk-YV4L3ILF.js.map → chunk-QV3DJVRZ.js.map} +1 -1
  92. package/dist/{chunk-447JLCBU.js → chunk-S2QH6EZ5.js} +3 -1
  93. package/dist/{chunk-447JLCBU.js.map → chunk-S2QH6EZ5.js.map} +1 -1
  94. package/dist/{chunk-PNY3JKBJ.js → chunk-SFH43JT2.js} +5 -4
  95. package/dist/{chunk-PNY3JKBJ.js.map → chunk-SFH43JT2.js.map} +1 -1
  96. package/dist/{chunk-ERGYJIBB.js → chunk-T46QZH3E.js} +4 -2
  97. package/dist/{chunk-ERGYJIBB.js.map → chunk-T46QZH3E.js.map} +1 -1
  98. package/dist/{chunk-5J4FOW6Q.js → chunk-TNEZNMSN.js} +3 -1
  99. package/dist/{chunk-5J4FOW6Q.js.map → chunk-TNEZNMSN.js.map} +1 -1
  100. package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
  101. package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
  102. package/dist/{chunk-742CZ3L3.js → chunk-UDPH5SLV.js} +19 -202
  103. package/dist/chunk-UDPH5SLV.js.map +1 -0
  104. package/dist/{chunk-HDJWDK7W.js → chunk-VCSERYXH.js} +10 -9
  105. package/dist/{chunk-HDJWDK7W.js.map → chunk-VCSERYXH.js.map} +1 -1
  106. package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
  107. package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
  108. package/dist/{chunk-5F4WR7X7.js → chunk-XS67YG5R.js} +3 -1
  109. package/dist/{chunk-5F4WR7X7.js.map → chunk-XS67YG5R.js.map} +1 -1
  110. package/dist/{chunk-TKOTKRWY.js → chunk-XTD7TZHW.js} +3 -1
  111. package/dist/{chunk-TKOTKRWY.js.map → chunk-XTD7TZHW.js.map} +1 -1
  112. package/dist/{chunk-A2PI6UMY.js → chunk-YSRBFJMQ.js} +3 -1
  113. package/dist/{chunk-A2PI6UMY.js.map → chunk-YSRBFJMQ.js.map} +1 -1
  114. package/dist/chunk-YVFRBXST.js +1420 -0
  115. package/dist/chunk-YVFRBXST.js.map +1 -0
  116. package/dist/{chunk-SCOTPE2I.js → chunk-YYLBXXTE.js} +3 -1
  117. package/dist/{chunk-SCOTPE2I.js.map → chunk-YYLBXXTE.js.map} +1 -1
  118. package/dist/{chunk-RHREZ7BO.js → chunk-ZUWF4NF7.js} +3 -1
  119. package/dist/{chunk-RHREZ7BO.js.map → chunk-ZUWF4NF7.js.map} +1 -1
  120. package/dist/clipboard.js +3 -2
  121. package/dist/components/composer-transcription-control.d.ts +7 -0
  122. package/dist/components/open-geni-chat.d.ts +6 -2
  123. package/dist/components/session-conversation.d.ts +31 -19
  124. package/dist/components/session-list.d.ts +4 -1
  125. package/dist/components/tool-action-review.d.ts +29 -2
  126. package/dist/components/tool-review-history.d.ts +8 -4
  127. package/dist/composer-voice-events.d.ts +8 -0
  128. package/dist/composer.js +15 -13
  129. package/dist/computer-viewer-46UIN7F3.js +20 -0
  130. package/dist/connect.js +15 -12
  131. package/dist/connect.js.map +1 -1
  132. package/dist/desktop.js +6 -5
  133. package/dist/desktop.js.map +1 -1
  134. package/dist/diffs.js +2 -1
  135. package/dist/diffs.js.map +1 -1
  136. package/dist/editor.js +4 -3
  137. package/dist/editor.js.map +1 -1
  138. package/dist/embedded-voice-NIL4RDX3.js +69 -0
  139. package/dist/embedded-voice-NIL4RDX3.js.map +1 -0
  140. package/dist/{fleet-decision-row-YZ3ZKWMM.js → fleet-decision-row-SI63O5V4.js} +3 -1
  141. package/dist/{fleet-decision-row-YZ3ZKWMM.js.map → fleet-decision-row-SI63O5V4.js.map} +1 -1
  142. package/dist/hooks/use-client-config-flags.d.ts +43 -0
  143. package/dist/hooks/use-realtime-voice-models.d.ts +9 -0
  144. package/dist/hooks/use-voice-input.d.ts +8 -1
  145. package/dist/index.d.ts +3 -3
  146. package/dist/index.js +60 -48
  147. package/dist/index.js.map +1 -1
  148. package/dist/interaction.js +14 -13
  149. package/dist/interaction.js.map +1 -1
  150. package/dist/{latest-question-YTOBHCWR.js → latest-question-2POFPYNB.js} +5 -4
  151. package/dist/{latest-question-YTOBHCWR.js.map → latest-question-2POFPYNB.js.map} +1 -1
  152. package/dist/machines.js +8 -7
  153. package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-DQ4DRNVT.js} +3 -1
  154. package/dist/{markdown-table-layout-QXSLK4BY.js.map → markdown-table-layout-DQ4DRNVT.js.map} +1 -1
  155. package/dist/model-policy-picker-menu-WRNSRFQC.js +15 -0
  156. package/dist/model-policy.js +2 -1
  157. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  158. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  159. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  160. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  161. package/dist/{platform-activity-row-627BFZ46.js → platform-activity-row-PJJVATTZ.js} +3 -1
  162. package/dist/{platform-activity-row-627BFZ46.js.map → platform-activity-row-PJJVATTZ.js.map} +1 -1
  163. package/dist/{queue-surface-implementation-ZHFI5JBP.js → queue-surface-implementation-67FS7FUI.js} +10 -9
  164. package/dist/{queue-surface-implementation-ZHFI5JBP.js.map → queue-surface-implementation-67FS7FUI.js.map} +1 -1
  165. package/dist/realtime/embedded-voice.d.ts +20 -0
  166. package/dist/realtime/realtime-control.d.ts +2 -0
  167. package/dist/realtime.js +22 -1332
  168. package/dist/realtime.js.map +1 -1
  169. package/dist/sandbox-files-34CJJNOY.js +21 -0
  170. package/dist/session-capability-policy.d.ts +20 -1
  171. package/dist/session-ui.js +32 -30
  172. package/dist/session-ui.js.map +1 -1
  173. package/dist/session.js +12 -11
  174. package/dist/sites.js +2 -1
  175. package/dist/terminal.js +6 -5
  176. package/dist/terminal.js.map +1 -1
  177. package/dist/timeline/index.d.ts +2 -0
  178. package/dist/timeline/retained-loaders.d.ts +27 -0
  179. package/dist/{timeline-annotation-markers-GNDHTGBD.js → timeline-annotation-markers-U3G66KUD.js} +5 -4
  180. package/dist/{timeline-annotation-markers-GNDHTGBD.js.map → timeline-annotation-markers-U3G66KUD.js.map} +1 -1
  181. package/dist/{timeline-annotation-selection-DMLMM2TX.js → timeline-annotation-selection-BI7QTPTZ.js} +4 -3
  182. package/dist/{timeline-annotation-selection-DMLMM2TX.js.map → timeline-annotation-selection-BI7QTPTZ.js.map} +1 -1
  183. package/dist/{timeline-annotations-dialog-ELRDBHTS.js → timeline-annotations-dialog-FOMHSDTE.js} +7 -6
  184. package/dist/{timeline-annotations-dialog-ELRDBHTS.js.map → timeline-annotations-dialog-FOMHSDTE.js.map} +1 -1
  185. package/dist/usage.js +6 -5
  186. package/dist/usage.js.map +1 -1
  187. package/dist/voice-recording-owner.d.ts +7 -0
  188. package/package.json +3 -3
  189. package/src/components/approval-surface.tsx +190 -79
  190. package/src/components/artifacts/session-artifact-viewer.tsx +4 -2
  191. package/src/components/composer-transcription-control.tsx +121 -25
  192. package/src/components/composer.tsx +1 -0
  193. package/src/components/markdown.tsx +7 -2
  194. package/src/components/message-timeline.tsx +10 -1
  195. package/src/components/open-geni-chat.tsx +53 -13
  196. package/src/components/session-conversation.tsx +273 -63
  197. package/src/components/session-list.tsx +9 -2
  198. package/src/components/session-proxy-scope.tsx +26 -5
  199. package/src/components/tool-action-review.tsx +502 -239
  200. package/src/components/tool-review-history.tsx +18 -43
  201. package/src/composer-voice-events.ts +12 -0
  202. package/src/connection-discovery.tsx +4 -1
  203. package/src/hooks/use-client-config-flags.ts +110 -0
  204. package/src/hooks/use-goal.ts +12 -1
  205. package/src/hooks/use-realtime-voice-models.ts +67 -0
  206. package/src/hooks/use-session-events.ts +19 -16
  207. package/src/hooks/use-transcription.ts +6 -0
  208. package/src/hooks/use-voice-input.ts +115 -18
  209. package/src/index.ts +9 -0
  210. package/src/realtime/embedded-voice.tsx +81 -0
  211. package/src/realtime/realtime-control.tsx +109 -17
  212. package/src/session-capability-policy.ts +119 -6
  213. package/src/timeline/index.ts +10 -0
  214. package/src/timeline/retained-loaders.ts +86 -0
  215. package/src/timeline/tool-display-name.ts +5 -1
  216. package/src/timeline/tool-renderers.tsx +143 -10
  217. package/src/voice-recording-owner.ts +22 -0
  218. package/styles/compiled.css +135 -37
  219. package/styles/index.css +49 -0
  220. package/styles/responsive.css +61 -4
  221. package/styles/session-capability.css +1 -1
  222. package/styles/tokens.css +1 -1
  223. package/dist/browser-viewer-SHYKQRNX.js +0 -16
  224. package/dist/chat-interactive-block-UGBTNB3L.js +0 -11
  225. package/dist/chunk-742CZ3L3.js.map +0 -1
  226. package/dist/chunk-CY7WK5FD.js.map +0 -1
  227. package/dist/chunk-I7IWRQLN.js.map +0 -1
  228. package/dist/chunk-IWIDV7SB.js +0 -1
  229. package/dist/chunk-PGSX7UCP.js.map +0 -1
  230. package/dist/chunk-TMYGL6X5.js.map +0 -1
  231. package/dist/chunk-WWEUO2PL.js.map +0 -1
  232. package/dist/computer-viewer-6SRGWRUX.js +0 -19
  233. package/dist/model-policy-picker-menu-DXHUL73H.js +0 -14
  234. package/dist/sandbox-files-GORZOUUJ.js +0 -20
  235. /package/dist/{browser-viewer-SHYKQRNX.js.map → browser-viewer-WGJ43YGI.js.map} +0 -0
  236. /package/dist/{chat-interactive-block-UGBTNB3L.js.map → chat-interactive-block-ZFHOKTMS.js.map} +0 -0
  237. /package/dist/{chunk-IWIDV7SB.js.map → chunk-M357MHH3.js.map} +0 -0
  238. /package/dist/{computer-viewer-6SRGWRUX.js.map → computer-viewer-46UIN7F3.js.map} +0 -0
  239. /package/dist/{model-policy-picker-menu-DXHUL73H.js.map → model-policy-picker-menu-WRNSRFQC.js.map} +0 -0
  240. /package/dist/{sandbox-files-GORZOUUJ.js.map → sandbox-files-34CJJNOY.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/timeline-annotation-markers.tsx"],"sourcesContent":["import type { DraftTimelineAnnotation } from \"@opengeni/sdk\";\nimport { useLayoutEffect, useState, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../lib/cn\";\nimport {\n annotationBoxIntersects,\n annotationViewportBox,\n layoutAnnotationBadges,\n type AnnotationBadgeAnchor,\n type AnnotationBox,\n} from \"./timeline-annotation-layout\";\nimport {\n annotationDisplayOrdinal,\n annotationHasNote,\n annotatableText,\n buildQuoteRange,\n cssEscapeAttribute,\n matchingQuoteInSource,\n occurrenceOffsets,\n} from \"./timeline-annotation-shared\";\n\nfunction lastVisibleRect(range: Range): DOMRect | null {\n try {\n const rects = range.getClientRects();\n for (let index = rects.length - 1; index >= 0; index--) {\n const rect = rects.item(index);\n if (rect && (rect.width > 0 || rect.height > 0)) return rect;\n }\n } catch {\n // jsdom ranges may omit client rects.\n }\n try {\n const bounding = range.getBoundingClientRect();\n if (bounding.width > 0 || bounding.height > 0) return bounding;\n } catch {\n // jsdom ranges may omit layout geometry.\n }\n return null;\n}\n\nfunction occurrenceForAnnotation(\n sourceEl: HTMLElement,\n annotation: DraftTimelineAnnotation,\n): number {\n const { text } = annotatableText(sourceEl);\n const quote = matchingQuoteInSource(text, annotation.quote) ?? annotation.quote;\n const offsets = occurrenceOffsets(text, quote);\n if (offsets.length <= 1) return 0;\n let best = 0;\n let bestDistance = Number.POSITIVE_INFINITY;\n offsets.forEach((offset, index) => {\n const distance = Math.abs(offset - annotation.source.startOffset);\n if (distance < bestDistance) {\n best = index;\n bestDistance = distance;\n }\n });\n return best;\n}\n\nfunction timelineScroller(root: HTMLElement): HTMLElement {\n if (root.matches(\"[data-og-timeline-scroller]\")) return root;\n const nested = root.querySelector(\"[data-og-timeline-scroller]\");\n return nested instanceof HTMLElement ? nested : root;\n}\n\nfunction currentAnnotationViewport(root: HTMLElement): AnnotationBox {\n const scroller = timelineScroller(root);\n const rect = scroller.getBoundingClientRect();\n const scrollerBox = {\n left: rect.left,\n right: rect.right,\n top: rect.top,\n bottom: rect.bottom,\n } satisfies AnnotationBox;\n return annotationViewportBox(window.innerWidth || 1024, window.innerHeight || 768, scrollerBox);\n}\n\nfunction sourceInTimeline(root: HTMLElement, eventId: string): HTMLElement | null {\n const source = root.querySelector(\n `[data-og-annotation-source-key=\"${cssEscapeAttribute(eventId)}\"]`,\n );\n return source instanceof HTMLElement ? source : null;\n}\n\nfunction collectMarkers(\n root: HTMLElement | null,\n annotations: readonly DraftTimelineAnnotation[],\n): AnnotationBadgeAnchor[] {\n if (!root) return [];\n const viewport = currentAnnotationViewport(root);\n const next: AnnotationBadgeAnchor[] = [];\n for (const [index, annotation] of annotations.entries()) {\n const source = sourceInTimeline(root, annotation.source.eventId);\n if (!source) continue;\n const quote =\n matchingQuoteInSource(annotatableText(source).text, annotation.quote) ?? annotation.quote;\n const range = buildQuoteRange(source, quote, occurrenceForAnnotation(source, annotation));\n if (!range) continue;\n const rect = lastVisibleRect(range);\n if (!rect) continue;\n if (\n !annotationBoxIntersects(\n { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom },\n viewport,\n )\n ) {\n continue;\n }\n next.push({\n id: annotation.id,\n ordinal: annotationDisplayOrdinal(annotation, index),\n left: rect.right,\n top: rect.top,\n incomplete: !annotationHasNote(annotation.note),\n });\n }\n return layoutAnnotationBadges(next, viewport);\n}\n\nexport function TimelineAnnotationMarkers({\n annotations,\n onSelect,\n rootRef,\n}: {\n annotations: readonly DraftTimelineAnnotation[];\n onSelect?: ((id: string) => void) | undefined;\n rootRef: RefObject<HTMLElement | null>;\n}) {\n const [markers, setMarkers] = useState<AnnotationBadgeAnchor[]>([]);\n\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root || annotations.length === 0) {\n setMarkers([]);\n return;\n }\n const update = () => setMarkers(collectMarkers(root, annotations));\n update();\n window.addEventListener(\"resize\", update);\n window.addEventListener(\"scroll\", update, true);\n const observer = typeof ResizeObserver === \"function\" ? new ResizeObserver(update) : null;\n observer?.observe(root);\n for (const annotation of annotations) {\n const source = sourceInTimeline(root, annotation.source.eventId);\n if (source) observer?.observe(source);\n }\n return () => {\n window.removeEventListener(\"resize\", update);\n window.removeEventListener(\"scroll\", update, true);\n observer?.disconnect();\n };\n }, [annotations, rootRef]);\n\n if (markers.length === 0 || typeof document === \"undefined\") return null;\n return createPortal(\n <div className=\"og-root pointer-events-none fixed inset-0 z-[35]\">\n {markers.map((marker) => (\n <button\n key={marker.id}\n type=\"button\"\n data-og-annotation-badge=\"\"\n data-og-annotation-badge-ordinal={marker.ordinal}\n style={{ left: marker.left, top: marker.top, zIndex: marker.ordinal }}\n className={cn(\n \"pointer-events-auto absolute flex h-4 min-w-4 -translate-x-1/2 -translate-y-[110%] items-center justify-center rounded-full bg-og-accent px-0.5 text-[10px] font-semibold tabular-nums text-og-accent-fg shadow-sm outline-hidden select-none after:absolute after:-inset-2 after:content-[''] focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:after:-inset-2.5\",\n marker.incomplete && \"ring-1 ring-og-accent-fg/35\",\n )}\n aria-label={`Annotation ${marker.ordinal}`}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onSelect?.(marker.id);\n }}\n >\n {marker.ordinal}\n </button>\n ))}\n </div>,\n document.body,\n );\n}\n\nexport default TimelineAnnotationMarkers;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/components/timeline-annotation-markers.tsx"],"sourcesContent":["import type { DraftTimelineAnnotation } from \"@opengeni/sdk\";\nimport { useLayoutEffect, useState, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../lib/cn\";\nimport {\n annotationBoxIntersects,\n annotationViewportBox,\n layoutAnnotationBadges,\n type AnnotationBadgeAnchor,\n type AnnotationBox,\n} from \"./timeline-annotation-layout\";\nimport {\n annotationDisplayOrdinal,\n annotationHasNote,\n annotatableText,\n buildQuoteRange,\n cssEscapeAttribute,\n matchingQuoteInSource,\n occurrenceOffsets,\n} from \"./timeline-annotation-shared\";\n\nfunction lastVisibleRect(range: Range): DOMRect | null {\n try {\n const rects = range.getClientRects();\n for (let index = rects.length - 1; index >= 0; index--) {\n const rect = rects.item(index);\n if (rect && (rect.width > 0 || rect.height > 0)) return rect;\n }\n } catch {\n // jsdom ranges may omit client rects.\n }\n try {\n const bounding = range.getBoundingClientRect();\n if (bounding.width > 0 || bounding.height > 0) return bounding;\n } catch {\n // jsdom ranges may omit layout geometry.\n }\n return null;\n}\n\nfunction occurrenceForAnnotation(\n sourceEl: HTMLElement,\n annotation: DraftTimelineAnnotation,\n): number {\n const { text } = annotatableText(sourceEl);\n const quote = matchingQuoteInSource(text, annotation.quote) ?? annotation.quote;\n const offsets = occurrenceOffsets(text, quote);\n if (offsets.length <= 1) return 0;\n let best = 0;\n let bestDistance = Number.POSITIVE_INFINITY;\n offsets.forEach((offset, index) => {\n const distance = Math.abs(offset - annotation.source.startOffset);\n if (distance < bestDistance) {\n best = index;\n bestDistance = distance;\n }\n });\n return best;\n}\n\nfunction timelineScroller(root: HTMLElement): HTMLElement {\n if (root.matches(\"[data-og-timeline-scroller]\")) return root;\n const nested = root.querySelector(\"[data-og-timeline-scroller]\");\n return nested instanceof HTMLElement ? nested : root;\n}\n\nfunction currentAnnotationViewport(root: HTMLElement): AnnotationBox {\n const scroller = timelineScroller(root);\n const rect = scroller.getBoundingClientRect();\n const scrollerBox = {\n left: rect.left,\n right: rect.right,\n top: rect.top,\n bottom: rect.bottom,\n } satisfies AnnotationBox;\n return annotationViewportBox(window.innerWidth || 1024, window.innerHeight || 768, scrollerBox);\n}\n\nfunction sourceInTimeline(root: HTMLElement, eventId: string): HTMLElement | null {\n const source = root.querySelector(\n `[data-og-annotation-source-key=\"${cssEscapeAttribute(eventId)}\"]`,\n );\n return source instanceof HTMLElement ? source : null;\n}\n\nfunction collectMarkers(\n root: HTMLElement | null,\n annotations: readonly DraftTimelineAnnotation[],\n): AnnotationBadgeAnchor[] {\n if (!root) return [];\n const viewport = currentAnnotationViewport(root);\n const next: AnnotationBadgeAnchor[] = [];\n for (const [index, annotation] of annotations.entries()) {\n const source = sourceInTimeline(root, annotation.source.eventId);\n if (!source) continue;\n const quote =\n matchingQuoteInSource(annotatableText(source).text, annotation.quote) ?? annotation.quote;\n const range = buildQuoteRange(source, quote, occurrenceForAnnotation(source, annotation));\n if (!range) continue;\n const rect = lastVisibleRect(range);\n if (!rect) continue;\n if (\n !annotationBoxIntersects(\n { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom },\n viewport,\n )\n ) {\n continue;\n }\n next.push({\n id: annotation.id,\n ordinal: annotationDisplayOrdinal(annotation, index),\n left: rect.right,\n top: rect.top,\n incomplete: !annotationHasNote(annotation.note),\n });\n }\n return layoutAnnotationBadges(next, viewport);\n}\n\nexport function TimelineAnnotationMarkers({\n annotations,\n onSelect,\n rootRef,\n}: {\n annotations: readonly DraftTimelineAnnotation[];\n onSelect?: ((id: string) => void) | undefined;\n rootRef: RefObject<HTMLElement | null>;\n}) {\n const [markers, setMarkers] = useState<AnnotationBadgeAnchor[]>([]);\n\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root || annotations.length === 0) {\n setMarkers([]);\n return;\n }\n const update = () => setMarkers(collectMarkers(root, annotations));\n update();\n window.addEventListener(\"resize\", update);\n window.addEventListener(\"scroll\", update, true);\n const observer = typeof ResizeObserver === \"function\" ? new ResizeObserver(update) : null;\n observer?.observe(root);\n for (const annotation of annotations) {\n const source = sourceInTimeline(root, annotation.source.eventId);\n if (source) observer?.observe(source);\n }\n return () => {\n window.removeEventListener(\"resize\", update);\n window.removeEventListener(\"scroll\", update, true);\n observer?.disconnect();\n };\n }, [annotations, rootRef]);\n\n if (markers.length === 0 || typeof document === \"undefined\") return null;\n return createPortal(\n <div className=\"og-root pointer-events-none fixed inset-0 z-[35]\">\n {markers.map((marker) => (\n <button\n key={marker.id}\n type=\"button\"\n data-og-annotation-badge=\"\"\n data-og-annotation-badge-ordinal={marker.ordinal}\n style={{ left: marker.left, top: marker.top, zIndex: marker.ordinal }}\n className={cn(\n \"pointer-events-auto absolute flex h-4 min-w-4 -translate-x-1/2 -translate-y-[110%] items-center justify-center rounded-full bg-og-accent px-0.5 text-[10px] font-semibold tabular-nums text-og-accent-fg shadow-sm outline-hidden select-none after:absolute after:-inset-2 after:content-[''] focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:after:-inset-2.5\",\n marker.incomplete && \"ring-1 ring-og-accent-fg/35\",\n )}\n aria-label={`Annotation ${marker.ordinal}`}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onSelect?.(marker.id);\n }}\n >\n {marker.ordinal}\n </button>\n ))}\n </div>,\n document.body,\n );\n}\n\nexport default TimelineAnnotationMarkers;\n"],"mappings":"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","names":[]}
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import {
2
3
  clipRangeToSource,
3
4
  matchingQuoteInSource,
@@ -5,10 +6,10 @@ import {
5
6
  quoteOccurrenceIndex,
6
7
  truncateAnnotationQuote,
7
8
  visibleClientRect
8
- } from "./chunk-447JLCBU.js";
9
+ } from "./chunk-S2QH6EZ5.js";
9
10
  import {
10
11
  usePortalTokenStyle
11
- } from "./chunk-VZTQ73XT.js";
12
+ } from "./chunk-XEA5BCMA.js";
12
13
 
13
14
  // src/components/timeline-annotation-selection.tsx
14
15
  import { QuoteIcon } from "lucide-react";
@@ -238,4 +239,4 @@ export {
238
239
  TimelineAnnotationSelection,
239
240
  timeline_annotation_selection_default as default
240
241
  };
241
- //# sourceMappingURL=timeline-annotation-selection-DMLMM2TX.js.map
242
+ //# sourceMappingURL=timeline-annotation-selection-BI7QTPTZ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/timeline-annotation-selection.tsx"],"sourcesContent":["import type { DraftTimelineAnnotation } from \"@opengeni/sdk\";\nimport { QuoteIcon } from \"lucide-react\";\nimport { useEffect, useRef, useState, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport type { TimelineAnnotationSourceDescriptor } from \"../timeline\";\nimport {\n clipRangeToSource,\n matchingQuoteInSource,\n occurrenceOffsets,\n quoteOccurrenceIndex,\n truncateAnnotationQuote,\n visibleClientRect,\n} from \"./timeline-annotation-shared\";\n\ntype SelectionCandidate = {\n annotation: DraftTimelineAnnotation;\n left: number;\n top: number;\n keyboard: boolean;\n};\n\nconst CONTROL_SELECTOR = \"button,input,textarea,select,summary,[contenteditable='true']\";\nconst SOURCE_CONTEXT_BYTES = 160;\nconst MAX_QUOTE_BYTES = 16 * 1024;\nconst POPOVER_WIDTH = 280;\nconst POPOVER_HEIGHT = 64;\n\nfunction utf8Prefix(value: string, maxBytes: number): string {\n let output = \"\";\n let bytes = 0;\n for (const character of value) {\n const next = new TextEncoder().encode(character).byteLength;\n if (bytes + next > maxBytes) break;\n output += character;\n bytes += next;\n }\n return output;\n}\n\nfunction utf8Suffix(value: string, maxBytes: number): string {\n let output = \"\";\n let bytes = 0;\n for (const character of [...value].reverse()) {\n const next = new TextEncoder().encode(character).byteLength;\n if (bytes + next > maxBytes) break;\n output = character + output;\n bytes += next;\n }\n return output;\n}\n\nfunction selectionElement(node: Node | null): Element | null {\n return node instanceof Element ? node : (node?.parentElement ?? null);\n}\n\nfunction containingSource(node: Node | null): HTMLElement | null {\n const element = selectionElement(node);\n const source = element?.closest(\"[data-og-annotation-source-key]\");\n return source instanceof HTMLElement ? source : null;\n}\n\nfunction resolveSourceBoundary(range: Range, root: HTMLElement): HTMLElement | null {\n const start = containingSource(range.startContainer);\n const end = containingSource(range.endContainer);\n if (start && end && start !== end) return null;\n const boundary = start ?? end;\n if (!boundary || !root.contains(boundary)) return null;\n return boundary;\n}\n\nfunction isWhollyInsideControl(range: Range, boundary: HTMLElement): boolean {\n const start = selectionElement(range.startContainer)?.closest(CONTROL_SELECTOR);\n const end = selectionElement(range.endContainer)?.closest(CONTROL_SELECTOR);\n return (\n start instanceof Element && start === end && boundary.contains(start) && start !== boundary\n );\n}\n\nfunction annotationId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `annotation-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction popoverPosition(rect: DOMRect): { left: number; top: number } {\n const half = POPOVER_WIDTH / 2;\n const left = clamp(rect.left + rect.width / 2, 12 + half, window.innerWidth - 12 - half);\n const preferBelow = rect.bottom + 8 + POPOVER_HEIGHT <= window.innerHeight - 12;\n const top = preferBelow ? rect.bottom + 8 : Math.max(12, rect.top - POPOVER_HEIGHT - 8);\n return { left, top };\n}\n\nfunction buildCandidate(\n root: HTMLElement,\n sources: ReadonlyMap<string, TimelineAnnotationSourceDescriptor>,\n keyboard: boolean,\n): SelectionCandidate | null {\n const selection = document.getSelection();\n if (!selection || selection.rangeCount !== 1 || selection.isCollapsed) return null;\n const range = selection.getRangeAt(0);\n const startBoundary = resolveSourceBoundary(range, root);\n if (!startBoundary) return null;\n if (isWhollyInsideControl(range, startBoundary)) return null;\n const clipped = clipRangeToSource(range, startBoundary);\n if (!clipped) return null;\n const sourceKey = startBoundary.dataset.ogAnnotationSourceKey;\n const source = sourceKey ? sources.get(sourceKey) : undefined;\n const rawQuote = clipped.toString();\n const quote = (source ? matchingQuoteInSource(source.text, rawQuote) : null) ?? rawQuote;\n if (\n !source ||\n quote.trim().length === 0 ||\n new TextEncoder().encode(quote).byteLength > MAX_QUOTE_BYTES\n ) {\n return null;\n }\n const sourceOffsets = occurrenceOffsets(source.text, quote);\n if (sourceOffsets.length === 0) return null;\n const occurrence = quoteOccurrenceIndex(startBoundary, clipped, quote);\n const startOffset = sourceOffsets[Math.min(occurrence, sourceOffsets.length - 1)]!;\n const endOffset = startOffset + quote.length;\n const rect = visibleClientRect(range) ?? visibleClientRect(clipped);\n if (!rect) return null;\n const position = popoverPosition(rect);\n return {\n annotation: {\n id: annotationId(),\n quote,\n note: \"\",\n source: {\n kind: source.kind,\n eventId: source.eventId,\n eventType: source.eventType,\n sequence: source.sequence,\n turnId: source.turnId,\n startOffset,\n endOffset,\n contextBefore: utf8Suffix(source.text.slice(0, startOffset), SOURCE_CONTEXT_BYTES),\n contextAfter: utf8Prefix(source.text.slice(endOffset), SOURCE_CONTEXT_BYTES),\n ...(source.label ? { label: source.label } : {}),\n },\n },\n left: position.left,\n top: position.top,\n keyboard,\n };\n}\n\nexport function TimelineAnnotationSelection({\n rootRef,\n sources,\n onAnnotate,\n}: {\n rootRef: RefObject<HTMLElement | null>;\n sources: ReadonlyMap<string, TimelineAnnotationSourceDescriptor>;\n onAnnotate?: ((annotation: DraftTimelineAnnotation) => void) | undefined;\n}) {\n const [candidate, setCandidate] = useState<SelectionCandidate | null>(null);\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const initialSelectionObservedRef = useRef(false);\n const pointerOriginRef = useRef<\"unset\" | \"inside\" | \"outside\">(\"unset\");\n const portalStyle = usePortalTokenStyle(rootRef.current);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root || !onAnnotate) {\n setCandidate(null);\n return;\n }\n const update = (keyboard: boolean) => {\n setCandidate(buildCandidate(root, sources, keyboard));\n };\n // A user may finish selecting text before this lazy component arrives.\n // Reconcile once; later prop changes must not revive dismissed selections.\n if (!initialSelectionObservedRef.current) {\n initialSelectionObservedRef.current = true;\n update(false);\n }\n const onRootPointerUp = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;\n // Tests may fire only pointerup on the source. A real click that started\n // outside the timeline must not resurrect leftover selection.\n if (pointerOriginRef.current === \"outside\") return;\n window.setTimeout(() => update(false), 0);\n };\n const onDocumentPointerUp = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;\n const startedInRoot = pointerOriginRef.current === \"inside\";\n pointerOriginRef.current = \"unset\";\n if (!startedInRoot) return;\n window.setTimeout(() => update(false), 0);\n };\n const onKeyUp = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setCandidate(null);\n return;\n }\n window.setTimeout(() => update(true), 0);\n };\n const onPointerDown = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) {\n event.preventDefault();\n return;\n }\n pointerOriginRef.current =\n event.target instanceof Node && root.contains(event.target) ? \"inside\" : \"outside\";\n setCandidate(null);\n };\n const onDocumentKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setCandidate(null);\n };\n const onSelectionChange = () => {\n if (buttonRef.current && document.activeElement === buttonRef.current) return;\n const selection = document.getSelection();\n if (!selection || selection.isCollapsed) setCandidate(null);\n };\n root.addEventListener(\"pointerup\", onRootPointerUp);\n root.addEventListener(\"keyup\", onKeyUp);\n document.addEventListener(\"pointerup\", onDocumentPointerUp);\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onDocumentKeyDown);\n document.addEventListener(\"selectionchange\", onSelectionChange);\n return () => {\n root.removeEventListener(\"pointerup\", onRootPointerUp);\n root.removeEventListener(\"keyup\", onKeyUp);\n document.removeEventListener(\"pointerup\", onDocumentPointerUp);\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onDocumentKeyDown);\n document.removeEventListener(\"selectionchange\", onSelectionChange);\n };\n }, [onAnnotate, rootRef, sources]);\n\n useEffect(() => {\n if (candidate?.keyboard) buttonRef.current?.focus();\n }, [candidate]);\n\n if (!candidate || !onAnnotate || typeof document === \"undefined\") return null;\n const preview = truncateAnnotationQuote(candidate.annotation.quote, 72);\n return createPortal(\n <button\n ref={buttonRef}\n type=\"button\"\n data-og-annotation-popover=\"\"\n style={{ left: candidate.left, top: candidate.top, ...portalStyle }}\n className=\"og-root fixed z-[80] flex max-w-[min(18rem,calc(100vw-1.5rem))] -translate-x-1/2 items-start gap-2 rounded-2xl border border-og-border bg-og-surface-1 px-3 py-2 text-og-sm font-medium text-og-fg shadow-xl outline-hidden transition hover:border-og-accent/40 hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-[44px]\"\n aria-label={`Add a note about “${preview}”`}\n title={candidate.annotation.quote}\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => {\n onAnnotate(candidate.annotation);\n document.getSelection()?.removeAllRanges();\n setCandidate(null);\n }}\n >\n <QuoteIcon className=\"mt-0.5 size-3.5 shrink-0 text-og-accent\" aria-hidden=\"true\" />\n <span className=\"min-w-0 text-left\">\n <span className=\"block leading-5\">Add note</span>\n <span className=\"mt-0.5 block truncate text-og-xs font-normal text-og-fg-muted\">\n {preview}\n </span>\n </span>\n </button>,\n document.body,\n );\n}\n\nexport default TimelineAnnotationSelection;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/components/timeline-annotation-selection.tsx"],"sourcesContent":["import type { DraftTimelineAnnotation } from \"@opengeni/sdk\";\nimport { QuoteIcon } from \"lucide-react\";\nimport { useEffect, useRef, useState, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport type { TimelineAnnotationSourceDescriptor } from \"../timeline\";\nimport {\n clipRangeToSource,\n matchingQuoteInSource,\n occurrenceOffsets,\n quoteOccurrenceIndex,\n truncateAnnotationQuote,\n visibleClientRect,\n} from \"./timeline-annotation-shared\";\n\ntype SelectionCandidate = {\n annotation: DraftTimelineAnnotation;\n left: number;\n top: number;\n keyboard: boolean;\n};\n\nconst CONTROL_SELECTOR = \"button,input,textarea,select,summary,[contenteditable='true']\";\nconst SOURCE_CONTEXT_BYTES = 160;\nconst MAX_QUOTE_BYTES = 16 * 1024;\nconst POPOVER_WIDTH = 280;\nconst POPOVER_HEIGHT = 64;\n\nfunction utf8Prefix(value: string, maxBytes: number): string {\n let output = \"\";\n let bytes = 0;\n for (const character of value) {\n const next = new TextEncoder().encode(character).byteLength;\n if (bytes + next > maxBytes) break;\n output += character;\n bytes += next;\n }\n return output;\n}\n\nfunction utf8Suffix(value: string, maxBytes: number): string {\n let output = \"\";\n let bytes = 0;\n for (const character of [...value].reverse()) {\n const next = new TextEncoder().encode(character).byteLength;\n if (bytes + next > maxBytes) break;\n output = character + output;\n bytes += next;\n }\n return output;\n}\n\nfunction selectionElement(node: Node | null): Element | null {\n return node instanceof Element ? node : (node?.parentElement ?? null);\n}\n\nfunction containingSource(node: Node | null): HTMLElement | null {\n const element = selectionElement(node);\n const source = element?.closest(\"[data-og-annotation-source-key]\");\n return source instanceof HTMLElement ? source : null;\n}\n\nfunction resolveSourceBoundary(range: Range, root: HTMLElement): HTMLElement | null {\n const start = containingSource(range.startContainer);\n const end = containingSource(range.endContainer);\n if (start && end && start !== end) return null;\n const boundary = start ?? end;\n if (!boundary || !root.contains(boundary)) return null;\n return boundary;\n}\n\nfunction isWhollyInsideControl(range: Range, boundary: HTMLElement): boolean {\n const start = selectionElement(range.startContainer)?.closest(CONTROL_SELECTOR);\n const end = selectionElement(range.endContainer)?.closest(CONTROL_SELECTOR);\n return (\n start instanceof Element && start === end && boundary.contains(start) && start !== boundary\n );\n}\n\nfunction annotationId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `annotation-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction popoverPosition(rect: DOMRect): { left: number; top: number } {\n const half = POPOVER_WIDTH / 2;\n const left = clamp(rect.left + rect.width / 2, 12 + half, window.innerWidth - 12 - half);\n const preferBelow = rect.bottom + 8 + POPOVER_HEIGHT <= window.innerHeight - 12;\n const top = preferBelow ? rect.bottom + 8 : Math.max(12, rect.top - POPOVER_HEIGHT - 8);\n return { left, top };\n}\n\nfunction buildCandidate(\n root: HTMLElement,\n sources: ReadonlyMap<string, TimelineAnnotationSourceDescriptor>,\n keyboard: boolean,\n): SelectionCandidate | null {\n const selection = document.getSelection();\n if (!selection || selection.rangeCount !== 1 || selection.isCollapsed) return null;\n const range = selection.getRangeAt(0);\n const startBoundary = resolveSourceBoundary(range, root);\n if (!startBoundary) return null;\n if (isWhollyInsideControl(range, startBoundary)) return null;\n const clipped = clipRangeToSource(range, startBoundary);\n if (!clipped) return null;\n const sourceKey = startBoundary.dataset.ogAnnotationSourceKey;\n const source = sourceKey ? sources.get(sourceKey) : undefined;\n const rawQuote = clipped.toString();\n const quote = (source ? matchingQuoteInSource(source.text, rawQuote) : null) ?? rawQuote;\n if (\n !source ||\n quote.trim().length === 0 ||\n new TextEncoder().encode(quote).byteLength > MAX_QUOTE_BYTES\n ) {\n return null;\n }\n const sourceOffsets = occurrenceOffsets(source.text, quote);\n if (sourceOffsets.length === 0) return null;\n const occurrence = quoteOccurrenceIndex(startBoundary, clipped, quote);\n const startOffset = sourceOffsets[Math.min(occurrence, sourceOffsets.length - 1)]!;\n const endOffset = startOffset + quote.length;\n const rect = visibleClientRect(range) ?? visibleClientRect(clipped);\n if (!rect) return null;\n const position = popoverPosition(rect);\n return {\n annotation: {\n id: annotationId(),\n quote,\n note: \"\",\n source: {\n kind: source.kind,\n eventId: source.eventId,\n eventType: source.eventType,\n sequence: source.sequence,\n turnId: source.turnId,\n startOffset,\n endOffset,\n contextBefore: utf8Suffix(source.text.slice(0, startOffset), SOURCE_CONTEXT_BYTES),\n contextAfter: utf8Prefix(source.text.slice(endOffset), SOURCE_CONTEXT_BYTES),\n ...(source.label ? { label: source.label } : {}),\n },\n },\n left: position.left,\n top: position.top,\n keyboard,\n };\n}\n\nexport function TimelineAnnotationSelection({\n rootRef,\n sources,\n onAnnotate,\n}: {\n rootRef: RefObject<HTMLElement | null>;\n sources: ReadonlyMap<string, TimelineAnnotationSourceDescriptor>;\n onAnnotate?: ((annotation: DraftTimelineAnnotation) => void) | undefined;\n}) {\n const [candidate, setCandidate] = useState<SelectionCandidate | null>(null);\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const initialSelectionObservedRef = useRef(false);\n const pointerOriginRef = useRef<\"unset\" | \"inside\" | \"outside\">(\"unset\");\n const portalStyle = usePortalTokenStyle(rootRef.current);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root || !onAnnotate) {\n setCandidate(null);\n return;\n }\n const update = (keyboard: boolean) => {\n setCandidate(buildCandidate(root, sources, keyboard));\n };\n // A user may finish selecting text before this lazy component arrives.\n // Reconcile once; later prop changes must not revive dismissed selections.\n if (!initialSelectionObservedRef.current) {\n initialSelectionObservedRef.current = true;\n update(false);\n }\n const onRootPointerUp = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;\n // Tests may fire only pointerup on the source. A real click that started\n // outside the timeline must not resurrect leftover selection.\n if (pointerOriginRef.current === \"outside\") return;\n window.setTimeout(() => update(false), 0);\n };\n const onDocumentPointerUp = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;\n const startedInRoot = pointerOriginRef.current === \"inside\";\n pointerOriginRef.current = \"unset\";\n if (!startedInRoot) return;\n window.setTimeout(() => update(false), 0);\n };\n const onKeyUp = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setCandidate(null);\n return;\n }\n window.setTimeout(() => update(true), 0);\n };\n const onPointerDown = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) {\n event.preventDefault();\n return;\n }\n pointerOriginRef.current =\n event.target instanceof Node && root.contains(event.target) ? \"inside\" : \"outside\";\n setCandidate(null);\n };\n const onDocumentKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setCandidate(null);\n };\n const onSelectionChange = () => {\n if (buttonRef.current && document.activeElement === buttonRef.current) return;\n const selection = document.getSelection();\n if (!selection || selection.isCollapsed) setCandidate(null);\n };\n root.addEventListener(\"pointerup\", onRootPointerUp);\n root.addEventListener(\"keyup\", onKeyUp);\n document.addEventListener(\"pointerup\", onDocumentPointerUp);\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onDocumentKeyDown);\n document.addEventListener(\"selectionchange\", onSelectionChange);\n return () => {\n root.removeEventListener(\"pointerup\", onRootPointerUp);\n root.removeEventListener(\"keyup\", onKeyUp);\n document.removeEventListener(\"pointerup\", onDocumentPointerUp);\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onDocumentKeyDown);\n document.removeEventListener(\"selectionchange\", onSelectionChange);\n };\n }, [onAnnotate, rootRef, sources]);\n\n useEffect(() => {\n if (candidate?.keyboard) buttonRef.current?.focus();\n }, [candidate]);\n\n if (!candidate || !onAnnotate || typeof document === \"undefined\") return null;\n const preview = truncateAnnotationQuote(candidate.annotation.quote, 72);\n return createPortal(\n <button\n ref={buttonRef}\n type=\"button\"\n data-og-annotation-popover=\"\"\n style={{ left: candidate.left, top: candidate.top, ...portalStyle }}\n className=\"og-root fixed z-[80] flex max-w-[min(18rem,calc(100vw-1.5rem))] -translate-x-1/2 items-start gap-2 rounded-2xl border border-og-border bg-og-surface-1 px-3 py-2 text-og-sm font-medium text-og-fg shadow-xl outline-hidden transition hover:border-og-accent/40 hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-[44px]\"\n aria-label={`Add a note about “${preview}”`}\n title={candidate.annotation.quote}\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => {\n onAnnotate(candidate.annotation);\n document.getSelection()?.removeAllRanges();\n setCandidate(null);\n }}\n >\n <QuoteIcon className=\"mt-0.5 size-3.5 shrink-0 text-og-accent\" aria-hidden=\"true\" />\n <span className=\"min-w-0 text-left\">\n <span className=\"block leading-5\">Add note</span>\n <span className=\"mt-0.5 block truncate text-og-xs font-normal text-og-fg-muted\">\n {preview}\n </span>\n </span>\n </button>,\n document.body,\n );\n}\n\nexport default TimelineAnnotationSelection;\n"],"mappings":"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","names":[]}
@@ -1,22 +1,23 @@
1
+ "use client";
1
2
  import {
2
3
  AnnotationAccentRow,
3
4
  AnnotationNoteField,
4
5
  AnnotationNotePreview,
5
6
  AnnotationQuoteSourceButton
6
- } from "./chunk-C5X5AJXK.js";
7
+ } from "./chunk-2HVLQ26H.js";
7
8
  import {
8
9
  ANNOTATION_REVIEW_DIALOG_WIDTH_PX,
9
10
  clampAnnotationDialogPlacement,
10
11
  scrollAnnotationRowIntoList
11
- } from "./chunk-ZYRGEKWS.js";
12
+ } from "./chunk-LJUMYUBO.js";
12
13
  import {
13
14
  annotationDisplayOrdinal,
14
15
  cssEscapeAttribute
15
- } from "./chunk-447JLCBU.js";
16
+ } from "./chunk-S2QH6EZ5.js";
16
17
  import {
17
18
  usePortalTokenStyle
18
- } from "./chunk-VZTQ73XT.js";
19
- import "./chunk-22KP6LR5.js";
19
+ } from "./chunk-XEA5BCMA.js";
20
+ import "./chunk-TXS65BB2.js";
20
21
 
21
22
  // src/components/timeline-annotations-dialog.tsx
22
23
  import { XIcon } from "lucide-react";
@@ -270,4 +271,4 @@ export {
270
271
  TimelineAnnotationsDialog,
271
272
  timeline_annotations_dialog_default as default
272
273
  };
273
- //# sourceMappingURL=timeline-annotations-dialog-ELRDBHTS.js.map
274
+ //# sourceMappingURL=timeline-annotations-dialog-FOMHSDTE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/timeline-annotations-dialog.tsx"],"sourcesContent":["import type { TimelineAnnotationSource } from \"@opengeni/sdk\";\nimport { XIcon } from \"lucide-react\";\nimport { useEffect, useLayoutEffect, useRef, useState, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport {\n AnnotationAccentRow,\n AnnotationNoteField,\n AnnotationNotePreview,\n AnnotationQuoteSourceButton,\n type TimelineAnnotationLike,\n} from \"./timeline-annotation-chrome\";\nimport {\n ANNOTATION_REVIEW_DIALOG_WIDTH_PX,\n clampAnnotationDialogPlacement,\n scrollAnnotationRowIntoList,\n} from \"./timeline-annotation-layout\";\nimport { annotationDisplayOrdinal, cssEscapeAttribute } from \"./timeline-annotation-shared\";\n\nfunction focusableElements(root: HTMLElement): HTMLElement[] {\n return [\n ...root.querySelectorAll<HTMLElement>(\n 'button:not([disabled]),textarea:not([disabled]),input:not([disabled]),[href],[tabindex]:not([tabindex=\"-1\"])',\n ),\n ].filter((element) => element.tabIndex >= 0 && !element.closest(\"[inert]\"));\n}\n\nexport function TimelineAnnotationsDialog({\n annotations,\n editable,\n focusAnnotationId,\n onFocusConsumed,\n onUpdate,\n onRemove,\n onRevealSource,\n triggerRef,\n countLabel,\n onDismiss,\n onCommit,\n}: {\n annotations: readonly TimelineAnnotationLike[];\n editable: boolean;\n focusAnnotationId?: string | null | undefined;\n onFocusConsumed?: (() => void) | undefined;\n onUpdate?: ((id: string, note: string) => void) | undefined;\n onRemove?: ((id: string) => void) | undefined;\n onRevealSource?: ((source: TimelineAnnotationSource) => boolean) | undefined;\n triggerRef: RefObject<HTMLButtonElement | null>;\n countLabel: string;\n onDismiss: (restoreFocus: boolean) => void;\n onCommit: () => void;\n}) {\n const [unavailableId, setUnavailableId] = useState<string | null>(null);\n const [position, setPosition] = useState({ left: 12, top: 12, maxHeight: 0 });\n const panelRef = useRef<HTMLDivElement | null>(null);\n const headerRef = useRef<HTMLDivElement | null>(null);\n const listRef = useRef<HTMLDivElement | null>(null);\n const noteRefs = useRef(new Map<string, HTMLTextAreaElement>());\n const focusedAnnotationIdRef = useRef<string | null>(null);\n const portalStyle = usePortalTokenStyle(triggerRef.current);\n const revealNote = (id: string) => {\n const row = listRef.current?.querySelector<HTMLElement>(\n `[data-og-annotation-id=\"${cssEscapeAttribute(id)}\"]`,\n );\n scrollAnnotationRowIntoList(listRef.current, row);\n noteRefs.current.get(id)?.focus();\n };\n\n const commitNotes = (fromId: string) => {\n const incomplete = annotations.filter((annotation) => {\n const live = noteRefs.current.get(annotation.id);\n return (live?.value ?? annotation.note).trim().length === 0;\n });\n if (incomplete.length === 0) {\n onCommit();\n return;\n }\n const fromIndex = annotations.findIndex((annotation) => annotation.id === fromId);\n const next =\n incomplete.find(\n (item) => annotations.findIndex((annotation) => annotation.id === item.id) > fromIndex,\n ) ?? incomplete[0]!;\n revealNote(next.id);\n };\n\n useLayoutEffect(() => {\n const updatePosition = () => {\n const rect = triggerRef.current?.getBoundingClientRect();\n if (!rect) return;\n const contentHeight =\n (headerRef.current?.offsetHeight ?? 44) + (listRef.current?.scrollHeight ?? 200);\n setPosition(\n clampAnnotationDialogPlacement({\n triggerLeft: rect.left,\n triggerTop: rect.top,\n triggerBottom: rect.bottom,\n contentHeight,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n panelWidth: ANNOTATION_REVIEW_DIALOG_WIDTH_PX,\n }),\n );\n };\n updatePosition();\n window.addEventListener(\"resize\", updatePosition);\n window.addEventListener(\"scroll\", updatePosition, true);\n return () => {\n window.removeEventListener(\"resize\", updatePosition);\n window.removeEventListener(\"scroll\", updatePosition, true);\n };\n }, [annotations, triggerRef]);\n\n useLayoutEffect(() => {\n if (!focusAnnotationId) {\n focusedAnnotationIdRef.current = null;\n return;\n }\n // A consumed request must not reclaim focus or scroll on layout/callback updates.\n if (focusedAnnotationIdRef.current === focusAnnotationId) return;\n focusedAnnotationIdRef.current = null;\n if (position.maxHeight <= 0) return;\n revealNote(focusAnnotationId);\n const note = noteRefs.current.get(focusAnnotationId);\n if (note && document.activeElement === note) {\n focusedAnnotationIdRef.current = focusAnnotationId;\n onFocusConsumed?.();\n }\n }, [focusAnnotationId, onFocusConsumed, annotations.length, position.maxHeight]);\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n onDismiss(true);\n return;\n }\n if (event.key !== \"Tab\" || !panelRef.current) return;\n const focusable = focusableElements(panelRef.current);\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n };\n const onPointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) return;\n if (panelRef.current?.contains(event.target) || triggerRef.current?.contains(event.target)) {\n return;\n }\n onDismiss(false);\n };\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"pointerdown\", onPointerDown);\n if (!focusAnnotationId) {\n window.setTimeout(() => {\n if (document.activeElement === triggerRef.current) panelRef.current?.focus();\n }, 0);\n }\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"pointerdown\", onPointerDown);\n };\n }, [focusAnnotationId, onDismiss, triggerRef]);\n\n if (typeof document === \"undefined\") return null;\n return createPortal(\n <div\n ref={panelRef}\n role=\"dialog\"\n tabIndex={-1}\n data-og-annotation-review=\"\"\n style={{\n left: position.left,\n top: position.top,\n maxHeight: position.maxHeight,\n ...portalStyle,\n }}\n className=\"og-root fixed z-[75] box-border flex w-[min(25rem,calc(100vw-1.5rem))] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg shadow-xl outline-hidden\"\n aria-label={editable ? \"Edit quoted notes\" : \"Quoted notes\"}\n >\n <div\n ref={headerRef}\n data-og-annotation-review-header=\"\"\n className=\"flex shrink-0 items-start justify-between gap-3 px-3 pt-3 pb-2\"\n >\n <p className=\"min-w-0 pt-0.5 text-og-sm font-medium text-og-fg-muted\">{countLabel}</p>\n <button\n type=\"button\"\n className=\"inline-flex size-8 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-og-fg-muted outline-hidden hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:size-11\"\n aria-label=\"Close\"\n onClick={() => onDismiss(true)}\n >\n <XIcon className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n <div\n ref={listRef}\n data-og-annotation-review-list=\"\"\n className=\"grid min-h-0 flex-1 gap-2.5 overflow-x-hidden overflow-y-auto overscroll-contain px-3 pb-3\"\n >\n {annotations.map((annotation, index) => {\n const ordinal = annotationDisplayOrdinal(annotation, index);\n return (\n <section\n key={annotation.id}\n data-og-annotation-id={annotation.id}\n aria-label={`Annotation ${ordinal}`}\n >\n <AnnotationAccentRow>\n <div className=\"flex items-start gap-1\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"mb-0.5 text-og-xs font-medium tabular-nums text-og-fg-subtle\">\n Annotation {ordinal}\n </p>\n <AnnotationQuoteSourceButton\n annotation={annotation}\n lines={2}\n onRevealSource={onRevealSource}\n onUnavailable={setUnavailableId}\n />\n </div>\n {editable && onRemove ? (\n <button\n type=\"button\"\n className=\"inline-flex size-7 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-og-fg-subtle outline-hidden hover:bg-og-surface-1 hover:text-og-status-failed focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:size-11\"\n aria-label={`Remove annotation ${ordinal}`}\n onClick={() => onRemove(annotation.id)}\n >\n <XIcon className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n ) : null}\n </div>\n {unavailableId === annotation.id ? (\n <p role=\"status\" className=\"text-og-xs text-og-status-waiting\">\n Source is outside the loaded timeline window.\n </p>\n ) : null}\n {editable && onUpdate ? (\n <AnnotationNoteField\n annotation={annotation}\n inputRef={(node) => {\n if (node) noteRefs.current.set(annotation.id, node);\n else noteRefs.current.delete(annotation.id);\n }}\n onUpdate={onUpdate}\n onCommit={() => commitNotes(annotation.id)}\n />\n ) : (\n <AnnotationNotePreview\n note={annotation.note}\n annotationId={annotation.id}\n ordinal={ordinal}\n />\n )}\n </AnnotationAccentRow>\n </section>\n );\n })}\n </div>\n </div>,\n document.body,\n );\n}\n\nexport default TimelineAnnotationsDialog;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/components/timeline-annotations-dialog.tsx"],"sourcesContent":["import type { TimelineAnnotationSource } from \"@opengeni/sdk\";\nimport { XIcon } from \"lucide-react\";\nimport { useEffect, useLayoutEffect, useRef, useState, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport {\n AnnotationAccentRow,\n AnnotationNoteField,\n AnnotationNotePreview,\n AnnotationQuoteSourceButton,\n type TimelineAnnotationLike,\n} from \"./timeline-annotation-chrome\";\nimport {\n ANNOTATION_REVIEW_DIALOG_WIDTH_PX,\n clampAnnotationDialogPlacement,\n scrollAnnotationRowIntoList,\n} from \"./timeline-annotation-layout\";\nimport { annotationDisplayOrdinal, cssEscapeAttribute } from \"./timeline-annotation-shared\";\n\nfunction focusableElements(root: HTMLElement): HTMLElement[] {\n return [\n ...root.querySelectorAll<HTMLElement>(\n 'button:not([disabled]),textarea:not([disabled]),input:not([disabled]),[href],[tabindex]:not([tabindex=\"-1\"])',\n ),\n ].filter((element) => element.tabIndex >= 0 && !element.closest(\"[inert]\"));\n}\n\nexport function TimelineAnnotationsDialog({\n annotations,\n editable,\n focusAnnotationId,\n onFocusConsumed,\n onUpdate,\n onRemove,\n onRevealSource,\n triggerRef,\n countLabel,\n onDismiss,\n onCommit,\n}: {\n annotations: readonly TimelineAnnotationLike[];\n editable: boolean;\n focusAnnotationId?: string | null | undefined;\n onFocusConsumed?: (() => void) | undefined;\n onUpdate?: ((id: string, note: string) => void) | undefined;\n onRemove?: ((id: string) => void) | undefined;\n onRevealSource?: ((source: TimelineAnnotationSource) => boolean) | undefined;\n triggerRef: RefObject<HTMLButtonElement | null>;\n countLabel: string;\n onDismiss: (restoreFocus: boolean) => void;\n onCommit: () => void;\n}) {\n const [unavailableId, setUnavailableId] = useState<string | null>(null);\n const [position, setPosition] = useState({ left: 12, top: 12, maxHeight: 0 });\n const panelRef = useRef<HTMLDivElement | null>(null);\n const headerRef = useRef<HTMLDivElement | null>(null);\n const listRef = useRef<HTMLDivElement | null>(null);\n const noteRefs = useRef(new Map<string, HTMLTextAreaElement>());\n const focusedAnnotationIdRef = useRef<string | null>(null);\n const portalStyle = usePortalTokenStyle(triggerRef.current);\n const revealNote = (id: string) => {\n const row = listRef.current?.querySelector<HTMLElement>(\n `[data-og-annotation-id=\"${cssEscapeAttribute(id)}\"]`,\n );\n scrollAnnotationRowIntoList(listRef.current, row);\n noteRefs.current.get(id)?.focus();\n };\n\n const commitNotes = (fromId: string) => {\n const incomplete = annotations.filter((annotation) => {\n const live = noteRefs.current.get(annotation.id);\n return (live?.value ?? annotation.note).trim().length === 0;\n });\n if (incomplete.length === 0) {\n onCommit();\n return;\n }\n const fromIndex = annotations.findIndex((annotation) => annotation.id === fromId);\n const next =\n incomplete.find(\n (item) => annotations.findIndex((annotation) => annotation.id === item.id) > fromIndex,\n ) ?? incomplete[0]!;\n revealNote(next.id);\n };\n\n useLayoutEffect(() => {\n const updatePosition = () => {\n const rect = triggerRef.current?.getBoundingClientRect();\n if (!rect) return;\n const contentHeight =\n (headerRef.current?.offsetHeight ?? 44) + (listRef.current?.scrollHeight ?? 200);\n setPosition(\n clampAnnotationDialogPlacement({\n triggerLeft: rect.left,\n triggerTop: rect.top,\n triggerBottom: rect.bottom,\n contentHeight,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n panelWidth: ANNOTATION_REVIEW_DIALOG_WIDTH_PX,\n }),\n );\n };\n updatePosition();\n window.addEventListener(\"resize\", updatePosition);\n window.addEventListener(\"scroll\", updatePosition, true);\n return () => {\n window.removeEventListener(\"resize\", updatePosition);\n window.removeEventListener(\"scroll\", updatePosition, true);\n };\n }, [annotations, triggerRef]);\n\n useLayoutEffect(() => {\n if (!focusAnnotationId) {\n focusedAnnotationIdRef.current = null;\n return;\n }\n // A consumed request must not reclaim focus or scroll on layout/callback updates.\n if (focusedAnnotationIdRef.current === focusAnnotationId) return;\n focusedAnnotationIdRef.current = null;\n if (position.maxHeight <= 0) return;\n revealNote(focusAnnotationId);\n const note = noteRefs.current.get(focusAnnotationId);\n if (note && document.activeElement === note) {\n focusedAnnotationIdRef.current = focusAnnotationId;\n onFocusConsumed?.();\n }\n }, [focusAnnotationId, onFocusConsumed, annotations.length, position.maxHeight]);\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n onDismiss(true);\n return;\n }\n if (event.key !== \"Tab\" || !panelRef.current) return;\n const focusable = focusableElements(panelRef.current);\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable[focusable.length - 1]!;\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n };\n const onPointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) return;\n if (panelRef.current?.contains(event.target) || triggerRef.current?.contains(event.target)) {\n return;\n }\n onDismiss(false);\n };\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"pointerdown\", onPointerDown);\n if (!focusAnnotationId) {\n window.setTimeout(() => {\n if (document.activeElement === triggerRef.current) panelRef.current?.focus();\n }, 0);\n }\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"pointerdown\", onPointerDown);\n };\n }, [focusAnnotationId, onDismiss, triggerRef]);\n\n if (typeof document === \"undefined\") return null;\n return createPortal(\n <div\n ref={panelRef}\n role=\"dialog\"\n tabIndex={-1}\n data-og-annotation-review=\"\"\n style={{\n left: position.left,\n top: position.top,\n maxHeight: position.maxHeight,\n ...portalStyle,\n }}\n className=\"og-root fixed z-[75] box-border flex w-[min(25rem,calc(100vw-1.5rem))] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg shadow-xl outline-hidden\"\n aria-label={editable ? \"Edit quoted notes\" : \"Quoted notes\"}\n >\n <div\n ref={headerRef}\n data-og-annotation-review-header=\"\"\n className=\"flex shrink-0 items-start justify-between gap-3 px-3 pt-3 pb-2\"\n >\n <p className=\"min-w-0 pt-0.5 text-og-sm font-medium text-og-fg-muted\">{countLabel}</p>\n <button\n type=\"button\"\n className=\"inline-flex size-8 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-og-fg-muted outline-hidden hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:size-11\"\n aria-label=\"Close\"\n onClick={() => onDismiss(true)}\n >\n <XIcon className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n <div\n ref={listRef}\n data-og-annotation-review-list=\"\"\n className=\"grid min-h-0 flex-1 gap-2.5 overflow-x-hidden overflow-y-auto overscroll-contain px-3 pb-3\"\n >\n {annotations.map((annotation, index) => {\n const ordinal = annotationDisplayOrdinal(annotation, index);\n return (\n <section\n key={annotation.id}\n data-og-annotation-id={annotation.id}\n aria-label={`Annotation ${ordinal}`}\n >\n <AnnotationAccentRow>\n <div className=\"flex items-start gap-1\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"mb-0.5 text-og-xs font-medium tabular-nums text-og-fg-subtle\">\n Annotation {ordinal}\n </p>\n <AnnotationQuoteSourceButton\n annotation={annotation}\n lines={2}\n onRevealSource={onRevealSource}\n onUnavailable={setUnavailableId}\n />\n </div>\n {editable && onRemove ? (\n <button\n type=\"button\"\n className=\"inline-flex size-7 shrink-0 items-center justify-center rounded-md border-0 bg-transparent text-og-fg-subtle outline-hidden hover:bg-og-surface-1 hover:text-og-status-failed focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:size-11\"\n aria-label={`Remove annotation ${ordinal}`}\n onClick={() => onRemove(annotation.id)}\n >\n <XIcon className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n ) : null}\n </div>\n {unavailableId === annotation.id ? (\n <p role=\"status\" className=\"text-og-xs text-og-status-waiting\">\n Source is outside the loaded timeline window.\n </p>\n ) : null}\n {editable && onUpdate ? (\n <AnnotationNoteField\n annotation={annotation}\n inputRef={(node) => {\n if (node) noteRefs.current.set(annotation.id, node);\n else noteRefs.current.delete(annotation.id);\n }}\n onUpdate={onUpdate}\n onCommit={() => commitNotes(annotation.id)}\n />\n ) : (\n <AnnotationNotePreview\n note={annotation.note}\n annotationId={annotation.id}\n ordinal={ordinal}\n />\n )}\n </AnnotationAccentRow>\n </section>\n );\n })}\n </div>\n </div>,\n document.body,\n );\n}\n\nexport default TimelineAnnotationsDialog;\n"],"mappings":"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","names":[]}
package/dist/usage.js CHANGED
@@ -1,20 +1,21 @@
1
+ "use client";
1
2
  import {
2
3
  DEFAULT_ALLOWANCE_LABELS,
3
4
  allowanceLabels,
4
5
  allowanceResetSentence,
5
6
  formatAllowanceDate,
6
7
  formatAllowanceInstant
7
- } from "./chunk-B3V6TG77.js";
8
+ } from "./chunk-GC3PH6N7.js";
8
9
  import {
9
10
  usePolledValue
10
- } from "./chunk-YQEAA6LT.js";
11
- import "./chunk-UGLEYEMP.js";
11
+ } from "./chunk-QHUE7E4N.js";
12
+ import "./chunk-CM7LVUKC.js";
12
13
  import {
13
14
  OpenGeniContext
14
- } from "./chunk-TKOTKRWY.js";
15
+ } from "./chunk-XTD7TZHW.js";
15
16
  import {
16
17
  cn
17
- } from "./chunk-22KP6LR5.js";
18
+ } from "./chunk-TXS65BB2.js";
18
19
 
19
20
  // src/hooks/use-usage.ts
20
21
  import {