@opengeni/react 3.8.0-canary.0 → 4.0.1-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +11 -24
  2. package/dist/artifacts.js +7 -7
  3. package/dist/{browser-viewer-SS46LUAC.js → browser-viewer-NTD6UIPR.js} +3 -3
  4. package/dist/{chunk-VEPNNDNG.js → chunk-24M4YBCL.js} +4 -4
  5. package/dist/chunk-5M6VTFJ5.js +171 -0
  6. package/dist/chunk-5M6VTFJ5.js.map +1 -0
  7. package/dist/{chunk-IOLVN74D.js → chunk-6J56T3MG.js} +15 -1
  8. package/dist/chunk-6J56T3MG.js.map +1 -0
  9. package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
  10. package/dist/{chunk-3HIF5IFC.js → chunk-HO6X4FHG.js} +2597 -1030
  11. package/dist/chunk-HO6X4FHG.js.map +1 -0
  12. package/dist/{chunk-BXJOQFCD.js → chunk-HUMIRRYI.js} +84 -221
  13. package/dist/chunk-HUMIRRYI.js.map +1 -0
  14. package/dist/{chunk-ZFOFY5ST.js → chunk-NLTS7JHI.js} +16 -31
  15. package/dist/chunk-NLTS7JHI.js.map +1 -0
  16. package/dist/chunk-NOSYBS2D.js +208 -0
  17. package/dist/chunk-NOSYBS2D.js.map +1 -0
  18. package/dist/chunk-YBM6CQRU.js +292 -0
  19. package/dist/chunk-YBM6CQRU.js.map +1 -0
  20. package/dist/{chunk-BQGIXRKV.js → chunk-Z7WBDB4E.js} +416 -290
  21. package/dist/chunk-Z7WBDB4E.js.map +1 -0
  22. package/dist/chunk-ZYRGEKWS.js +216 -0
  23. package/dist/chunk-ZYRGEKWS.js.map +1 -0
  24. package/dist/components/composer.d.ts +4 -2
  25. package/dist/components/human-input-form.d.ts +3 -1
  26. package/dist/components/human-input-surface.d.ts +3 -2
  27. package/dist/components/message-timeline.d.ts +9 -2
  28. package/dist/components/session-conversation.d.ts +2 -0
  29. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  30. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  31. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  32. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  33. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  34. package/dist/components/timeline-annotations-dialog.d.ts +1 -1
  35. package/dist/components/timeline-annotations.d.ts +8 -2
  36. package/dist/composer.js +3 -4
  37. package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
  38. package/dist/hooks/use-codex-accounts.d.ts +11 -1
  39. package/dist/hooks/use-composer.d.ts +2 -0
  40. package/dist/hooks/use-session-events.d.ts +2 -2
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.js +50 -37
  43. package/dist/index.js.map +1 -1
  44. package/dist/interaction.js +3 -3
  45. package/dist/machines.js +1 -1
  46. package/dist/older-history.d.ts +3 -2
  47. package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-27LKAWZD.js} +2 -2
  48. package/dist/platform-activity-row-27LKAWZD.js.map +1 -0
  49. package/dist/{queue-surface-implementation-XL2A6PBM.js → queue-surface-implementation-LK3S4BIW.js} +5 -2
  50. package/dist/{queue-surface-implementation-XL2A6PBM.js.map → queue-surface-implementation-LK3S4BIW.js.map} +1 -1
  51. package/dist/realtime.js +3 -3
  52. package/dist/session-ui.js +11 -11
  53. package/dist/session.js +2 -2
  54. package/dist/timeline/types.d.ts +3 -0
  55. package/dist/timeline-annotation-markers-DRSTLEM6.js +165 -0
  56. package/dist/timeline-annotation-markers-DRSTLEM6.js.map +1 -0
  57. package/dist/timeline-annotation-selection-IHLUGVND.js +236 -0
  58. package/dist/timeline-annotation-selection-IHLUGVND.js.map +1 -0
  59. package/dist/timeline-annotations-dialog-6Q53N5FG.js +262 -0
  60. package/dist/timeline-annotations-dialog-6Q53N5FG.js.map +1 -0
  61. package/package.json +2 -6
  62. package/src/components/chat-composer.tsx +5 -2
  63. package/src/components/composer.tsx +40 -5
  64. package/src/components/copy-button.tsx +1 -0
  65. package/src/components/human-input-form.tsx +185 -53
  66. package/src/components/human-input-surface.tsx +3 -0
  67. package/src/components/machines-dashboard.tsx +1 -16
  68. package/src/components/message-timeline.tsx +397 -302
  69. package/src/components/session-commands-panel.tsx +7 -1
  70. package/src/components/session-conversation.tsx +4 -1
  71. package/src/components/timeline-annotation-chrome.tsx +183 -0
  72. package/src/components/timeline-annotation-layout.ts +268 -0
  73. package/src/components/timeline-annotation-markers.tsx +185 -0
  74. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  75. package/src/components/timeline-annotation-selection.tsx +99 -54
  76. package/src/components/timeline-annotation-shared.ts +245 -0
  77. package/src/components/timeline-annotations-dialog.tsx +172 -128
  78. package/src/components/timeline-annotations.tsx +178 -45
  79. package/src/hooks/use-codex-accounts.ts +32 -5
  80. package/src/hooks/use-composer.ts +17 -0
  81. package/src/hooks/use-session-events.ts +16 -277
  82. package/src/index.ts +0 -1
  83. package/src/older-history.ts +22 -4
  84. package/src/timeline/platform-activity-row.tsx +4 -1
  85. package/src/timeline/projection.ts +99 -9
  86. package/src/timeline/types.ts +3 -0
  87. package/styles/compiled.css +86 -22
  88. package/dist/chat.d.ts +0 -37
  89. package/dist/chat.js +0 -315
  90. package/dist/chat.js.map +0 -1
  91. package/dist/chunk-3HIF5IFC.js.map +0 -1
  92. package/dist/chunk-BQGIXRKV.js.map +0 -1
  93. package/dist/chunk-BXJOQFCD.js.map +0 -1
  94. package/dist/chunk-F2CFHN3Y.js +0 -1415
  95. package/dist/chunk-F2CFHN3Y.js.map +0 -1
  96. package/dist/chunk-HQV2I5HV.js +0 -124
  97. package/dist/chunk-HQV2I5HV.js.map +0 -1
  98. package/dist/chunk-IOLVN74D.js.map +0 -1
  99. package/dist/chunk-W3OUAH6M.js +0 -170
  100. package/dist/chunk-W3OUAH6M.js.map +0 -1
  101. package/dist/chunk-ZFOFY5ST.js.map +0 -1
  102. package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
  103. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  104. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  105. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  106. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  107. package/src/chat.tsx +0 -393
  108. /package/dist/{browser-viewer-SS46LUAC.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
  109. /package/dist/{chunk-VEPNNDNG.js.map → chunk-24M4YBCL.js.map} +0 -0
  110. /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
  111. /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
@@ -0,0 +1,165 @@
1
+ import {
2
+ annotationBoxIntersects,
3
+ annotationViewportBox,
4
+ layoutAnnotationBadges
5
+ } from "./chunk-ZYRGEKWS.js";
6
+ import {
7
+ annotatableText,
8
+ annotationDisplayOrdinal,
9
+ annotationHasNote,
10
+ buildQuoteRange,
11
+ cssEscapeAttribute,
12
+ matchingQuoteInSource,
13
+ occurrenceOffsets
14
+ } from "./chunk-NOSYBS2D.js";
15
+ import {
16
+ cn
17
+ } from "./chunk-22KP6LR5.js";
18
+
19
+ // src/components/timeline-annotation-markers.tsx
20
+ import { useLayoutEffect, useState } from "react";
21
+ import { createPortal } from "react-dom";
22
+ import { jsx } from "react/jsx-runtime";
23
+ function lastVisibleRect(range) {
24
+ try {
25
+ const rects = range.getClientRects();
26
+ for (let index = rects.length - 1; index >= 0; index--) {
27
+ const rect = rects.item(index);
28
+ if (rect && (rect.width > 0 || rect.height > 0)) return rect;
29
+ }
30
+ } catch {
31
+ }
32
+ try {
33
+ const bounding = range.getBoundingClientRect();
34
+ if (bounding.width > 0 || bounding.height > 0) return bounding;
35
+ } catch {
36
+ }
37
+ return null;
38
+ }
39
+ function occurrenceForAnnotation(sourceEl, annotation) {
40
+ const { text } = annotatableText(sourceEl);
41
+ const quote = matchingQuoteInSource(text, annotation.quote) ?? annotation.quote;
42
+ const offsets = occurrenceOffsets(text, quote);
43
+ if (offsets.length <= 1) return 0;
44
+ let best = 0;
45
+ let bestDistance = Number.POSITIVE_INFINITY;
46
+ offsets.forEach((offset, index) => {
47
+ const distance = Math.abs(offset - annotation.source.startOffset);
48
+ if (distance < bestDistance) {
49
+ best = index;
50
+ bestDistance = distance;
51
+ }
52
+ });
53
+ return best;
54
+ }
55
+ function timelineScroller(root) {
56
+ if (root.matches("[data-og-timeline-scroller]")) return root;
57
+ const nested = root.querySelector("[data-og-timeline-scroller]");
58
+ return nested instanceof HTMLElement ? nested : root;
59
+ }
60
+ function currentAnnotationViewport(root) {
61
+ const scroller = timelineScroller(root);
62
+ const rect = scroller.getBoundingClientRect();
63
+ const scrollerBox = {
64
+ left: rect.left,
65
+ right: rect.right,
66
+ top: rect.top,
67
+ bottom: rect.bottom
68
+ };
69
+ return annotationViewportBox(window.innerWidth || 1024, window.innerHeight || 768, scrollerBox);
70
+ }
71
+ function sourceInTimeline(root, eventId) {
72
+ const source = root.querySelector(
73
+ `[data-og-annotation-source-key="${cssEscapeAttribute(eventId)}"]`
74
+ );
75
+ return source instanceof HTMLElement ? source : null;
76
+ }
77
+ function collectMarkers(root, annotations) {
78
+ if (!root) return [];
79
+ const viewport = currentAnnotationViewport(root);
80
+ const next = [];
81
+ for (const [index, annotation] of annotations.entries()) {
82
+ const source = sourceInTimeline(root, annotation.source.eventId);
83
+ if (!source) continue;
84
+ const quote = matchingQuoteInSource(annotatableText(source).text, annotation.quote) ?? annotation.quote;
85
+ const range = buildQuoteRange(source, quote, occurrenceForAnnotation(source, annotation));
86
+ if (!range) continue;
87
+ const rect = lastVisibleRect(range);
88
+ if (!rect) continue;
89
+ if (!annotationBoxIntersects(
90
+ { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom },
91
+ viewport
92
+ )) {
93
+ continue;
94
+ }
95
+ next.push({
96
+ id: annotation.id,
97
+ ordinal: annotationDisplayOrdinal(annotation, index),
98
+ left: rect.right,
99
+ top: rect.top,
100
+ incomplete: !annotationHasNote(annotation.note)
101
+ });
102
+ }
103
+ return layoutAnnotationBadges(next, viewport);
104
+ }
105
+ function TimelineAnnotationMarkers({
106
+ annotations,
107
+ onSelect,
108
+ rootRef
109
+ }) {
110
+ const [markers, setMarkers] = useState([]);
111
+ useLayoutEffect(() => {
112
+ const root = rootRef.current;
113
+ if (!root || annotations.length === 0) {
114
+ setMarkers([]);
115
+ return;
116
+ }
117
+ const update = () => setMarkers(collectMarkers(root, annotations));
118
+ update();
119
+ window.addEventListener("resize", update);
120
+ window.addEventListener("scroll", update, true);
121
+ const observer = typeof ResizeObserver === "function" ? new ResizeObserver(update) : null;
122
+ observer?.observe(root);
123
+ for (const annotation of annotations) {
124
+ const source = sourceInTimeline(root, annotation.source.eventId);
125
+ if (source) observer?.observe(source);
126
+ }
127
+ return () => {
128
+ window.removeEventListener("resize", update);
129
+ window.removeEventListener("scroll", update, true);
130
+ observer?.disconnect();
131
+ };
132
+ }, [annotations, rootRef]);
133
+ if (markers.length === 0 || typeof document === "undefined") return null;
134
+ return createPortal(
135
+ /* @__PURE__ */ jsx("div", { className: "og-root pointer-events-none fixed inset-0 z-[35]", children: markers.map((marker) => /* @__PURE__ */ jsx(
136
+ "button",
137
+ {
138
+ type: "button",
139
+ "data-og-annotation-badge": "",
140
+ "data-og-annotation-badge-ordinal": marker.ordinal,
141
+ style: { left: marker.left, top: marker.top, zIndex: marker.ordinal },
142
+ className: cn(
143
+ "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-white 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",
144
+ marker.incomplete && "ring-1 ring-og-accent-fg/35"
145
+ ),
146
+ "aria-label": `Annotation ${marker.ordinal}`,
147
+ onMouseDown: (event) => event.preventDefault(),
148
+ onClick: (event) => {
149
+ event.preventDefault();
150
+ event.stopPropagation();
151
+ onSelect?.(marker.id);
152
+ },
153
+ children: marker.ordinal
154
+ },
155
+ marker.id
156
+ )) }),
157
+ document.body
158
+ );
159
+ }
160
+ var timeline_annotation_markers_default = TimelineAnnotationMarkers;
161
+ export {
162
+ TimelineAnnotationMarkers,
163
+ timeline_annotation_markers_default as default
164
+ };
165
+ //# sourceMappingURL=timeline-annotation-markers-DRSTLEM6.js.map
@@ -0,0 +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-white 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":[]}
@@ -0,0 +1,236 @@
1
+ import {
2
+ clipRangeToSource,
3
+ matchingQuoteInSource,
4
+ occurrenceOffsets,
5
+ quoteOccurrenceIndex,
6
+ truncateAnnotationQuote,
7
+ visibleClientRect
8
+ } from "./chunk-NOSYBS2D.js";
9
+ import {
10
+ usePortalTokenStyle
11
+ } from "./chunk-VZTQ73XT.js";
12
+
13
+ // src/components/timeline-annotation-selection.tsx
14
+ import { QuoteIcon } from "lucide-react";
15
+ import { useEffect, useRef, useState } from "react";
16
+ import { createPortal } from "react-dom";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ var CONTROL_SELECTOR = "button,input,textarea,select,summary,[contenteditable='true']";
19
+ var SOURCE_CONTEXT_BYTES = 160;
20
+ var MAX_QUOTE_BYTES = 16 * 1024;
21
+ var POPOVER_WIDTH = 280;
22
+ var POPOVER_HEIGHT = 64;
23
+ function utf8Prefix(value, maxBytes) {
24
+ let output = "";
25
+ let bytes = 0;
26
+ for (const character of value) {
27
+ const next = new TextEncoder().encode(character).byteLength;
28
+ if (bytes + next > maxBytes) break;
29
+ output += character;
30
+ bytes += next;
31
+ }
32
+ return output;
33
+ }
34
+ function utf8Suffix(value, maxBytes) {
35
+ let output = "";
36
+ let bytes = 0;
37
+ for (const character of [...value].reverse()) {
38
+ const next = new TextEncoder().encode(character).byteLength;
39
+ if (bytes + next > maxBytes) break;
40
+ output = character + output;
41
+ bytes += next;
42
+ }
43
+ return output;
44
+ }
45
+ function selectionElement(node) {
46
+ return node instanceof Element ? node : node?.parentElement ?? null;
47
+ }
48
+ function containingSource(node) {
49
+ const element = selectionElement(node);
50
+ const source = element?.closest("[data-og-annotation-source-key]");
51
+ return source instanceof HTMLElement ? source : null;
52
+ }
53
+ function resolveSourceBoundary(range, root) {
54
+ const start = containingSource(range.startContainer);
55
+ const end = containingSource(range.endContainer);
56
+ if (start && end && start !== end) return null;
57
+ const boundary = start ?? end;
58
+ if (!boundary || !root.contains(boundary)) return null;
59
+ return boundary;
60
+ }
61
+ function isWhollyInsideControl(range, boundary) {
62
+ const start = selectionElement(range.startContainer)?.closest(CONTROL_SELECTOR);
63
+ const end = selectionElement(range.endContainer)?.closest(CONTROL_SELECTOR);
64
+ return start instanceof Element && start === end && boundary.contains(start) && start !== boundary;
65
+ }
66
+ function annotationId() {
67
+ if (typeof globalThis.crypto?.randomUUID === "function") {
68
+ return globalThis.crypto.randomUUID();
69
+ }
70
+ return `annotation-${Date.now()}-${Math.random().toString(16).slice(2)}`;
71
+ }
72
+ function clamp(value, min, max) {
73
+ return Math.min(Math.max(value, min), max);
74
+ }
75
+ function popoverPosition(rect) {
76
+ const half = POPOVER_WIDTH / 2;
77
+ const left = clamp(rect.left + rect.width / 2, 12 + half, window.innerWidth - 12 - half);
78
+ const preferBelow = rect.bottom + 8 + POPOVER_HEIGHT <= window.innerHeight - 12;
79
+ const top = preferBelow ? rect.bottom + 8 : Math.max(12, rect.top - POPOVER_HEIGHT - 8);
80
+ return { left, top };
81
+ }
82
+ function buildCandidate(root, sources, keyboard) {
83
+ const selection = document.getSelection();
84
+ if (!selection || selection.rangeCount !== 1 || selection.isCollapsed) return null;
85
+ const range = selection.getRangeAt(0);
86
+ const startBoundary = resolveSourceBoundary(range, root);
87
+ if (!startBoundary) return null;
88
+ if (isWhollyInsideControl(range, startBoundary)) return null;
89
+ const clipped = clipRangeToSource(range, startBoundary);
90
+ if (!clipped) return null;
91
+ const sourceKey = startBoundary.dataset.ogAnnotationSourceKey;
92
+ const source = sourceKey ? sources.get(sourceKey) : void 0;
93
+ const rawQuote = clipped.toString();
94
+ const quote = (source ? matchingQuoteInSource(source.text, rawQuote) : null) ?? rawQuote;
95
+ if (!source || quote.trim().length === 0 || new TextEncoder().encode(quote).byteLength > MAX_QUOTE_BYTES) {
96
+ return null;
97
+ }
98
+ const sourceOffsets = occurrenceOffsets(source.text, quote);
99
+ if (sourceOffsets.length === 0) return null;
100
+ const occurrence = quoteOccurrenceIndex(startBoundary, clipped, quote);
101
+ const startOffset = sourceOffsets[Math.min(occurrence, sourceOffsets.length - 1)];
102
+ const endOffset = startOffset + quote.length;
103
+ const rect = visibleClientRect(range) ?? visibleClientRect(clipped);
104
+ if (!rect) return null;
105
+ const position = popoverPosition(rect);
106
+ return {
107
+ annotation: {
108
+ id: annotationId(),
109
+ quote,
110
+ note: "",
111
+ source: {
112
+ kind: source.kind,
113
+ eventId: source.eventId,
114
+ eventType: source.eventType,
115
+ sequence: source.sequence,
116
+ turnId: source.turnId,
117
+ startOffset,
118
+ endOffset,
119
+ contextBefore: utf8Suffix(source.text.slice(0, startOffset), SOURCE_CONTEXT_BYTES),
120
+ contextAfter: utf8Prefix(source.text.slice(endOffset), SOURCE_CONTEXT_BYTES),
121
+ ...source.label ? { label: source.label } : {}
122
+ }
123
+ },
124
+ left: position.left,
125
+ top: position.top,
126
+ keyboard
127
+ };
128
+ }
129
+ function TimelineAnnotationSelection({
130
+ rootRef,
131
+ sources,
132
+ onAnnotate
133
+ }) {
134
+ const [candidate, setCandidate] = useState(null);
135
+ const buttonRef = useRef(null);
136
+ const pointerOriginRef = useRef("unset");
137
+ const portalStyle = usePortalTokenStyle(rootRef.current);
138
+ useEffect(() => {
139
+ const root = rootRef.current;
140
+ if (!root || !onAnnotate) {
141
+ setCandidate(null);
142
+ return;
143
+ }
144
+ const update = (keyboard) => {
145
+ setCandidate(buildCandidate(root, sources, keyboard));
146
+ };
147
+ const onRootPointerUp = (event) => {
148
+ if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;
149
+ if (pointerOriginRef.current === "outside") return;
150
+ window.setTimeout(() => update(false), 0);
151
+ };
152
+ const onDocumentPointerUp = (event) => {
153
+ if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;
154
+ const startedInRoot = pointerOriginRef.current === "inside";
155
+ pointerOriginRef.current = "unset";
156
+ if (!startedInRoot) return;
157
+ window.setTimeout(() => update(false), 0);
158
+ };
159
+ const onKeyUp = (event) => {
160
+ if (event.key === "Escape") {
161
+ setCandidate(null);
162
+ return;
163
+ }
164
+ window.setTimeout(() => update(true), 0);
165
+ };
166
+ const onPointerDown = (event) => {
167
+ if (event.target instanceof Node && buttonRef.current?.contains(event.target)) {
168
+ event.preventDefault();
169
+ return;
170
+ }
171
+ pointerOriginRef.current = event.target instanceof Node && root.contains(event.target) ? "inside" : "outside";
172
+ setCandidate(null);
173
+ };
174
+ const onDocumentKeyDown = (event) => {
175
+ if (event.key === "Escape") setCandidate(null);
176
+ };
177
+ const onSelectionChange = () => {
178
+ if (buttonRef.current && document.activeElement === buttonRef.current) return;
179
+ const selection = document.getSelection();
180
+ if (!selection || selection.isCollapsed) setCandidate(null);
181
+ };
182
+ root.addEventListener("pointerup", onRootPointerUp);
183
+ root.addEventListener("keyup", onKeyUp);
184
+ document.addEventListener("pointerup", onDocumentPointerUp);
185
+ document.addEventListener("pointerdown", onPointerDown);
186
+ document.addEventListener("keydown", onDocumentKeyDown);
187
+ document.addEventListener("selectionchange", onSelectionChange);
188
+ return () => {
189
+ root.removeEventListener("pointerup", onRootPointerUp);
190
+ root.removeEventListener("keyup", onKeyUp);
191
+ document.removeEventListener("pointerup", onDocumentPointerUp);
192
+ document.removeEventListener("pointerdown", onPointerDown);
193
+ document.removeEventListener("keydown", onDocumentKeyDown);
194
+ document.removeEventListener("selectionchange", onSelectionChange);
195
+ };
196
+ }, [onAnnotate, rootRef, sources]);
197
+ useEffect(() => {
198
+ if (candidate?.keyboard) buttonRef.current?.focus();
199
+ }, [candidate]);
200
+ if (!candidate || !onAnnotate || typeof document === "undefined") return null;
201
+ const preview = truncateAnnotationQuote(candidate.annotation.quote, 72);
202
+ return createPortal(
203
+ /* @__PURE__ */ jsxs(
204
+ "button",
205
+ {
206
+ ref: buttonRef,
207
+ type: "button",
208
+ "data-og-annotation-popover": "",
209
+ style: { left: candidate.left, top: candidate.top, ...portalStyle },
210
+ 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]",
211
+ "aria-label": `Add a note about \u201C${preview}\u201D`,
212
+ title: candidate.annotation.quote,
213
+ onPointerDown: (event) => event.preventDefault(),
214
+ onClick: () => {
215
+ onAnnotate(candidate.annotation);
216
+ document.getSelection()?.removeAllRanges();
217
+ setCandidate(null);
218
+ },
219
+ children: [
220
+ /* @__PURE__ */ jsx(QuoteIcon, { className: "mt-0.5 size-3.5 shrink-0 text-og-accent", "aria-hidden": "true" }),
221
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 text-left", children: [
222
+ /* @__PURE__ */ jsx("span", { className: "block leading-5", children: "Add note" }),
223
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 block truncate text-og-xs font-normal text-og-fg-muted", children: preview })
224
+ ] })
225
+ ]
226
+ }
227
+ ),
228
+ document.body
229
+ );
230
+ }
231
+ var timeline_annotation_selection_default = TimelineAnnotationSelection;
232
+ export {
233
+ TimelineAnnotationSelection,
234
+ timeline_annotation_selection_default as default
235
+ };
236
+ //# sourceMappingURL=timeline-annotation-selection-IHLUGVND.js.map
@@ -0,0 +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 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 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":[]}