@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
|
@@ -106,6 +106,7 @@ import {
|
|
|
106
106
|
useTurnSettleOpen,
|
|
107
107
|
} from "../timeline";
|
|
108
108
|
import { CopyHoverFrame } from "./copy-button";
|
|
109
|
+
import { TimelineAnnotationSourceRootContext } from "./timeline-annotation-reveal-context";
|
|
109
110
|
import { GeneratedVideoPlayer } from "./generated-video-player";
|
|
110
111
|
import {
|
|
111
112
|
MACHINE_INPUT_META,
|
|
@@ -118,10 +119,11 @@ import { SESSION_STATUS_META, StatusDot } from "./session-status";
|
|
|
118
119
|
import { TimelineComputeLabelProvider } from "../timeline/compute-label";
|
|
119
120
|
import { EntranceAnimationProvider, useEntranceAnimation } from "../timeline/entrance";
|
|
120
121
|
import { SeenActivityIdsProvider } from "../timeline/seen-activity-ids";
|
|
121
|
-
import {
|
|
122
|
+
import { TimelineAnnotationCards } from "./timeline-annotations";
|
|
122
123
|
import { TooltipProvider } from "./tooltip";
|
|
123
124
|
|
|
124
125
|
const TimelineAnnotationSelection = lazy(() => import("./timeline-annotation-selection"));
|
|
126
|
+
const TimelineAnnotationMarkers = lazy(() => import("./timeline-annotation-markers"));
|
|
125
127
|
|
|
126
128
|
export type MessageTimelineProps = {
|
|
127
129
|
/** Raw session events (projected internally) … */
|
|
@@ -130,6 +132,8 @@ export type MessageTimelineProps = {
|
|
|
130
132
|
items?: TimelineItem[] | undefined;
|
|
131
133
|
/** Current session status (reserved; tip "Working…" chrome removed for now). */
|
|
132
134
|
status?: SessionStatus | null | undefined;
|
|
135
|
+
/** Host-owned controls beside Copy and the timestamp on settled message rows. */
|
|
136
|
+
renderMessageActions?: ((item: AgentMessageItem | UserMessageItem) => ReactNode) | undefined;
|
|
133
137
|
/** Plug a markdown renderer for message bodies (e.g. streamdown). */
|
|
134
138
|
renderMessageText?:
|
|
135
139
|
| ((text: string, item: AgentMessageItem | UserMessageItem) => ReactNode)
|
|
@@ -190,6 +194,10 @@ export type MessageTimelineProps = {
|
|
|
190
194
|
autoFollow?: boolean | undefined;
|
|
191
195
|
/** Capture a same-row text selection into the host's canonical composer draft. */
|
|
192
196
|
onAnnotate?: ((annotation: DraftTimelineAnnotation) => void) | undefined;
|
|
197
|
+
/** Composer draft quotes currently attached to the next send. */
|
|
198
|
+
draftAnnotations?: readonly DraftTimelineAnnotation[] | undefined;
|
|
199
|
+
/** Open the composer review list for one numbered draft badge. */
|
|
200
|
+
onDraftAnnotationSelect?: ((id: string) => void) | undefined;
|
|
193
201
|
/** Older durable history exists above the current window (see useSessionEvents). */
|
|
194
202
|
hasOlder?: boolean | undefined;
|
|
195
203
|
/** An older window is being fetched; shows the quiet top shimmer. */
|
|
@@ -285,13 +293,18 @@ function invokeOlderLoad(
|
|
|
285
293
|
load: OlderHistoryLoader,
|
|
286
294
|
noProgress: () => void,
|
|
287
295
|
attempt: OlderLoadAttempt,
|
|
296
|
+
preserveTail = false,
|
|
288
297
|
): 1 | undefined {
|
|
289
298
|
try {
|
|
290
299
|
// Receipt creation is captured synchronously through legacy wrappers such
|
|
291
300
|
// as `() => void loadOlder()`, even when the wrapper discards the return.
|
|
292
|
-
const result = invokeOlderHistoryLoaderWithReceiptCapture(
|
|
293
|
-
|
|
294
|
-
|
|
301
|
+
const result = invokeOlderHistoryLoaderWithReceiptCapture(
|
|
302
|
+
load,
|
|
303
|
+
(receipt) => {
|
|
304
|
+
attempt[2] = receipt;
|
|
305
|
+
},
|
|
306
|
+
preserveTail,
|
|
307
|
+
) as OlderHistoryLoadReceipt | PromiseLike<unknown> | unknown;
|
|
295
308
|
const receipt =
|
|
296
309
|
attempt[2] ??
|
|
297
310
|
(typeof (result as { committed?: unknown } | undefined)?.committed === "boolean"
|
|
@@ -389,6 +402,7 @@ export function MessageTimeline({
|
|
|
389
402
|
events,
|
|
390
403
|
items,
|
|
391
404
|
status: _status,
|
|
405
|
+
renderMessageActions,
|
|
392
406
|
renderMessageText,
|
|
393
407
|
onOpenSession,
|
|
394
408
|
onMemoryClick,
|
|
@@ -403,6 +417,8 @@ export function MessageTimeline({
|
|
|
403
417
|
turnSummary,
|
|
404
418
|
autoFollow = true,
|
|
405
419
|
onAnnotate,
|
|
420
|
+
draftAnnotations,
|
|
421
|
+
onDraftAnnotationSelect,
|
|
406
422
|
hasOlder = false,
|
|
407
423
|
loadingOlder = false,
|
|
408
424
|
onLoadOlder,
|
|
@@ -889,6 +905,7 @@ export function MessageTimeline({
|
|
|
889
905
|
() => ({
|
|
890
906
|
userMessageDisclosureContext,
|
|
891
907
|
behavior: {
|
|
908
|
+
renderMessageActions,
|
|
892
909
|
renderMessageText,
|
|
893
910
|
onOpenSession,
|
|
894
911
|
onMemoryClick,
|
|
@@ -908,6 +925,7 @@ export function MessageTimeline({
|
|
|
908
925
|
onMemoryClick,
|
|
909
926
|
onOpenSession,
|
|
910
927
|
onReconnect,
|
|
928
|
+
renderMessageActions,
|
|
911
929
|
renderMessageText,
|
|
912
930
|
resolveProviderLogo,
|
|
913
931
|
toolRegistry,
|
|
@@ -965,7 +983,7 @@ export function MessageTimeline({
|
|
|
965
983
|
// exact `false` is the first-party request-not-accepted receipt.
|
|
966
984
|
// All other fulfillment retains this exact owner until its prepend
|
|
967
985
|
// boundary commits; promise settlement alone cannot prove progress.
|
|
968
|
-
invokeOlderLoad(onLoadOlder, noProgress, attempt);
|
|
986
|
+
invokeOlderLoad(onLoadOlder, noProgress, attempt, !retry);
|
|
969
987
|
},
|
|
970
988
|
[hasOlder, loadingOlder, olderBoundaryKey, onLoadOlder],
|
|
971
989
|
);
|
|
@@ -1523,7 +1541,9 @@ export function MessageTimeline({
|
|
|
1523
1541
|
}
|
|
1524
1542
|
const observer = new IntersectionObserver(
|
|
1525
1543
|
(entries) => {
|
|
1526
|
-
|
|
1544
|
+
// An underfilled history window has both sentinels visible. Advancing
|
|
1545
|
+
// it automatically would undo an explicit older-page navigation.
|
|
1546
|
+
if (maxScrollOf(root) > 1 && entries.some((entry) => entry.isIntersecting)) {
|
|
1527
1547
|
onLoadNewer();
|
|
1528
1548
|
}
|
|
1529
1549
|
},
|
|
@@ -1785,303 +1805,318 @@ export function MessageTimeline({
|
|
|
1785
1805
|
<TimelineComputeLabelProvider value={computeLabel ?? null}>
|
|
1786
1806
|
<EntranceAnimationProvider value={false}>
|
|
1787
1807
|
<TooltipProvider delayDuration={400}>
|
|
1788
|
-
<
|
|
1789
|
-
{
|
|
1790
|
-
|
|
1791
|
-
<
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1808
|
+
<TimelineAnnotationSourceRootContext.Provider value={scrollRef}>
|
|
1809
|
+
<div className={cn("og-root relative flex min-h-0 flex-col", className)}>
|
|
1810
|
+
{onAnnotate ? (
|
|
1811
|
+
<Suspense fallback={null}>
|
|
1812
|
+
<TimelineAnnotationSelection
|
|
1813
|
+
rootRef={scrollRef}
|
|
1814
|
+
sources={annotationSources}
|
|
1815
|
+
onAnnotate={onAnnotate}
|
|
1816
|
+
/>
|
|
1817
|
+
</Suspense>
|
|
1818
|
+
) : null}
|
|
1819
|
+
{/* Pinned: anchoring off so the tip-follow camera owns the motion.
|
|
1799
1820
|
Unpinned: native scroll anchoring holds the reader's place. */}
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
: null;
|
|
1813
|
-
}}
|
|
1814
|
-
onTouchMove={(event) => {
|
|
1815
|
-
const touch = event.touches[0];
|
|
1816
|
-
const previous = touchPositionRef.current;
|
|
1817
|
-
if (!touch || !previous || event.touches.length !== 1) {
|
|
1818
|
-
touchPositionRef.current = null;
|
|
1819
|
-
return;
|
|
1820
|
-
}
|
|
1821
|
-
const deltaX = previous.x - touch.clientX;
|
|
1822
|
-
const deltaY = previous.y - touch.clientY;
|
|
1823
|
-
if (Math.max(Math.abs(deltaX), Math.abs(deltaY)) < 4) return;
|
|
1824
|
-
touchPositionRef.current = { x: touch.clientX, y: touch.clientY };
|
|
1825
|
-
onWheel({
|
|
1826
|
-
deltaX,
|
|
1827
|
-
deltaY,
|
|
1828
|
-
target: event.target,
|
|
1829
|
-
currentTarget: event.currentTarget,
|
|
1830
|
-
});
|
|
1831
|
-
}}
|
|
1832
|
-
onTouchEnd={() => {
|
|
1833
|
-
touchPositionRef.current = null;
|
|
1834
|
-
}}
|
|
1835
|
-
onTouchCancel={() => {
|
|
1836
|
-
touchPositionRef.current = null;
|
|
1837
|
-
}}
|
|
1838
|
-
onClickCapture={(event) => {
|
|
1839
|
-
const target =
|
|
1840
|
-
event.target instanceof Element
|
|
1841
|
-
? event.target.closest("button[aria-expanded]")
|
|
1821
|
+
<div
|
|
1822
|
+
ref={scrollRef}
|
|
1823
|
+
data-og-timeline-scroller=""
|
|
1824
|
+
data-og-bottom-follow={autoFollow && pinned && !hasNewer ? "true" : "false"}
|
|
1825
|
+
tabIndex={-1}
|
|
1826
|
+
onScroll={onScroll}
|
|
1827
|
+
onScrollEnd={onScrollEnd}
|
|
1828
|
+
onWheel={onWheel}
|
|
1829
|
+
onTouchStart={(event) => {
|
|
1830
|
+
const touch = event.touches.length === 1 ? event.touches[0] : undefined;
|
|
1831
|
+
touchPositionRef.current = touch
|
|
1832
|
+
? { x: touch.clientX, y: touch.clientY }
|
|
1842
1833
|
: null;
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1834
|
+
}}
|
|
1835
|
+
onTouchMove={(event) => {
|
|
1836
|
+
const touch = event.touches[0];
|
|
1837
|
+
const previous = touchPositionRef.current;
|
|
1838
|
+
if (!touch || !previous || event.touches.length !== 1) {
|
|
1839
|
+
touchPositionRef.current = null;
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1842
|
+
const deltaX = previous.x - touch.clientX;
|
|
1843
|
+
const deltaY = previous.y - touch.clientY;
|
|
1844
|
+
if (Math.max(Math.abs(deltaX), Math.abs(deltaY)) < 4) return;
|
|
1845
|
+
touchPositionRef.current = { x: touch.clientX, y: touch.clientY };
|
|
1846
|
+
onWheel({
|
|
1847
|
+
deltaX,
|
|
1848
|
+
deltaY,
|
|
1849
|
+
target: event.target,
|
|
1850
|
+
currentTarget: event.currentTarget,
|
|
1851
|
+
});
|
|
1852
|
+
}}
|
|
1853
|
+
onTouchEnd={() => {
|
|
1854
|
+
touchPositionRef.current = null;
|
|
1855
|
+
}}
|
|
1856
|
+
onTouchCancel={() => {
|
|
1857
|
+
touchPositionRef.current = null;
|
|
1858
|
+
}}
|
|
1859
|
+
onClickCapture={(event) => {
|
|
1860
|
+
const target =
|
|
1861
|
+
event.target instanceof Element
|
|
1862
|
+
? event.target.closest("button[aria-expanded]")
|
|
1865
1863
|
: null;
|
|
1864
|
+
if (target) {
|
|
1865
|
+
releasePinFromReader();
|
|
1866
|
+
disclosureKeepsUnpinnedRef.current = true;
|
|
1867
|
+
}
|
|
1866
1868
|
}}
|
|
1869
|
+
onPointerDown={onPointerDown}
|
|
1870
|
+
onKeyDown={onKeyDown}
|
|
1871
|
+
style={groups.length > 0 && !revealed ? { visibility: "hidden" } : undefined}
|
|
1872
|
+
className={cn(
|
|
1873
|
+
// tabIndex=-1 is programmatic only — never paint a focus ring on
|
|
1874
|
+
// the whole scroller (click + Shift used to flash a blue outline).
|
|
1875
|
+
"min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 pt-16 pb-6 sm:px-6 outline-hidden",
|
|
1876
|
+
autoFollow && pinned && !hasNewer
|
|
1877
|
+
? "[overflow-anchor:none]"
|
|
1878
|
+
: "[overflow-anchor:auto]",
|
|
1879
|
+
)}
|
|
1867
1880
|
>
|
|
1868
|
-
<
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1881
|
+
<TimelineBeforeLayout
|
|
1882
|
+
capture={() => {
|
|
1883
|
+
readingAnchorRef.current =
|
|
1884
|
+
!pinnedRef.current && scrollRef.current
|
|
1885
|
+
? captureTimelineAnchor(scrollRef.current)
|
|
1886
|
+
: null;
|
|
1887
|
+
}}
|
|
1888
|
+
>
|
|
1889
|
+
<div className="relative mx-auto flex w-full max-w-3xl flex-col gap-5">
|
|
1890
|
+
<AnimatePresence>
|
|
1891
|
+
{loadingOlder ||
|
|
1892
|
+
loadingOldest ||
|
|
1893
|
+
(hasOlder && onJumpToStart && olderPrefetchArmed) ||
|
|
1894
|
+
underfillRetryReady ? (
|
|
1895
|
+
// Reserved top gutter: controls scroll with history and cannot
|
|
1896
|
+
// cover a disclosure. Visibility never changes content height.
|
|
1897
|
+
<motion.div
|
|
1898
|
+
initial={{ opacity: 0, y: -6 }}
|
|
1899
|
+
animate={{ opacity: 1, y: 0 }}
|
|
1900
|
+
exit={{ opacity: 0, y: -6 }}
|
|
1901
|
+
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
1902
|
+
data-og-loading-older=""
|
|
1903
|
+
aria-live="polite"
|
|
1904
|
+
className="pointer-events-none absolute inset-x-0 -top-11 z-10 flex justify-center"
|
|
1905
|
+
>
|
|
1906
|
+
{loadingOlder || loadingOldest ? (
|
|
1907
|
+
<span className={LOADING_CHIP_CLASS}>
|
|
1908
|
+
<span className="og-shimmer-text">
|
|
1909
|
+
{loadingOldest
|
|
1910
|
+
? "Jumping to start…"
|
|
1911
|
+
: "Loading earlier activity…"}
|
|
1912
|
+
</span>
|
|
1891
1913
|
</span>
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
requestOlderIfUnderfilled(node, underfillSettledAttempt!);
|
|
1910
|
-
}
|
|
1911
|
-
return;
|
|
1912
|
-
}
|
|
1913
|
-
applyPinned(false);
|
|
1914
|
-
pendingJumpToStartRef.current = true;
|
|
1915
|
-
const seq = ++jumpToStartSeqRef.current;
|
|
1916
|
-
void Promise.resolve(onJumpToStart!()).then(
|
|
1917
|
-
() => {
|
|
1918
|
-
// The commit that swaps in the oldest window consumes
|
|
1919
|
-
// the flag against the new DOM; this write covers the
|
|
1920
|
-
// already-committed order and the no-window-change
|
|
1921
|
-
// case (jumping within the current window).
|
|
1922
|
-
const scroller = scrollRef.current ?? node;
|
|
1923
|
-
if (scroller) {
|
|
1924
|
-
scroller.scrollTop = 0;
|
|
1914
|
+
) : null}
|
|
1915
|
+
{hasOlder &&
|
|
1916
|
+
!loadingOlder &&
|
|
1917
|
+
!loadingOldest &&
|
|
1918
|
+
(underfillRetryReady || onJumpToStart) ? (
|
|
1919
|
+
<button
|
|
1920
|
+
type="button"
|
|
1921
|
+
data-og-retry={underfillRetryReady || undefined}
|
|
1922
|
+
data-og-jump-to-start={!underfillRetryReady || undefined}
|
|
1923
|
+
onClick={() => {
|
|
1924
|
+
const node = scrollRef.current;
|
|
1925
|
+
if (underfillRetryReady) {
|
|
1926
|
+
if (node && underfillRetryReadyRef.current) {
|
|
1927
|
+
// AnimatePresence retains this handler during
|
|
1928
|
+
// exit. Current authorization plus the exact
|
|
1929
|
+
// attempt check prevent stale dispatch.
|
|
1930
|
+
requestOlderIfUnderfilled(node, underfillSettledAttempt!);
|
|
1925
1931
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
applyPinned(false);
|
|
1935
|
+
pendingJumpToStartRef.current = true;
|
|
1936
|
+
const seq = ++jumpToStartSeqRef.current;
|
|
1937
|
+
void Promise.resolve(onJumpToStart!()).then(
|
|
1938
|
+
() => {
|
|
1939
|
+
// The commit that swaps in the oldest window consumes
|
|
1940
|
+
// the flag against the new DOM; this write covers the
|
|
1941
|
+
// already-committed order and the no-window-change
|
|
1942
|
+
// case (jumping within the current window).
|
|
1943
|
+
const scroller = scrollRef.current ?? node;
|
|
1944
|
+
if (scroller) {
|
|
1945
|
+
scroller.scrollTop = 0;
|
|
1946
|
+
}
|
|
1947
|
+
// A host may resolve without ever changing the
|
|
1948
|
+
// window (already on the oldest page). Any swap
|
|
1949
|
+
// commit runs its layout effect before the next
|
|
1950
|
+
// frame, so a flag still armed by then is the
|
|
1951
|
+
// no-change case — clear it, or a LATER prepend
|
|
1952
|
+
// would spuriously jump the reader to the top.
|
|
1953
|
+
requestFrame(() => {
|
|
1954
|
+
if (jumpToStartSeqRef.current === seq) {
|
|
1955
|
+
pendingJumpToStartRef.current = false;
|
|
1956
|
+
}
|
|
1957
|
+
});
|
|
1958
|
+
},
|
|
1959
|
+
() => {
|
|
1933
1960
|
if (jumpToStartSeqRef.current === seq) {
|
|
1934
1961
|
pendingJumpToStartRef.current = false;
|
|
1935
1962
|
}
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1963
|
+
},
|
|
1964
|
+
);
|
|
1965
|
+
}}
|
|
1966
|
+
className="pointer-events-auto rounded-full border border-og-border px-3 py-1.5 text-og-control"
|
|
1967
|
+
>
|
|
1968
|
+
{underfillRetryReady
|
|
1969
|
+
? underfillSettledAttempt?.[2]?.tailPreserved
|
|
1970
|
+
? "Load earlier activity"
|
|
1971
|
+
: "Retry earlier activity"
|
|
1972
|
+
: "Jump to start"}
|
|
1973
|
+
</button>
|
|
1974
|
+
) : null}
|
|
1975
|
+
</motion.div>
|
|
1976
|
+
) : null}
|
|
1977
|
+
</AnimatePresence>
|
|
1978
|
+
{!groups.length
|
|
1979
|
+
? (emptyState ?? (
|
|
1980
|
+
<p className="py-10 text-center text-og-menu text-og-fg-subtle">
|
|
1981
|
+
No activity yet.
|
|
1982
|
+
</p>
|
|
1983
|
+
))
|
|
1984
|
+
: null}
|
|
1985
|
+
{hasOlder && olderPrefetchArmed ? (
|
|
1986
|
+
// Overlaid, not a layout row: mounting/unmounting the sentinel
|
|
1987
|
+
// must never shift content (that shift was itself a wobble).
|
|
1988
|
+
// End at the scroll origin above the pt-16 gutter so the
|
|
1989
|
+
// observer and scrollTop cooldown share the same 400px band.
|
|
1990
|
+
<div
|
|
1991
|
+
ref={topSentinelRef}
|
|
1992
|
+
data-og-top-sentinel=""
|
|
1993
|
+
data-og-timeline-chrome=""
|
|
1994
|
+
aria-hidden="true"
|
|
1995
|
+
className="pointer-events-none absolute inset-x-0 -top-16 h-px -translate-y-full"
|
|
1996
|
+
/>
|
|
1951
1997
|
) : null}
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
data-og-timeline-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
groupKey={key}
|
|
1978
|
-
group={group}
|
|
1979
|
-
nextGroup={groups[index + 1]?.group}
|
|
1980
|
-
entranceEnabled={entranceEnabled}
|
|
1981
|
-
liveEntranceEnabled={
|
|
1982
|
-
group.kind === "activity" ? !bulkRender : undefined
|
|
1983
|
-
}
|
|
1984
|
-
context={timelineGroupEntryContext}
|
|
1998
|
+
{groups.map(({ group, key, entranceEnabled }, index) => {
|
|
1999
|
+
return (
|
|
2000
|
+
<TimelineGroupEntry
|
|
2001
|
+
key={key}
|
|
2002
|
+
groupKey={key}
|
|
2003
|
+
group={group}
|
|
2004
|
+
nextGroup={groups[index + 1]?.group}
|
|
2005
|
+
entranceEnabled={entranceEnabled}
|
|
2006
|
+
liveEntranceEnabled={
|
|
2007
|
+
group.kind === "activity" ? !bulkRender : undefined
|
|
2008
|
+
}
|
|
2009
|
+
context={timelineGroupEntryContext}
|
|
2010
|
+
/>
|
|
2011
|
+
);
|
|
2012
|
+
})}
|
|
2013
|
+
{groups.length > 0 && trailingState ? (
|
|
2014
|
+
<div data-og-timeline-trailing-state="">{trailingState}</div>
|
|
2015
|
+
) : null}
|
|
2016
|
+
{hasNewer ? (
|
|
2017
|
+
<div
|
|
2018
|
+
ref={bottomSentinelRef}
|
|
2019
|
+
data-og-bottom-sentinel=""
|
|
2020
|
+
data-og-timeline-chrome=""
|
|
2021
|
+
aria-hidden="true"
|
|
2022
|
+
className="h-px w-full"
|
|
1985
2023
|
/>
|
|
1986
|
-
)
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
/>
|
|
1999
|
-
) : null}
|
|
2000
|
-
</div>
|
|
2001
|
-
</TimelineBeforeLayout>
|
|
2002
|
-
</div>
|
|
2003
|
-
|
|
2004
|
-
<AnimatePresence>
|
|
2005
|
-
{loadingNewer ? (
|
|
2006
|
-
<motion.div
|
|
2007
|
-
initial={{ opacity: 0, y: 6 }}
|
|
2008
|
-
animate={{ opacity: 1, y: 0 }}
|
|
2009
|
-
exit={{ opacity: 0, y: 6 }}
|
|
2010
|
-
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2011
|
-
data-og-loading-newer=""
|
|
2012
|
-
aria-live="polite"
|
|
2013
|
-
className="pointer-events-none absolute inset-x-0 bottom-14 z-10 flex justify-center"
|
|
2014
|
-
>
|
|
2015
|
-
<span className={LOADING_CHIP_CLASS}>
|
|
2016
|
-
<span className="og-shimmer-text">Loading later activity…</span>
|
|
2017
|
-
</span>
|
|
2018
|
-
</motion.div>
|
|
2024
|
+
) : null}
|
|
2025
|
+
</div>
|
|
2026
|
+
</TimelineBeforeLayout>
|
|
2027
|
+
</div>
|
|
2028
|
+
{draftAnnotations && draftAnnotations.length > 0 ? (
|
|
2029
|
+
<Suspense fallback={null}>
|
|
2030
|
+
<TimelineAnnotationMarkers
|
|
2031
|
+
rootRef={scrollRef}
|
|
2032
|
+
annotations={draftAnnotations}
|
|
2033
|
+
onSelect={onDraftAnnotationSelect}
|
|
2034
|
+
/>
|
|
2035
|
+
</Suspense>
|
|
2019
2036
|
) : null}
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2037
|
+
|
|
2038
|
+
<AnimatePresence>
|
|
2039
|
+
{loadingNewer ? (
|
|
2040
|
+
<motion.div
|
|
2041
|
+
initial={{ opacity: 0, y: 6 }}
|
|
2042
|
+
animate={{ opacity: 1, y: 0 }}
|
|
2043
|
+
exit={{ opacity: 0, y: 6 }}
|
|
2044
|
+
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2045
|
+
data-og-loading-newer=""
|
|
2046
|
+
aria-live="polite"
|
|
2047
|
+
className="pointer-events-none absolute inset-x-0 bottom-14 z-10 flex justify-center"
|
|
2048
|
+
>
|
|
2049
|
+
<span className={LOADING_CHIP_CLASS}>
|
|
2050
|
+
<span className="og-shimmer-text">Loading later activity…</span>
|
|
2051
|
+
</span>
|
|
2052
|
+
</motion.div>
|
|
2053
|
+
) : null}
|
|
2054
|
+
</AnimatePresence>
|
|
2055
|
+
<AnimatePresence>
|
|
2056
|
+
{((!pinned && autoFollow) || hasNewer || canSkipTipCatchup) && autoFollow ? (
|
|
2057
|
+
<motion.button
|
|
2058
|
+
type="button"
|
|
2059
|
+
data-og-jump-to-latest=""
|
|
2060
|
+
initial={{ opacity: 0, y: 8 }}
|
|
2061
|
+
animate={{ opacity: 1, y: 0 }}
|
|
2062
|
+
exit={{ opacity: 0, y: 8 }}
|
|
2063
|
+
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2064
|
+
onClick={() => {
|
|
2065
|
+
disclosureKeepsUnpinnedRef.current = false;
|
|
2066
|
+
if (hasNewer) {
|
|
2067
|
+
// Do not pin against the current history page — its bottom
|
|
2068
|
+
// is not the tip. The pin + snap run when the tip window
|
|
2069
|
+
// actually lands (`hasNewer` flips false).
|
|
2070
|
+
wantPinRef.current = true;
|
|
2071
|
+
const node = scrollRef.current;
|
|
2072
|
+
if (onJumpToLatest) {
|
|
2073
|
+
void Promise.resolve(onJumpToLatest()).then(
|
|
2074
|
+
() => {
|
|
2075
|
+
// Covers a host that flipped hasNewer before
|
|
2076
|
+
// resolving; otherwise the tip-window commit
|
|
2077
|
+
// consumes the flag.
|
|
2078
|
+
const current = scrollRef.current;
|
|
2079
|
+
if (current && wantPinRef.current && !hasNewerRef.current) {
|
|
2080
|
+
wantPinRef.current = false;
|
|
2081
|
+
applyPinned(true);
|
|
2082
|
+
snapToBottom(current);
|
|
2083
|
+
}
|
|
2084
|
+
},
|
|
2085
|
+
() => {
|
|
2086
|
+
// The tip reload failed (ordinary network error):
|
|
2087
|
+
// an armed latch would fire a surprise snap when
|
|
2088
|
+
// the reader later pages to the tip themselves.
|
|
2046
2089
|
wantPinRef.current = false;
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
// window so the newer sentinel can page forward; the
|
|
2061
|
-
// latch pins if the tip window eventually lands.
|
|
2090
|
+
},
|
|
2091
|
+
);
|
|
2092
|
+
} else if (node) {
|
|
2093
|
+
// No tip reload available: jump within the in-memory
|
|
2094
|
+
// window so the newer sentinel can page forward; the
|
|
2095
|
+
// latch pins if the tip window eventually lands.
|
|
2096
|
+
snapToBottom(node);
|
|
2097
|
+
}
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
const node = scrollRef.current;
|
|
2101
|
+
if (node) {
|
|
2102
|
+
applyPinned(true);
|
|
2062
2103
|
snapToBottom(node);
|
|
2063
2104
|
}
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
<ArrowDownIcon className="size-3.5" />
|
|
2080
|
-
Jump to latest
|
|
2081
|
-
</motion.button>
|
|
2082
|
-
) : null}
|
|
2083
|
-
</AnimatePresence>
|
|
2084
|
-
</div>
|
|
2105
|
+
}}
|
|
2106
|
+
className={cn(
|
|
2107
|
+
"absolute inset-x-0 bottom-4 mx-auto w-fit",
|
|
2108
|
+
"inline-flex items-center gap-1.5 rounded-full border border-og-border bg-og-surface-3/90 px-3 py-1.5",
|
|
2109
|
+
"text-og-control font-medium text-og-fg shadow-og-md backdrop-blur",
|
|
2110
|
+
"hover:border-og-border-strong",
|
|
2111
|
+
)}
|
|
2112
|
+
>
|
|
2113
|
+
<ArrowDownIcon className="size-3.5" />
|
|
2114
|
+
Jump to latest
|
|
2115
|
+
</motion.button>
|
|
2116
|
+
) : null}
|
|
2117
|
+
</AnimatePresence>
|
|
2118
|
+
</div>
|
|
2119
|
+
</TimelineAnnotationSourceRootContext.Provider>
|
|
2085
2120
|
</TooltipProvider>
|
|
2086
2121
|
</EntranceAnimationProvider>
|
|
2087
2122
|
</TimelineComputeLabelProvider>
|
|
@@ -2269,6 +2304,7 @@ type TimelineGroupEntryContext = {
|
|
|
2269
2304
|
};
|
|
2270
2305
|
|
|
2271
2306
|
type TimelineGroupBehaviorProps = {
|
|
2307
|
+
renderMessageActions: MessageTimelineProps["renderMessageActions"];
|
|
2272
2308
|
renderMessageText: MessageTimelineProps["renderMessageText"];
|
|
2273
2309
|
onOpenSession: MessageTimelineProps["onOpenSession"];
|
|
2274
2310
|
onMemoryClick: MessageTimelineProps["onMemoryClick"];
|
|
@@ -2332,6 +2368,7 @@ const TimelineGroupEntry = memo(function TimelineGroupEntry({
|
|
|
2332
2368
|
// streaming and host updates still invalidate immediately.
|
|
2333
2369
|
const TimelineGroupView = memo(function TimelineGroupView({
|
|
2334
2370
|
group,
|
|
2371
|
+
renderMessageActions,
|
|
2335
2372
|
renderMessageText,
|
|
2336
2373
|
onOpenSession,
|
|
2337
2374
|
onMemoryClick,
|
|
@@ -2349,6 +2386,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2349
2386
|
contextCompactionCount,
|
|
2350
2387
|
}: {
|
|
2351
2388
|
group: TimelineGroup;
|
|
2389
|
+
renderMessageActions?: ((item: AgentMessageItem | UserMessageItem) => ReactNode) | undefined;
|
|
2352
2390
|
renderMessageText?:
|
|
2353
2391
|
| ((text: string, item: AgentMessageItem | UserMessageItem) => ReactNode)
|
|
2354
2392
|
| undefined;
|
|
@@ -2507,6 +2545,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2507
2545
|
key={key}
|
|
2508
2546
|
resetKeys={[
|
|
2509
2547
|
child,
|
|
2548
|
+
renderMessageActions,
|
|
2510
2549
|
renderMessageText,
|
|
2511
2550
|
onOpenSession,
|
|
2512
2551
|
onMemoryClick,
|
|
@@ -2521,6 +2560,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2521
2560
|
>
|
|
2522
2561
|
<TimelineGroupView
|
|
2523
2562
|
group={child}
|
|
2563
|
+
renderMessageActions={renderMessageActions}
|
|
2524
2564
|
renderMessageText={renderMessageText}
|
|
2525
2565
|
onOpenSession={onOpenSession}
|
|
2526
2566
|
onMemoryClick={onMemoryClick}
|
|
@@ -2567,6 +2607,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2567
2607
|
return (
|
|
2568
2608
|
<TimelineRow
|
|
2569
2609
|
item={group.item}
|
|
2610
|
+
renderMessageActions={renderMessageActions}
|
|
2570
2611
|
renderMessageText={renderMessageText}
|
|
2571
2612
|
onReconnect={onReconnect}
|
|
2572
2613
|
resolveProviderLogo={resolveProviderLogo}
|
|
@@ -2818,6 +2859,7 @@ function durationBetween(startedAt: string, endedAt: string): number | undefined
|
|
|
2818
2859
|
*/
|
|
2819
2860
|
export function TimelineRow({
|
|
2820
2861
|
item,
|
|
2862
|
+
renderMessageActions,
|
|
2821
2863
|
renderMessageText,
|
|
2822
2864
|
onReconnect,
|
|
2823
2865
|
resolveProviderLogo,
|
|
@@ -2825,6 +2867,7 @@ export function TimelineRow({
|
|
|
2825
2867
|
loadVideoArtifactPlayback,
|
|
2826
2868
|
}: {
|
|
2827
2869
|
item: TimelineItem;
|
|
2870
|
+
renderMessageActions?: ((item: AgentMessageItem | UserMessageItem) => ReactNode) | undefined;
|
|
2828
2871
|
renderMessageText?:
|
|
2829
2872
|
| ((text: string, item: AgentMessageItem | UserMessageItem) => ReactNode)
|
|
2830
2873
|
| undefined;
|
|
@@ -2835,11 +2878,23 @@ export function TimelineRow({
|
|
|
2835
2878
|
}) {
|
|
2836
2879
|
switch (item.kind) {
|
|
2837
2880
|
case "user-message":
|
|
2838
|
-
return
|
|
2881
|
+
return (
|
|
2882
|
+
<UserMessageRow
|
|
2883
|
+
item={item}
|
|
2884
|
+
renderMessageActions={renderMessageActions}
|
|
2885
|
+
renderMessageText={renderMessageText}
|
|
2886
|
+
/>
|
|
2887
|
+
);
|
|
2839
2888
|
case "human-input":
|
|
2840
2889
|
return <HumanInputConversationRow item={item} />;
|
|
2841
2890
|
case "agent-message":
|
|
2842
|
-
return
|
|
2891
|
+
return (
|
|
2892
|
+
<AgentMessageRow
|
|
2893
|
+
item={item}
|
|
2894
|
+
renderMessageActions={renderMessageActions}
|
|
2895
|
+
renderMessageText={renderMessageText}
|
|
2896
|
+
/>
|
|
2897
|
+
);
|
|
2843
2898
|
case "worker-completion":
|
|
2844
2899
|
return <WorkerCompletionRow item={item} onOpenSession={onOpenSession} />;
|
|
2845
2900
|
case "session-status":
|
|
@@ -2965,9 +3020,11 @@ function MessageFooterTime({ occurredAt }: { occurredAt: string }) {
|
|
|
2965
3020
|
|
|
2966
3021
|
function UserMessageRow({
|
|
2967
3022
|
item,
|
|
3023
|
+
renderMessageActions,
|
|
2968
3024
|
renderMessageText,
|
|
2969
3025
|
}: {
|
|
2970
3026
|
item: UserMessageItem;
|
|
3027
|
+
renderMessageActions?: ((item: AgentMessageItem | UserMessageItem) => ReactNode) | undefined;
|
|
2971
3028
|
renderMessageText?:
|
|
2972
3029
|
| ((text: string, item: AgentMessageItem | UserMessageItem) => ReactNode)
|
|
2973
3030
|
| undefined;
|
|
@@ -2986,7 +3043,12 @@ function UserMessageRow({
|
|
|
2986
3043
|
}
|
|
2987
3044
|
label="Copy message"
|
|
2988
3045
|
className="w-fit max-w-full min-w-0"
|
|
2989
|
-
trailing={
|
|
3046
|
+
trailing={
|
|
3047
|
+
<>
|
|
3048
|
+
{renderMessageActions?.(item)}
|
|
3049
|
+
<MessageFooterTime occurredAt={item.occurredAt} />
|
|
3050
|
+
</>
|
|
3051
|
+
}
|
|
2990
3052
|
>
|
|
2991
3053
|
<div className={MESSAGE_BUBBLE_CLASS}>
|
|
2992
3054
|
{item.text ? (
|
|
@@ -3001,7 +3063,7 @@ function UserMessageRow({
|
|
|
3001
3063
|
</div>
|
|
3002
3064
|
) : null}
|
|
3003
3065
|
{(item.annotations?.length ?? 0) > 0 ? (
|
|
3004
|
-
<
|
|
3066
|
+
<TimelineAnnotationCards
|
|
3005
3067
|
annotations={item.annotations ?? []}
|
|
3006
3068
|
className={item.text ? "mt-2" : undefined}
|
|
3007
3069
|
/>
|
|
@@ -3177,9 +3239,11 @@ function humanInputConversationCopyText(item: HumanInputItem, settledLabel: stri
|
|
|
3177
3239
|
|
|
3178
3240
|
function AgentMessageRow({
|
|
3179
3241
|
item,
|
|
3242
|
+
renderMessageActions,
|
|
3180
3243
|
renderMessageText,
|
|
3181
3244
|
}: {
|
|
3182
3245
|
item: AgentMessageItem;
|
|
3246
|
+
renderMessageActions?: ((item: AgentMessageItem | UserMessageItem) => ReactNode) | undefined;
|
|
3183
3247
|
renderMessageText?:
|
|
3184
3248
|
| ((text: string, item: AgentMessageItem | UserMessageItem) => ReactNode)
|
|
3185
3249
|
| undefined;
|
|
@@ -3195,17 +3259,22 @@ function AgentMessageRow({
|
|
|
3195
3259
|
// While streaming, copy is still useful (current text) but keep chrome calm —
|
|
3196
3260
|
// stamp only after the message finishes (occurredAt tracks completion).
|
|
3197
3261
|
return (
|
|
3198
|
-
<
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3262
|
+
<CopyHoverFrame
|
|
3263
|
+
copyText={item.text}
|
|
3264
|
+
label="Copy message"
|
|
3265
|
+
align="start"
|
|
3266
|
+
className={cn(enter && "animate-og-enter", "min-w-0 text-og-md leading-7 text-og-fg")}
|
|
3267
|
+
trailing={
|
|
3268
|
+
item.streaming ? null : (
|
|
3269
|
+
<>
|
|
3270
|
+
{renderMessageActions?.(item)}
|
|
3271
|
+
<MessageFooterTime occurredAt={item.occurredAt} />
|
|
3272
|
+
</>
|
|
3273
|
+
)
|
|
3274
|
+
}
|
|
3275
|
+
>
|
|
3276
|
+
<div data-og-annotation-source-key={item.annotationSource?.eventId}>{body}</div>
|
|
3277
|
+
</CopyHoverFrame>
|
|
3209
3278
|
);
|
|
3210
3279
|
}
|
|
3211
3280
|
|
|
@@ -3642,6 +3711,32 @@ function formatVideoDuration(seconds: number): string {
|
|
|
3642
3711
|
|
|
3643
3712
|
function NoticeRow({ item }: { item: NoticeItem }) {
|
|
3644
3713
|
const enter = useEntranceAnimation();
|
|
3714
|
+
if (item.recordedOutcome) {
|
|
3715
|
+
return (
|
|
3716
|
+
<details
|
|
3717
|
+
className="group text-og-sm text-og-fg-muted"
|
|
3718
|
+
role="note"
|
|
3719
|
+
data-og-recorded-outcome="wait"
|
|
3720
|
+
>
|
|
3721
|
+
<summary className="flex cursor-pointer list-none items-center gap-2 py-1 [&::-webkit-details-marker]:hidden">
|
|
3722
|
+
<ChevronRightIcon
|
|
3723
|
+
aria-hidden
|
|
3724
|
+
className="size-3.5 transition-transform group-open:rotate-90"
|
|
3725
|
+
/>
|
|
3726
|
+
<span>
|
|
3727
|
+
Wait recorded ·{" "}
|
|
3728
|
+
<time dateTime={item.occurredAt}>
|
|
3729
|
+
{new Date(item.occurredAt).toLocaleString(undefined, {
|
|
3730
|
+
dateStyle: "medium",
|
|
3731
|
+
timeStyle: "short",
|
|
3732
|
+
})}
|
|
3733
|
+
</time>
|
|
3734
|
+
</span>
|
|
3735
|
+
</summary>
|
|
3736
|
+
<p className="mt-1 whitespace-pre-wrap break-words pl-5 text-og-fg-muted">{item.text}</p>
|
|
3737
|
+
</details>
|
|
3738
|
+
);
|
|
3739
|
+
}
|
|
3645
3740
|
const tone =
|
|
3646
3741
|
item.tone === "failed"
|
|
3647
3742
|
? "border-og-status-failed/35 bg-og-status-failed/10 text-og-status-failed"
|