@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.
- package/README.md +11 -24
- package/dist/artifacts.js +7 -7
- package/dist/{browser-viewer-SS46LUAC.js → browser-viewer-NTD6UIPR.js} +3 -3
- package/dist/{chunk-VEPNNDNG.js → chunk-24M4YBCL.js} +4 -4
- package/dist/chunk-5M6VTFJ5.js +171 -0
- package/dist/chunk-5M6VTFJ5.js.map +1 -0
- package/dist/{chunk-IOLVN74D.js → chunk-6J56T3MG.js} +15 -1
- package/dist/chunk-6J56T3MG.js.map +1 -0
- package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
- package/dist/{chunk-3HIF5IFC.js → chunk-HO6X4FHG.js} +2597 -1030
- package/dist/chunk-HO6X4FHG.js.map +1 -0
- package/dist/{chunk-BXJOQFCD.js → chunk-HUMIRRYI.js} +84 -221
- package/dist/chunk-HUMIRRYI.js.map +1 -0
- package/dist/{chunk-ZFOFY5ST.js → chunk-NLTS7JHI.js} +16 -31
- package/dist/chunk-NLTS7JHI.js.map +1 -0
- package/dist/chunk-NOSYBS2D.js +208 -0
- package/dist/chunk-NOSYBS2D.js.map +1 -0
- package/dist/chunk-YBM6CQRU.js +292 -0
- package/dist/chunk-YBM6CQRU.js.map +1 -0
- package/dist/{chunk-BQGIXRKV.js → chunk-Z7WBDB4E.js} +416 -290
- package/dist/chunk-Z7WBDB4E.js.map +1 -0
- package/dist/chunk-ZYRGEKWS.js +216 -0
- package/dist/chunk-ZYRGEKWS.js.map +1 -0
- package/dist/components/composer.d.ts +4 -2
- package/dist/components/human-input-form.d.ts +3 -1
- package/dist/components/human-input-surface.d.ts +3 -2
- package/dist/components/message-timeline.d.ts +9 -2
- package/dist/components/session-conversation.d.ts +2 -0
- package/dist/components/timeline-annotation-chrome.d.ts +25 -0
- package/dist/components/timeline-annotation-layout.d.ts +42 -0
- package/dist/components/timeline-annotation-markers.d.ts +8 -0
- package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
- package/dist/components/timeline-annotation-shared.d.ts +31 -0
- package/dist/components/timeline-annotations-dialog.d.ts +1 -1
- package/dist/components/timeline-annotations.d.ts +8 -2
- package/dist/composer.js +3 -4
- package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
- package/dist/hooks/use-codex-accounts.d.ts +11 -1
- package/dist/hooks/use-composer.d.ts +2 -0
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +50 -37
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +3 -3
- package/dist/machines.js +1 -1
- package/dist/older-history.d.ts +3 -2
- package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-27LKAWZD.js} +2 -2
- package/dist/platform-activity-row-27LKAWZD.js.map +1 -0
- package/dist/{queue-surface-implementation-XL2A6PBM.js → queue-surface-implementation-LK3S4BIW.js} +5 -2
- package/dist/{queue-surface-implementation-XL2A6PBM.js.map → queue-surface-implementation-LK3S4BIW.js.map} +1 -1
- package/dist/realtime.js +3 -3
- package/dist/session-ui.js +11 -11
- package/dist/session.js +2 -2
- package/dist/timeline/types.d.ts +3 -0
- package/dist/timeline-annotation-markers-DRSTLEM6.js +165 -0
- package/dist/timeline-annotation-markers-DRSTLEM6.js.map +1 -0
- package/dist/timeline-annotation-selection-IHLUGVND.js +236 -0
- package/dist/timeline-annotation-selection-IHLUGVND.js.map +1 -0
- package/dist/timeline-annotations-dialog-6Q53N5FG.js +262 -0
- package/dist/timeline-annotations-dialog-6Q53N5FG.js.map +1 -0
- package/package.json +2 -6
- package/src/components/chat-composer.tsx +5 -2
- package/src/components/composer.tsx +40 -5
- package/src/components/copy-button.tsx +1 -0
- package/src/components/human-input-form.tsx +185 -53
- package/src/components/human-input-surface.tsx +3 -0
- package/src/components/machines-dashboard.tsx +1 -16
- package/src/components/message-timeline.tsx +397 -302
- package/src/components/session-commands-panel.tsx +7 -1
- package/src/components/session-conversation.tsx +4 -1
- package/src/components/timeline-annotation-chrome.tsx +183 -0
- package/src/components/timeline-annotation-layout.ts +268 -0
- package/src/components/timeline-annotation-markers.tsx +185 -0
- package/src/components/timeline-annotation-reveal-context.ts +9 -0
- package/src/components/timeline-annotation-selection.tsx +99 -54
- package/src/components/timeline-annotation-shared.ts +245 -0
- package/src/components/timeline-annotations-dialog.tsx +172 -128
- package/src/components/timeline-annotations.tsx +178 -45
- package/src/hooks/use-codex-accounts.ts +32 -5
- package/src/hooks/use-composer.ts +17 -0
- package/src/hooks/use-session-events.ts +16 -277
- package/src/index.ts +0 -1
- package/src/older-history.ts +22 -4
- package/src/timeline/platform-activity-row.tsx +4 -1
- package/src/timeline/projection.ts +99 -9
- package/src/timeline/types.ts +3 -0
- package/styles/compiled.css +86 -22
- package/dist/chat.d.ts +0 -37
- package/dist/chat.js +0 -315
- package/dist/chat.js.map +0 -1
- package/dist/chunk-3HIF5IFC.js.map +0 -1
- package/dist/chunk-BQGIXRKV.js.map +0 -1
- package/dist/chunk-BXJOQFCD.js.map +0 -1
- package/dist/chunk-F2CFHN3Y.js +0 -1415
- package/dist/chunk-F2CFHN3Y.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-IOLVN74D.js.map +0 -1
- package/dist/chunk-W3OUAH6M.js +0 -170
- package/dist/chunk-W3OUAH6M.js.map +0 -1
- package/dist/chunk-ZFOFY5ST.js.map +0 -1
- package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
- package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
- package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
- package/src/chat.tsx +0 -393
- /package/dist/{browser-viewer-SS46LUAC.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
- /package/dist/{chunk-VEPNNDNG.js.map → chunk-24M4YBCL.js.map} +0 -0
- /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
- /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
|
+
"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": "^
|
|
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 &&
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
:
|
|
560
|
-
?
|
|
561
|
-
:
|
|
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.
|
|
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",
|