@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,262 @@
1
+ import {
2
+ AnnotationAccentRow,
3
+ AnnotationNoteField,
4
+ AnnotationNotePreview,
5
+ AnnotationQuoteSourceButton
6
+ } from "./chunk-5M6VTFJ5.js";
7
+ import {
8
+ ANNOTATION_REVIEW_DIALOG_WIDTH_PX,
9
+ clampAnnotationDialogPlacement,
10
+ scrollAnnotationRowIntoList
11
+ } from "./chunk-ZYRGEKWS.js";
12
+ import {
13
+ annotationDisplayOrdinal,
14
+ cssEscapeAttribute
15
+ } from "./chunk-NOSYBS2D.js";
16
+ import {
17
+ usePortalTokenStyle
18
+ } from "./chunk-VZTQ73XT.js";
19
+ import "./chunk-22KP6LR5.js";
20
+
21
+ // src/components/timeline-annotations-dialog.tsx
22
+ import { XIcon } from "lucide-react";
23
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
24
+ import { createPortal } from "react-dom";
25
+ import { jsx, jsxs } from "react/jsx-runtime";
26
+ function focusableElements(root) {
27
+ return [
28
+ ...root.querySelectorAll(
29
+ 'button:not([disabled]),textarea:not([disabled]),input:not([disabled]),[href],[tabindex]:not([tabindex="-1"])'
30
+ )
31
+ ].filter((element) => element.tabIndex >= 0 && !element.closest("[inert]"));
32
+ }
33
+ function TimelineAnnotationsDialog({
34
+ annotations,
35
+ editable,
36
+ focusAnnotationId,
37
+ onFocusConsumed,
38
+ onUpdate,
39
+ onRemove,
40
+ onRevealSource,
41
+ triggerRef,
42
+ countLabel,
43
+ onDismiss
44
+ }) {
45
+ const [unavailableId, setUnavailableId] = useState(null);
46
+ const [position, setPosition] = useState({ left: 12, top: 12, maxHeight: 0 });
47
+ const panelRef = useRef(null);
48
+ const headerRef = useRef(null);
49
+ const listRef = useRef(null);
50
+ const noteRefs = useRef(/* @__PURE__ */ new Map());
51
+ const portalStyle = usePortalTokenStyle(triggerRef.current);
52
+ const revealNote = (id) => {
53
+ const row = listRef.current?.querySelector(
54
+ `[data-og-annotation-id="${cssEscapeAttribute(id)}"]`
55
+ );
56
+ scrollAnnotationRowIntoList(listRef.current, row);
57
+ noteRefs.current.get(id)?.focus();
58
+ };
59
+ const commitNotes = (fromId) => {
60
+ const incomplete = annotations.filter((annotation) => {
61
+ const live = noteRefs.current.get(annotation.id);
62
+ return (live?.value ?? annotation.note).trim().length === 0;
63
+ });
64
+ if (incomplete.length === 0) {
65
+ onDismiss(true);
66
+ return;
67
+ }
68
+ const fromIndex = annotations.findIndex((annotation) => annotation.id === fromId);
69
+ const next = incomplete.find(
70
+ (item) => annotations.findIndex((annotation) => annotation.id === item.id) > fromIndex
71
+ ) ?? incomplete[0];
72
+ revealNote(next.id);
73
+ };
74
+ useLayoutEffect(() => {
75
+ const updatePosition = () => {
76
+ const rect = triggerRef.current?.getBoundingClientRect();
77
+ if (!rect) return;
78
+ const contentHeight = (headerRef.current?.offsetHeight ?? 44) + (listRef.current?.scrollHeight ?? 200);
79
+ setPosition(
80
+ clampAnnotationDialogPlacement({
81
+ triggerLeft: rect.left,
82
+ triggerTop: rect.top,
83
+ triggerBottom: rect.bottom,
84
+ contentHeight,
85
+ viewportWidth: window.innerWidth,
86
+ viewportHeight: window.innerHeight,
87
+ panelWidth: ANNOTATION_REVIEW_DIALOG_WIDTH_PX
88
+ })
89
+ );
90
+ };
91
+ updatePosition();
92
+ window.addEventListener("resize", updatePosition);
93
+ window.addEventListener("scroll", updatePosition, true);
94
+ return () => {
95
+ window.removeEventListener("resize", updatePosition);
96
+ window.removeEventListener("scroll", updatePosition, true);
97
+ };
98
+ }, [annotations, triggerRef]);
99
+ useLayoutEffect(() => {
100
+ if (!focusAnnotationId || position.maxHeight <= 0) return;
101
+ revealNote(focusAnnotationId);
102
+ const note = noteRefs.current.get(focusAnnotationId);
103
+ if (note && document.activeElement === note) onFocusConsumed?.();
104
+ }, [focusAnnotationId, onFocusConsumed, annotations.length, position.maxHeight]);
105
+ useEffect(() => {
106
+ const onKeyDown = (event) => {
107
+ if (event.key === "Escape") {
108
+ event.stopPropagation();
109
+ onDismiss(true);
110
+ return;
111
+ }
112
+ if (event.key !== "Tab" || !panelRef.current) return;
113
+ const focusable = focusableElements(panelRef.current);
114
+ if (focusable.length === 0) return;
115
+ const first = focusable[0];
116
+ const last = focusable[focusable.length - 1];
117
+ if (event.shiftKey && document.activeElement === first) {
118
+ event.preventDefault();
119
+ last.focus();
120
+ } else if (!event.shiftKey && document.activeElement === last) {
121
+ event.preventDefault();
122
+ first.focus();
123
+ }
124
+ };
125
+ const onPointerDown = (event) => {
126
+ if (!(event.target instanceof Node)) return;
127
+ if (panelRef.current?.contains(event.target) || triggerRef.current?.contains(event.target)) {
128
+ return;
129
+ }
130
+ onDismiss(false);
131
+ };
132
+ document.addEventListener("keydown", onKeyDown);
133
+ document.addEventListener("pointerdown", onPointerDown);
134
+ if (!focusAnnotationId) {
135
+ window.setTimeout(() => {
136
+ if (document.activeElement === triggerRef.current) panelRef.current?.focus();
137
+ }, 0);
138
+ }
139
+ return () => {
140
+ document.removeEventListener("keydown", onKeyDown);
141
+ document.removeEventListener("pointerdown", onPointerDown);
142
+ };
143
+ }, [focusAnnotationId, onDismiss, triggerRef]);
144
+ if (typeof document === "undefined") return null;
145
+ return createPortal(
146
+ /* @__PURE__ */ jsxs(
147
+ "div",
148
+ {
149
+ ref: panelRef,
150
+ role: "dialog",
151
+ tabIndex: -1,
152
+ "data-og-annotation-review": "",
153
+ style: {
154
+ left: position.left,
155
+ top: position.top,
156
+ maxHeight: position.maxHeight,
157
+ ...portalStyle
158
+ },
159
+ 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",
160
+ "aria-label": editable ? "Edit quoted notes" : "Quoted notes",
161
+ children: [
162
+ /* @__PURE__ */ jsxs(
163
+ "div",
164
+ {
165
+ ref: headerRef,
166
+ "data-og-annotation-review-header": "",
167
+ className: "flex shrink-0 items-start justify-between gap-3 px-3 pt-3 pb-2",
168
+ children: [
169
+ /* @__PURE__ */ jsx("p", { className: "min-w-0 pt-0.5 text-og-sm font-medium text-og-fg-muted", children: countLabel }),
170
+ /* @__PURE__ */ jsx(
171
+ "button",
172
+ {
173
+ type: "button",
174
+ 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",
175
+ "aria-label": "Close",
176
+ onClick: () => onDismiss(true),
177
+ children: /* @__PURE__ */ jsx(XIcon, { className: "size-3.5", "aria-hidden": "true" })
178
+ }
179
+ )
180
+ ]
181
+ }
182
+ ),
183
+ /* @__PURE__ */ jsx(
184
+ "div",
185
+ {
186
+ ref: listRef,
187
+ "data-og-annotation-review-list": "",
188
+ className: "grid min-h-0 flex-1 gap-2.5 overflow-x-hidden overflow-y-auto overscroll-contain px-3 pb-3",
189
+ children: annotations.map((annotation, index) => {
190
+ const ordinal = annotationDisplayOrdinal(annotation, index);
191
+ return /* @__PURE__ */ jsx(
192
+ "section",
193
+ {
194
+ "data-og-annotation-id": annotation.id,
195
+ "aria-label": `Annotation ${ordinal}`,
196
+ children: /* @__PURE__ */ jsxs(AnnotationAccentRow, { children: [
197
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1", children: [
198
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
199
+ /* @__PURE__ */ jsxs("p", { className: "mb-0.5 text-og-xs font-medium tabular-nums text-og-fg-subtle", children: [
200
+ "Annotation ",
201
+ ordinal
202
+ ] }),
203
+ /* @__PURE__ */ jsx(
204
+ AnnotationQuoteSourceButton,
205
+ {
206
+ annotation,
207
+ lines: 2,
208
+ onRevealSource,
209
+ onUnavailable: setUnavailableId
210
+ }
211
+ )
212
+ ] }),
213
+ editable && onRemove ? /* @__PURE__ */ jsx(
214
+ "button",
215
+ {
216
+ type: "button",
217
+ 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",
218
+ "aria-label": `Remove annotation ${ordinal}`,
219
+ onClick: () => onRemove(annotation.id),
220
+ children: /* @__PURE__ */ jsx(XIcon, { className: "size-3.5", "aria-hidden": "true" })
221
+ }
222
+ ) : null
223
+ ] }),
224
+ unavailableId === annotation.id ? /* @__PURE__ */ jsx("p", { role: "status", className: "text-og-xs text-og-status-waiting", children: "Source is outside the loaded timeline window." }) : null,
225
+ editable && onUpdate ? /* @__PURE__ */ jsx(
226
+ AnnotationNoteField,
227
+ {
228
+ annotation,
229
+ inputRef: (node) => {
230
+ if (node) noteRefs.current.set(annotation.id, node);
231
+ else noteRefs.current.delete(annotation.id);
232
+ },
233
+ onUpdate,
234
+ onCommit: () => commitNotes(annotation.id)
235
+ }
236
+ ) : /* @__PURE__ */ jsx(
237
+ AnnotationNotePreview,
238
+ {
239
+ note: annotation.note,
240
+ annotationId: annotation.id,
241
+ ordinal
242
+ }
243
+ )
244
+ ] })
245
+ },
246
+ annotation.id
247
+ );
248
+ })
249
+ }
250
+ )
251
+ ]
252
+ }
253
+ ),
254
+ document.body
255
+ );
256
+ }
257
+ var timeline_annotations_dialog_default = TimelineAnnotationsDialog;
258
+ export {
259
+ TimelineAnnotationsDialog,
260
+ timeline_annotations_dialog_default as default
261
+ };
262
+ //# sourceMappingURL=timeline-annotations-dialog-6Q53N5FG.js.map
@@ -0,0 +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}: {\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}) {\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 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 onDismiss(true);\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 || position.maxHeight <= 0) return;\n revealNote(focusAnnotationId);\n const note = noteRefs.current.get(focusAnnotationId);\n if (note && document.activeElement === note) onFocusConsumed?.();\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/react",
3
- "version": "3.8.0-canary.0",
3
+ "version": "4.0.1-canary.0",
4
4
  "description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -78,10 +78,6 @@
78
78
  "types": "./dist/clipboard.d.ts",
79
79
  "import": "./dist/clipboard.js"
80
80
  },
81
- "./chat": {
82
- "types": "./dist/chat.tsx",
83
- "import": "./dist/chat.tsx"
84
- },
85
81
  "./styles.css": {
86
82
  "types": "./styles/index.d.ts",
87
83
  "style": "./styles/index.css",
@@ -124,7 +120,7 @@
124
120
  "@dnd-kit/utilities": "3.2.2",
125
121
  "@fontsource-variable/noto-sans-arabic": "5.2.10",
126
122
  "@fontsource-variable/noto-sans-jp": "5.2.10",
127
- "@opengeni/sdk": "^3.8.0-canary.0",
123
+ "@opengeni/sdk": "^4.0.1-canary.0",
128
124
  "clsx": "^2.1.1",
129
125
  "dequal": "2.0.3",
130
126
  "lucide-react": "^1.8.0",
@@ -157,8 +157,11 @@ export function ChatComposer({
157
157
  <RestoredResources />
158
158
  <Attachments />
159
159
  {header}
160
- {composer.annotations && composer.annotations.length > 0 ? (
161
- <div className="px-3 pt-2 sm:px-4">
160
+ {composer.annotations &&
161
+ composer.annotations.length > 0 &&
162
+ composer.updateAnnotation &&
163
+ composer.removeAnnotation ? (
164
+ <div className="px-3.5 pt-2 md:px-4">
162
165
  <TimelineAnnotationsChip
163
166
  annotations={composer.annotations}
164
167
  editable
@@ -74,7 +74,16 @@ function ComposerTip({
74
74
 
75
75
  export type ComposerDelivery = Pick<
76
76
  ComposerState,
77
- "value" | "setValue" | "send" | "steer" | "sending" | "canSend" | "error" | "clearError"
77
+ | "value"
78
+ | "setValue"
79
+ | "send"
80
+ | "steer"
81
+ | "sending"
82
+ | "canSend"
83
+ | "error"
84
+ | "clearError"
85
+ | "annotations"
86
+ | "requestAnnotationReview"
78
87
  >;
79
88
 
80
89
  export type ComposerDraftState = Pick<
@@ -112,6 +121,7 @@ export type ChatComposerMessages = {
112
121
  sendAndResumeAriaLabel: string;
113
122
  sendTitle: string;
114
123
  sendAndResumeTitle: string;
124
+ annotationNotesRequired: string;
115
125
  workspacePaused: string;
116
126
  pausedHere: string;
117
127
  parentBlocker: string;
@@ -179,6 +189,7 @@ export const defaultChatComposerMessages: ChatComposerMessages = {
179
189
  sendAndResumeAriaLabel: "Add message to queue",
180
190
  sendTitle: "Queue message (Enter); steer with Cmd/Ctrl+Enter",
181
191
  sendAndResumeTitle: "Add to queue (Enter); steer now with Cmd/Ctrl+Enter",
192
+ annotationNotesRequired: "Add a note to each quote before sending.",
182
193
  workspacePaused: "Workspace paused",
183
194
  pausedHere: "Paused here",
184
195
  parentBlocker: "parent",
@@ -237,6 +248,7 @@ export type ComposerSubmitBlocker =
237
248
  | "attachment"
238
249
  | "sending"
239
250
  | "command"
251
+ | "annotations"
240
252
  | "empty"
241
253
  | null;
242
254
 
@@ -547,6 +559,9 @@ export function useChatComposerController({
547
559
  });
548
560
  const paletteEnabled = commandContext !== undefined;
549
561
  const commandDraftBlocked = paletteEnabled && palette.isCommandDraft;
562
+ const annotationsIncomplete = (delivery.annotations ?? []).some(
563
+ (annotation) => annotation.note.trim().length === 0,
564
+ );
550
565
 
551
566
  const submitBlocker: ComposerSubmitBlocker = disabled
552
567
  ? "disabled"
@@ -556,9 +571,11 @@ export function useChatComposerController({
556
571
  ? "sending"
557
572
  : commandDraftBlocked
558
573
  ? "command"
559
- : delivery.canSend || hasReadyAttachment
560
- ? null
561
- : "empty";
574
+ : annotationsIncomplete
575
+ ? "annotations"
576
+ : delivery.canSend || hasReadyAttachment
577
+ ? null
578
+ : "empty";
562
579
  const canSubmit = submitBlocker === null;
563
580
 
564
581
  const submit = useCallback(
@@ -570,6 +587,12 @@ export function useChatComposerController({
570
587
  }
571
588
  if (disabled || blockedByAttachment || delivery.sending || submittingRef.current)
572
589
  return false;
590
+ if (annotationsIncomplete) {
591
+ setNotice({ tone: "error", message: messages.annotationNotesRequired });
592
+ delivery.clearError();
593
+ delivery.requestAnnotationReview?.();
594
+ return false;
595
+ }
573
596
  if (!delivery.canSend && !hasReadyAttachment) return false;
574
597
  submittingRef.current = true;
575
598
  setSubmitting(true);
@@ -581,11 +604,13 @@ export function useChatComposerController({
581
604
  }
582
605
  },
583
606
  [
607
+ annotationsIncomplete,
584
608
  blockedByAttachment,
585
609
  commandDraftBlocked,
586
610
  delivery,
587
611
  disabled,
588
612
  hasReadyAttachment,
613
+ messages.annotationNotesRequired,
589
614
  messages.slashCommandBlocked,
590
615
  ],
591
616
  );
@@ -645,6 +670,12 @@ export function useChatComposerController({
645
670
  );
646
671
  return () => window.clearTimeout(timer);
647
672
  }, [notice]);
673
+ useEffect(() => {
674
+ if (annotationsIncomplete) return;
675
+ setNotice((current) =>
676
+ current?.message === messages.annotationNotesRequired ? null : current,
677
+ );
678
+ }, [annotationsIncomplete, messages.annotationNotesRequired]);
648
679
 
649
680
  const runControlOperation = useCallback(async (operation: () => Promise<void>) => {
650
681
  if (controlOperationRef.current) return false;
@@ -1260,7 +1291,11 @@ export const SendButton = forwardRef<HTMLButtonElement, ComposerSendButtonProps>
1260
1291
  const controller = useComposerController();
1261
1292
  const tip =
1262
1293
  title ??
1263
- (controller.paused ? controller.messages.sendAndResumeTitle : controller.messages.sendTitle);
1294
+ (controller.submitBlocker === "annotations"
1295
+ ? controller.messages.annotationNotesRequired
1296
+ : controller.paused
1297
+ ? controller.messages.sendAndResumeTitle
1298
+ : controller.messages.sendTitle);
1264
1299
  return (
1265
1300
  <ComposerTip tip={tip}>
1266
1301
  <button
@@ -140,6 +140,7 @@ export function CopyHoverFrame({
140
140
  {children}
141
141
  {/* Sit under the body — top-right overlay collided with the first line. */}
142
142
  <div
143
+ data-og-annotation-chrome=""
143
144
  className={cn(
144
145
  "mt-1 flex h-7 items-center gap-1.5",
145
146
  align === "end" ? "justify-end" : "justify-start",