@opengeni/react 7.4.0-canary.36855269964001 → 7.4.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 +3 -48
- package/dist/artifacts-document.js +2 -3
- package/dist/artifacts-presentation.js +2 -3
- package/dist/artifacts-spreadsheet.js +2 -2
- package/dist/artifacts.d.ts +0 -1
- package/dist/artifacts.js +1402 -505
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-R6KK5VKR.js → browser-viewer-2E6R2Y4W.js} +4 -4
- package/dist/{chunk-WUISZ3WU.js → chunk-3E2AITTZ.js} +28 -32
- package/dist/{chunk-WUISZ3WU.js.map → chunk-3E2AITTZ.js.map} +1 -1
- package/dist/{chunk-PWUILT2U.js → chunk-5AZSK6MV.js} +2 -2
- package/dist/{chunk-Y2PRMYLC.js → chunk-DUEBTL3N.js} +3 -4
- package/dist/{chunk-Y2PRMYLC.js.map → chunk-DUEBTL3N.js.map} +1 -1
- package/dist/{chunk-7UAMYXJY.js → chunk-KNDIXZN6.js} +18 -25
- package/dist/{chunk-7UAMYXJY.js.map → chunk-KNDIXZN6.js.map} +1 -1
- package/dist/{chunk-P4G6K2E2.js → chunk-L4OSXQ6H.js} +884 -1119
- package/dist/chunk-L4OSXQ6H.js.map +1 -0
- package/dist/{chunk-LLIH7Q4B.js → chunk-S5JQ4R22.js} +19 -47
- package/dist/chunk-S5JQ4R22.js.map +1 -0
- package/dist/{chunk-A4UJB2MM.js → chunk-SEEVCT36.js} +2 -2
- package/dist/{chunk-UXJCQ5GH.js → chunk-UDD6DLLT.js} +1 -6
- package/dist/chunk-UDD6DLLT.js.map +1 -0
- package/dist/chunk-UGMKPUEM.js +299 -0
- package/dist/chunk-UGMKPUEM.js.map +1 -0
- package/dist/{chunk-ZCZHKJKZ.js → chunk-USXVQACU.js} +3 -3
- package/dist/{chunk-VABBF52G.js → chunk-V2ABIVYJ.js} +6 -70
- package/dist/chunk-V2ABIVYJ.js.map +1 -0
- package/dist/{chunk-TVO6GXR6.js → chunk-X4HIN6TV.js} +162 -71
- package/dist/chunk-X4HIN6TV.js.map +1 -0
- package/dist/{chunk-IIDEIBMC.js → chunk-XEJEOAOV.js} +38 -49
- package/dist/chunk-XEJEOAOV.js.map +1 -0
- package/dist/{chunk-ZKLCPBWN.js → chunk-Y3QTZN2F.js} +5 -5
- package/dist/{chunk-73BUNGLF.js → chunk-YZ3YYMOM.js} +1 -1
- package/dist/components/artifacts/editable-artifact-ui.d.ts +1 -2
- package/dist/components/artifacts/index.d.ts +0 -7
- package/dist/components/browser-select-control.d.ts +1 -3
- package/dist/components/message-timeline.d.ts +1 -18
- package/dist/components/open-geni-links.d.ts +0 -16
- package/dist/components/session-conversation.d.ts +1 -20
- package/dist/composer.js +3 -3
- package/dist/{computer-viewer-DJHPSP2J.js → computer-viewer-WMJE6OA3.js} +3 -3
- package/dist/connect.js +6 -7
- package/dist/connect.js.map +1 -1
- package/dist/hooks/latest-question.d.ts +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.js +28 -39
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +4 -4
- package/dist/{latest-question-KGCDK3XX.js → latest-question-QB4EUCW5.js} +3 -3
- package/dist/lib/format.d.ts +0 -3
- package/dist/machines.js +2 -2
- package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-PFUEZ2U4.js} +5 -37
- package/dist/markdown-table-layout-PFUEZ2U4.js.map +1 -0
- package/dist/{sandbox-files-4PMXDSDV.js → sandbox-files-PXPIZAFG.js} +2 -2
- package/dist/session-ui.d.ts +2 -5
- package/dist/session-ui.js +13 -23
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +3 -3
- package/dist/sites.js +289 -4
- package/dist/sites.js.map +1 -1
- package/dist/timeline/types.d.ts +1 -7
- package/dist/{timeline-annotation-selection-DMLMM2TX.js → timeline-annotation-selection-ODLVF4OI.js} +1 -6
- package/dist/timeline-annotation-selection-ODLVF4OI.js.map +1 -0
- package/package.json +3 -7
- package/src/artifacts.ts +0 -37
- package/src/components/artifacts/editable-artifact-ui.tsx +1 -3
- package/src/components/artifacts/editable-artifact-workbench.tsx +2 -5
- package/src/components/artifacts/editable-document.tsx +36 -34
- package/src/components/artifacts/editable-presentation.tsx +16 -21
- package/src/components/artifacts/index.ts +0 -41
- package/src/components/browser-select-control.tsx +1 -4
- package/src/components/browser-viewer.tsx +22 -81
- package/src/components/computer-viewer.tsx +23 -51
- package/src/components/markdown-table-layout.ts +5 -49
- package/src/components/markdown.tsx +2 -5
- package/src/components/message-timeline.tsx +434 -515
- package/src/components/open-geni-links.tsx +0 -24
- package/src/components/session-conversation.tsx +20 -131
- package/src/components/timeline-annotation-selection.tsx +0 -7
- package/src/hooks/use-available-models.ts +1 -1
- package/src/index.ts +0 -9
- package/src/lib/format.ts +0 -13
- package/src/session-ui.ts +0 -9
- package/src/timeline/projection.ts +4 -78
- package/src/timeline/types.ts +0 -7
- package/styles/compiled.css +0 -325
- package/dist/chat-interactive-block-VAGABN7J.js +0 -11
- package/dist/chunk-5CVXZA3Y.js +0 -292
- package/dist/chunk-5CVXZA3Y.js.map +0 -1
- package/dist/chunk-B3V6TG77.js +0 -41
- package/dist/chunk-B3V6TG77.js.map +0 -1
- package/dist/chunk-IIDEIBMC.js.map +0 -1
- package/dist/chunk-LLIH7Q4B.js.map +0 -1
- package/dist/chunk-P4G6K2E2.js.map +0 -1
- package/dist/chunk-RAIQYITC.js +0 -1772
- package/dist/chunk-RAIQYITC.js.map +0 -1
- package/dist/chunk-SCOTPE2I.js +0 -589
- package/dist/chunk-SCOTPE2I.js.map +0 -1
- package/dist/chunk-TVO6GXR6.js.map +0 -1
- package/dist/chunk-UXJCQ5GH.js.map +0 -1
- package/dist/chunk-VABBF52G.js.map +0 -1
- package/dist/chunk-WEHMPOXK.js +0 -39
- package/dist/chunk-WEHMPOXK.js.map +0 -1
- package/dist/chunk-XCHWDJ22.js +0 -107
- package/dist/chunk-XCHWDJ22.js.map +0 -1
- package/dist/components/artifacts/artifact-chrome.d.ts +0 -120
- package/dist/components/artifacts/artifact-sandbox.d.ts +0 -27
- package/dist/components/artifacts/chat-interactive-block.d.ts +0 -29
- package/dist/components/artifacts/deferred-chat-media.d.ts +0 -15
- package/dist/components/artifacts/editable-artifact-view.d.ts +0 -46
- package/dist/components/artifacts/latest-task-runner.d.ts +0 -15
- package/dist/components/artifacts/session-artifact-viewer.d.ts +0 -39
- package/dist/components/artifacts/site-view.d.ts +0 -23
- package/dist/components/timeline-pill-geometry.d.ts +0 -6
- package/dist/components/timeline-pill-placement.d.ts +0 -18
- package/dist/components/usage-limit-notice.d.ts +0 -49
- package/dist/components/usage-member-list.d.ts +0 -91
- package/dist/components/usage-meter.d.ts +0 -73
- package/dist/hooks/use-usage.d.ts +0 -40
- package/dist/markdown-table-layout-QXSLK4BY.js.map +0 -1
- package/dist/sandbox-files-4PMXDSDV.js.map +0 -1
- package/dist/timeline/allowance-exhausted-row.d.ts +0 -24
- package/dist/timeline-annotation-selection-DMLMM2TX.js.map +0 -1
- package/dist/timeline-pill-geometry-2G6EK47V.js +0 -73
- package/dist/timeline-pill-geometry-2G6EK47V.js.map +0 -1
- package/dist/usage/allowance-copy.d.ts +0 -25
- package/dist/usage/summary.d.ts +0 -35
- package/dist/usage.d.ts +0 -13
- package/dist/usage.js +0 -1073
- package/dist/usage.js.map +0 -1
- package/src/components/artifacts/artifact-chrome.tsx +0 -413
- package/src/components/artifacts/artifact-sandbox.tsx +0 -185
- package/src/components/artifacts/chat-interactive-block.tsx +0 -260
- package/src/components/artifacts/deferred-chat-media.tsx +0 -67
- package/src/components/artifacts/editable-artifact-view.tsx +0 -187
- package/src/components/artifacts/latest-task-runner.ts +0 -49
- package/src/components/artifacts/session-artifact-viewer.tsx +0 -376
- package/src/components/artifacts/site-view.tsx +0 -116
- package/src/components/timeline-pill-geometry.ts +0 -112
- package/src/components/timeline-pill-placement.ts +0 -81
- package/src/components/usage-limit-notice.tsx +0 -179
- package/src/components/usage-member-list.tsx +0 -792
- package/src/components/usage-meter.tsx +0 -382
- package/src/hooks/use-usage.ts +0 -100
- package/src/timeline/allowance-exhausted-row.tsx +0 -86
- package/src/usage/allowance-copy.ts +0 -64
- package/src/usage/summary.ts +0 -93
- package/src/usage.ts +0 -50
- /package/dist/{browser-viewer-R6KK5VKR.js.map → browser-viewer-2E6R2Y4W.js.map} +0 -0
- /package/dist/{chunk-PWUILT2U.js.map → chunk-5AZSK6MV.js.map} +0 -0
- /package/dist/{chunk-A4UJB2MM.js.map → chunk-SEEVCT36.js.map} +0 -0
- /package/dist/{chunk-ZCZHKJKZ.js.map → chunk-USXVQACU.js.map} +0 -0
- /package/dist/{chunk-ZKLCPBWN.js.map → chunk-Y3QTZN2F.js.map} +0 -0
- /package/dist/{chunk-73BUNGLF.js.map → chunk-YZ3YYMOM.js.map} +0 -0
- /package/dist/{chat-interactive-block-VAGABN7J.js.map → computer-viewer-WMJE6OA3.js.map} +0 -0
- /package/dist/{latest-question-KGCDK3XX.js.map → latest-question-QB4EUCW5.js.map} +0 -0
- /package/dist/{computer-viewer-DJHPSP2J.js.map → sandbox-files-PXPIZAFG.js.map} +0 -0
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
type OlderHistoryLoadReceipt,
|
|
64
64
|
type OlderHistoryLoader,
|
|
65
65
|
} from "../older-history";
|
|
66
|
-
import { Markdown
|
|
66
|
+
import { Markdown } from "./markdown";
|
|
67
67
|
import { OpenGeniLinkProvider, type OpenGeniLinkResolver } from "./open-geni-links";
|
|
68
68
|
import {
|
|
69
69
|
TimelineBeforeLayout,
|
|
@@ -73,7 +73,6 @@ import {
|
|
|
73
73
|
type TimelineAnchor,
|
|
74
74
|
} from "./timeline-anchor";
|
|
75
75
|
import { useReadingProgress } from "./timeline-reading-progress";
|
|
76
|
-
import { type TimelinePillPlacement, useTimelinePillPlacement } from "./timeline-pill-placement";
|
|
77
76
|
import {
|
|
78
77
|
animateTimelineSettlement,
|
|
79
78
|
captureTimelineSettlement,
|
|
@@ -143,12 +142,6 @@ import {
|
|
|
143
142
|
import { SESSION_STATUS_META, StatusDot } from "./session-status";
|
|
144
143
|
import { TimelineComputeLabelProvider } from "../timeline/compute-label";
|
|
145
144
|
import { EntranceAnimationProvider, useEntranceAnimation } from "../timeline/entrance";
|
|
146
|
-
import {
|
|
147
|
-
AllowanceExhaustedRow,
|
|
148
|
-
AllowancePresentationProvider,
|
|
149
|
-
type RenderAllowanceExhausted,
|
|
150
|
-
} from "../timeline/allowance-exhausted-row";
|
|
151
|
-
import type { AllowanceLabels } from "../usage/allowance-copy";
|
|
152
145
|
import { SeenActivityIdsProvider } from "../timeline/seen-activity-ids";
|
|
153
146
|
import { TimelineAnnotationCards } from "./timeline-annotations";
|
|
154
147
|
import { TooltipProvider } from "./tooltip";
|
|
@@ -177,11 +170,6 @@ export type MessageTimelineProps = {
|
|
|
177
170
|
context: { searchTarget: TimelineSearchTarget | null },
|
|
178
171
|
) => ReactNode)
|
|
179
172
|
| undefined;
|
|
180
|
-
/**
|
|
181
|
-
* Render assistant `opengeni-html` / `opengeni-site` fences in the default
|
|
182
|
-
* message renderer (`SessionConversation` supplies the inline Site preview).
|
|
183
|
-
*/
|
|
184
|
-
renderInteractiveBlock?: MarkdownProps["renderInteractiveBlock"];
|
|
185
173
|
/** Drill into a spawned worker session. */
|
|
186
174
|
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
187
175
|
/**
|
|
@@ -203,15 +191,6 @@ export type MessageTimelineProps = {
|
|
|
203
191
|
onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
|
|
204
192
|
/** Host-owned inline connection setup. Return undefined to use the default recovery card. */
|
|
205
193
|
renderAuthNeeded?: ((item: AuthNeededItem) => ReactNode | undefined) | undefined;
|
|
206
|
-
/**
|
|
207
|
-
* Replace the "usage limit reached" row shown when a workspace or member
|
|
208
|
-
* usage allowance stops work (an `allowance_exhausted` refusal). Receives the
|
|
209
|
-
* typed refusal (scope, reset time) and the default row; return undefined to
|
|
210
|
-
* keep the default. Use it to point people at your own plan or admin page.
|
|
211
|
-
*/
|
|
212
|
-
renderAllowanceExhausted?: RenderAllowanceExhausted | undefined;
|
|
213
|
-
/** Replace the words of the default "usage limit reached" row. */
|
|
214
|
-
allowanceExhaustedLabels?: Partial<AllowanceLabels> | undefined;
|
|
215
194
|
/**
|
|
216
195
|
* Decide which durable authentication notices this timeline presents.
|
|
217
196
|
* Defaults to showing every notice. Embedded hosts can suppress notices for
|
|
@@ -332,20 +311,6 @@ const PIN_THRESHOLD_PX = 48;
|
|
|
332
311
|
const JUMP_TO_LATEST_CATCHUP_DEBT_PX = 240;
|
|
333
312
|
/** Breathing room above the question when following stops at an answer. */
|
|
334
313
|
const QUESTION_NAV_MARGIN_PX = 12;
|
|
335
|
-
// Floating pills rest at these spots and slide sideways, in this order, when
|
|
336
|
-
// their resting spot would cover a control (see useTimelinePillPlacement).
|
|
337
|
-
const JUMP_PILL_PLACEMENTS: readonly TimelinePillPlacement[] = ["center", "end", "start"];
|
|
338
|
-
const QUESTION_PILL_PLACEMENTS: readonly TimelinePillPlacement[] = ["end", "center", "start"];
|
|
339
|
-
const JUMP_PILL_PLACEMENT_CLASS: Record<TimelinePillPlacement, string> = {
|
|
340
|
-
center: "inset-x-0 mx-auto",
|
|
341
|
-
end: "right-4 sm:right-6",
|
|
342
|
-
start: "left-4 sm:left-6",
|
|
343
|
-
};
|
|
344
|
-
const QUESTION_PILL_PLACEMENT_CLASS: Record<TimelinePillPlacement, string> = {
|
|
345
|
-
center: "justify-center",
|
|
346
|
-
end: "justify-end",
|
|
347
|
-
start: "justify-start",
|
|
348
|
-
};
|
|
349
314
|
/**
|
|
350
315
|
* Prefetch older history when the top sentinel is this far from the viewport.
|
|
351
316
|
* After a page loads we stay cool until the reader leaves this band (scrolls
|
|
@@ -517,7 +482,6 @@ function cssEscapeAttribute(value: string): string {
|
|
|
517
482
|
*/
|
|
518
483
|
export function MessageTimeline({
|
|
519
484
|
resolveLink,
|
|
520
|
-
renderInteractiveBlock,
|
|
521
485
|
userMessageDisclosureLabels,
|
|
522
486
|
searchTarget,
|
|
523
487
|
events,
|
|
@@ -529,8 +493,6 @@ export function MessageTimeline({
|
|
|
529
493
|
onMemoryClick,
|
|
530
494
|
onReconnect,
|
|
531
495
|
renderAuthNeeded,
|
|
532
|
-
renderAllowanceExhausted,
|
|
533
|
-
allowanceExhaustedLabels,
|
|
534
496
|
shouldRenderAuthNeeded,
|
|
535
497
|
resolveProviderLogo,
|
|
536
498
|
toolRegistry = defaultToolRegistry,
|
|
@@ -593,9 +555,6 @@ export function MessageTimeline({
|
|
|
593
555
|
<Markdown
|
|
594
556
|
searchTarget={searchItem?.id === item.id ? searchTarget : null}
|
|
595
557
|
streaming={item.kind === "agent-message" && item.streaming}
|
|
596
|
-
renderInteractiveBlock={
|
|
597
|
-
item.kind === "agent-message" ? renderInteractiveBlock : undefined
|
|
598
|
-
}
|
|
599
558
|
>
|
|
600
559
|
{text}
|
|
601
560
|
</Markdown>
|
|
@@ -608,7 +567,7 @@ export function MessageTimeline({
|
|
|
608
567
|
body
|
|
609
568
|
);
|
|
610
569
|
},
|
|
611
|
-
[
|
|
570
|
+
[renderMessageText, searchItem?.id, searchTarget],
|
|
612
571
|
);
|
|
613
572
|
// Event-window identity is independent of projected rows (partial messages
|
|
614
573
|
// can acquire a different first-delta id when older text arrives).
|
|
@@ -741,21 +700,6 @@ export function MessageTimeline({
|
|
|
741
700
|
const resizeFollowRafRef = useRef<number | null>(null);
|
|
742
701
|
const questionNavFrameRef = useRef<number | null>(null);
|
|
743
702
|
const [questionNav, setQuestionNav] = useState<QuestionNav | null>(null);
|
|
744
|
-
const jumpPillRef = useRef<HTMLButtonElement | null>(null);
|
|
745
|
-
const questionPillRef = useRef<HTMLDivElement | null>(null);
|
|
746
|
-
const jumpPillShown = autoFollow && (!pinned || hasNewer || canSkipTipCatchup);
|
|
747
|
-
const jumpPillPlacement = useTimelinePillPlacement({
|
|
748
|
-
pillRef: jumpPillRef,
|
|
749
|
-
scrollerRef: scrollRef,
|
|
750
|
-
active: jumpPillShown,
|
|
751
|
-
preference: JUMP_PILL_PLACEMENTS,
|
|
752
|
-
});
|
|
753
|
-
const questionPillPlacement = useTimelinePillPlacement({
|
|
754
|
-
pillRef: questionPillRef,
|
|
755
|
-
scrollerRef: scrollRef,
|
|
756
|
-
active: questionNav !== null,
|
|
757
|
-
preference: QUESTION_PILL_PLACEMENTS,
|
|
758
|
-
});
|
|
759
703
|
const [questionTarget, setQuestionTarget] = useState<number | null>(null);
|
|
760
704
|
const [questionPending, setQuestionPending] = useState(false);
|
|
761
705
|
const [questionError, setQuestionError] = useState<string | null>(null);
|
|
@@ -2243,484 +2187,460 @@ export function MessageTimeline({
|
|
|
2243
2187
|
};
|
|
2244
2188
|
|
|
2245
2189
|
const timeline = (
|
|
2246
|
-
<
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
/>
|
|
2274
|
-
</Suspense>
|
|
2275
|
-
) : null}
|
|
2276
|
-
{/* Pinned: anchoring off so the tip-follow camera owns the motion.
|
|
2190
|
+
<LightboxProvider>
|
|
2191
|
+
<FoldMemoryProvider value={foldMemoryRef.current}>
|
|
2192
|
+
<SeenActivityIdsProvider value={seenActivityIdsRef.current}>
|
|
2193
|
+
<TimelineComputeLabelProvider value={computeLabel ?? null}>
|
|
2194
|
+
<EntranceAnimationProvider value={false}>
|
|
2195
|
+
<TooltipProvider delayDuration={400}>
|
|
2196
|
+
<TimelineAnnotationSourceRootContext.Provider value={scrollRef}>
|
|
2197
|
+
<div
|
|
2198
|
+
className={cn("og-root relative flex min-h-0 flex-col", className)}
|
|
2199
|
+
// Sticky insets start at the scroller's padded content edge
|
|
2200
|
+
// (pt-16). Subtract exactly that padding so an expanded work
|
|
2201
|
+
// header pins flush to the scrollport. Pinning it lower left a
|
|
2202
|
+
// band of scrolling rows visible above the header, which then
|
|
2203
|
+
// looked like it floated over the middle of the timeline. The
|
|
2204
|
+
// floating question action sits below this strip instead.
|
|
2205
|
+
style={{ "--og-work-header-top": "-4rem" } as CSSProperties}
|
|
2206
|
+
>
|
|
2207
|
+
{onAnnotate ? (
|
|
2208
|
+
<Suspense fallback={null}>
|
|
2209
|
+
<TimelineAnnotationSelection
|
|
2210
|
+
rootRef={scrollRef}
|
|
2211
|
+
sources={annotationSources}
|
|
2212
|
+
onAnnotate={onAnnotate}
|
|
2213
|
+
/>
|
|
2214
|
+
</Suspense>
|
|
2215
|
+
) : null}
|
|
2216
|
+
{/* Pinned: anchoring off so the tip-follow camera owns the motion.
|
|
2277
2217
|
Unpinned: native scroll anchoring holds the reader's place. */}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
touchPositionRef.current = null;
|
|
2298
|
-
return;
|
|
2299
|
-
}
|
|
2300
|
-
const deltaX = previous.x - touch.clientX;
|
|
2301
|
-
const deltaY = previous.y - touch.clientY;
|
|
2302
|
-
if (Math.max(Math.abs(deltaX), Math.abs(deltaY)) < 4) return;
|
|
2303
|
-
touchPositionRef.current = { x: touch.clientX, y: touch.clientY };
|
|
2304
|
-
onWheel({
|
|
2305
|
-
deltaX,
|
|
2306
|
-
deltaY,
|
|
2307
|
-
target: event.target,
|
|
2308
|
-
currentTarget: event.currentTarget,
|
|
2309
|
-
});
|
|
2310
|
-
}}
|
|
2311
|
-
onTouchEnd={() => {
|
|
2312
|
-
touchPositionRef.current = null;
|
|
2313
|
-
}}
|
|
2314
|
-
onTouchCancel={() => {
|
|
2218
|
+
<div
|
|
2219
|
+
ref={scrollRef}
|
|
2220
|
+
data-og-timeline-scroller=""
|
|
2221
|
+
data-og-bottom-follow={autoFollow && pinned && !hasNewer ? "true" : "false"}
|
|
2222
|
+
tabIndex={-1}
|
|
2223
|
+
onScroll={onScroll}
|
|
2224
|
+
onScrollEnd={onScrollEnd}
|
|
2225
|
+
onWheel={onWheel}
|
|
2226
|
+
onTouchStart={(event) => {
|
|
2227
|
+
stopSettlement();
|
|
2228
|
+
const touch = event.touches.length === 1 ? event.touches[0] : undefined;
|
|
2229
|
+
touchPositionRef.current = touch
|
|
2230
|
+
? { x: touch.clientX, y: touch.clientY }
|
|
2231
|
+
: null;
|
|
2232
|
+
}}
|
|
2233
|
+
onTouchMove={(event) => {
|
|
2234
|
+
const touch = event.touches[0];
|
|
2235
|
+
const previous = touchPositionRef.current;
|
|
2236
|
+
if (!touch || !previous || event.touches.length !== 1) {
|
|
2315
2237
|
touchPositionRef.current = null;
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
const deltaX = previous.x - touch.clientX;
|
|
2241
|
+
const deltaY = previous.y - touch.clientY;
|
|
2242
|
+
if (Math.max(Math.abs(deltaX), Math.abs(deltaY)) < 4) return;
|
|
2243
|
+
touchPositionRef.current = { x: touch.clientX, y: touch.clientY };
|
|
2244
|
+
onWheel({
|
|
2245
|
+
deltaX,
|
|
2246
|
+
deltaY,
|
|
2247
|
+
target: event.target,
|
|
2248
|
+
currentTarget: event.currentTarget,
|
|
2249
|
+
});
|
|
2250
|
+
}}
|
|
2251
|
+
onTouchEnd={() => {
|
|
2252
|
+
touchPositionRef.current = null;
|
|
2253
|
+
}}
|
|
2254
|
+
onTouchCancel={() => {
|
|
2255
|
+
touchPositionRef.current = null;
|
|
2256
|
+
}}
|
|
2257
|
+
onClickCapture={(event) => {
|
|
2258
|
+
const target =
|
|
2259
|
+
event.target instanceof Element
|
|
2260
|
+
? event.target.closest("button[aria-expanded]")
|
|
2261
|
+
: null;
|
|
2262
|
+
if (target) {
|
|
2263
|
+
if (target.hasAttribute("data-og-work-header")) {
|
|
2264
|
+
releaseProgress(
|
|
2265
|
+
target
|
|
2266
|
+
.closest("[data-og-group-key]")
|
|
2267
|
+
?.getAttribute("data-og-group-key") ?? null,
|
|
2268
|
+
);
|
|
2332
2269
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2270
|
+
releasePinFromReader();
|
|
2271
|
+
disclosureKeepsUnpinnedRef.current = true;
|
|
2272
|
+
}
|
|
2273
|
+
}}
|
|
2274
|
+
onFocusCapture={(event) => {
|
|
2275
|
+
if (readableTurns && event.target !== event.currentTarget) {
|
|
2276
|
+
stopSettlement();
|
|
2277
|
+
releasePinFromReader();
|
|
2278
|
+
disclosureKeepsUnpinnedRef.current = true;
|
|
2279
|
+
}
|
|
2280
|
+
}}
|
|
2281
|
+
onPointerDown={onPointerDown}
|
|
2282
|
+
onKeyDown={onKeyDown}
|
|
2283
|
+
style={groups.length > 0 && !revealed ? { visibility: "hidden" } : undefined}
|
|
2284
|
+
className={cn(
|
|
2285
|
+
// tabIndex=-1 is programmatic only — never paint a focus ring on
|
|
2286
|
+
// the whole scroller (click + Shift used to flash a blue outline).
|
|
2287
|
+
"min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 pt-16 pb-6 sm:px-6 outline-hidden",
|
|
2288
|
+
autoFollow && pinned && !hasNewer
|
|
2289
|
+
? "[overflow-anchor:none]"
|
|
2290
|
+
: "[overflow-anchor:auto]",
|
|
2291
|
+
)}
|
|
2292
|
+
>
|
|
2293
|
+
<TimelineBeforeLayout
|
|
2294
|
+
capture={() => {
|
|
2295
|
+
if (readableTurns && timelineHasReader(scrollRef.current)) {
|
|
2337
2296
|
releasePinFromReader();
|
|
2338
|
-
disclosureKeepsUnpinnedRef.current = true;
|
|
2339
2297
|
}
|
|
2298
|
+
readingAnchorRef.current =
|
|
2299
|
+
!pinnedRef.current && scrollRef.current
|
|
2300
|
+
? captureTimelineAnchor(scrollRef.current)
|
|
2301
|
+
: null;
|
|
2302
|
+
settlementRef.current =
|
|
2303
|
+
readableTurns &&
|
|
2304
|
+
autoFollow &&
|
|
2305
|
+
pinnedRef.current &&
|
|
2306
|
+
revealedRef.current &&
|
|
2307
|
+
!hasNewer &&
|
|
2308
|
+
!pendingReaderLeaveRef.current &&
|
|
2309
|
+
!disclosureKeepsUnpinnedRef.current &&
|
|
2310
|
+
!prefersReducedMotion() &&
|
|
2311
|
+
scrollRef.current
|
|
2312
|
+
? captureTimelineSettlement(scrollRef.current, groups)
|
|
2313
|
+
: null;
|
|
2340
2314
|
}}
|
|
2341
|
-
onPointerDown={onPointerDown}
|
|
2342
|
-
onKeyDown={onKeyDown}
|
|
2343
|
-
style={
|
|
2344
|
-
groups.length > 0 && !revealed ? { visibility: "hidden" } : undefined
|
|
2345
|
-
}
|
|
2346
|
-
className={cn(
|
|
2347
|
-
// tabIndex=-1 is programmatic only — never paint a focus ring on
|
|
2348
|
-
// the whole scroller (click + Shift used to flash a blue outline).
|
|
2349
|
-
"min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 pt-16 pb-6 sm:px-6 outline-hidden",
|
|
2350
|
-
autoFollow && pinned && !hasNewer
|
|
2351
|
-
? "[overflow-anchor:none]"
|
|
2352
|
-
: "[overflow-anchor:auto]",
|
|
2353
|
-
)}
|
|
2354
2315
|
>
|
|
2355
|
-
<
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
// cover a disclosure. Visibility never changes content height.
|
|
2386
|
-
<motion.div
|
|
2387
|
-
initial={{ opacity: 0, y: -6 }}
|
|
2388
|
-
animate={{ opacity: 1, y: 0 }}
|
|
2389
|
-
exit={{ opacity: 0, y: -6 }}
|
|
2390
|
-
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2391
|
-
data-og-loading-older=""
|
|
2392
|
-
aria-live="polite"
|
|
2393
|
-
className={cn(
|
|
2394
|
-
"pointer-events-none absolute inset-x-0 -top-11 z-10 flex",
|
|
2395
|
-
// The scrolling history control and fixed question
|
|
2396
|
-
// action must never share the same pointer region.
|
|
2397
|
-
questionNav
|
|
2398
|
-
? "max-w-[calc(50%-0.5rem)] justify-start"
|
|
2399
|
-
: "justify-center",
|
|
2400
|
-
)}
|
|
2401
|
-
>
|
|
2402
|
-
{loadingOlder || loadingOldest ? (
|
|
2403
|
-
<span className={LOADING_CHIP_CLASS}>
|
|
2404
|
-
<span className="og-shimmer-text">
|
|
2405
|
-
{loadingOldest
|
|
2406
|
-
? "Jumping to start…"
|
|
2407
|
-
: "Loading earlier activity…"}
|
|
2408
|
-
</span>
|
|
2316
|
+
<div className="relative mx-auto flex w-full max-w-3xl flex-col gap-5">
|
|
2317
|
+
<AnimatePresence>
|
|
2318
|
+
{loadingOlder ||
|
|
2319
|
+
loadingOldest ||
|
|
2320
|
+
(hasOlder && onJumpToStart && olderPrefetchArmed) ||
|
|
2321
|
+
underfillRetryReady ? (
|
|
2322
|
+
// Reserved top gutter: controls scroll with history and cannot
|
|
2323
|
+
// cover a disclosure. Visibility never changes content height.
|
|
2324
|
+
<motion.div
|
|
2325
|
+
initial={{ opacity: 0, y: -6 }}
|
|
2326
|
+
animate={{ opacity: 1, y: 0 }}
|
|
2327
|
+
exit={{ opacity: 0, y: -6 }}
|
|
2328
|
+
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2329
|
+
data-og-loading-older=""
|
|
2330
|
+
aria-live="polite"
|
|
2331
|
+
className={cn(
|
|
2332
|
+
"pointer-events-none absolute inset-x-0 -top-11 z-10 flex",
|
|
2333
|
+
// The scrolling history control and fixed question
|
|
2334
|
+
// action must never share the same pointer region.
|
|
2335
|
+
questionNav
|
|
2336
|
+
? "max-w-[calc(50%-0.5rem)] justify-start"
|
|
2337
|
+
: "justify-center",
|
|
2338
|
+
)}
|
|
2339
|
+
>
|
|
2340
|
+
{loadingOlder || loadingOldest ? (
|
|
2341
|
+
<span className={LOADING_CHIP_CLASS}>
|
|
2342
|
+
<span className="og-shimmer-text">
|
|
2343
|
+
{loadingOldest
|
|
2344
|
+
? "Jumping to start…"
|
|
2345
|
+
: "Loading earlier activity…"}
|
|
2409
2346
|
</span>
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
underfillSettledAttempt!,
|
|
2429
|
-
);
|
|
2430
|
-
}
|
|
2431
|
-
return;
|
|
2347
|
+
</span>
|
|
2348
|
+
) : null}
|
|
2349
|
+
{hasOlder &&
|
|
2350
|
+
!loadingOlder &&
|
|
2351
|
+
!loadingOldest &&
|
|
2352
|
+
(underfillRetryReady || onJumpToStart) ? (
|
|
2353
|
+
<button
|
|
2354
|
+
type="button"
|
|
2355
|
+
data-og-retry={underfillRetryReady || undefined}
|
|
2356
|
+
data-og-jump-to-start={!underfillRetryReady || undefined}
|
|
2357
|
+
onClick={() => {
|
|
2358
|
+
const node = scrollRef.current;
|
|
2359
|
+
if (underfillRetryReady) {
|
|
2360
|
+
if (node && underfillRetryReadyRef.current) {
|
|
2361
|
+
// AnimatePresence retains this handler during
|
|
2362
|
+
// exit. Current authorization plus the exact
|
|
2363
|
+
// attempt check prevent stale dispatch.
|
|
2364
|
+
requestOlderIfUnderfilled(node, underfillSettledAttempt!);
|
|
2432
2365
|
}
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
}
|
|
2456
|
-
});
|
|
2457
|
-
},
|
|
2458
|
-
() => {
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2368
|
+
applyPinned(false);
|
|
2369
|
+
pendingJumpToStartRef.current = true;
|
|
2370
|
+
const seq = ++jumpToStartSeqRef.current;
|
|
2371
|
+
void Promise.resolve(onJumpToStart!()).then(
|
|
2372
|
+
() => {
|
|
2373
|
+
// The commit that swaps in the oldest window consumes
|
|
2374
|
+
// the flag against the new DOM; this write covers the
|
|
2375
|
+
// already-committed order and the no-window-change
|
|
2376
|
+
// case (jumping within the current window).
|
|
2377
|
+
const scroller = scrollRef.current ?? node;
|
|
2378
|
+
if (scroller) {
|
|
2379
|
+
scroller.scrollTop = 0;
|
|
2380
|
+
}
|
|
2381
|
+
// A host may resolve without ever changing the
|
|
2382
|
+
// window (already on the oldest page). Any swap
|
|
2383
|
+
// commit runs its layout effect before the next
|
|
2384
|
+
// frame, so a flag still armed by then is the
|
|
2385
|
+
// no-change case — clear it, or a LATER prepend
|
|
2386
|
+
// would spuriously jump the reader to the top.
|
|
2387
|
+
requestFrame(() => {
|
|
2459
2388
|
if (jumpToStartSeqRef.current === seq) {
|
|
2460
2389
|
pendingJumpToStartRef.current = false;
|
|
2461
2390
|
}
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
</p>
|
|
2482
|
-
))
|
|
2483
|
-
: null}
|
|
2484
|
-
{hasOlder && olderPrefetchArmed ? (
|
|
2485
|
-
// Overlaid, not a layout row: mounting/unmounting the sentinel
|
|
2486
|
-
// must never shift content (that shift was itself a wobble).
|
|
2487
|
-
// End at the scroll origin above the pt-16 gutter so the
|
|
2488
|
-
// observer and scrollTop cooldown share the same 400px band.
|
|
2489
|
-
<div
|
|
2490
|
-
ref={topSentinelRef}
|
|
2491
|
-
data-og-top-sentinel=""
|
|
2492
|
-
data-og-timeline-chrome=""
|
|
2493
|
-
aria-hidden="true"
|
|
2494
|
-
className="pointer-events-none absolute inset-x-0 -top-16 h-px -translate-y-full"
|
|
2495
|
-
/>
|
|
2496
|
-
) : null}
|
|
2497
|
-
{groups.map(({ group, key, entranceEnabled }, index) => {
|
|
2498
|
-
return (
|
|
2499
|
-
<TimelineSearchRevealContext.Provider
|
|
2500
|
-
key={key}
|
|
2501
|
-
value={
|
|
2502
|
-
searchItem &&
|
|
2503
|
-
timelineGroupItemIds(group).includes(searchItem.id)
|
|
2504
|
-
? searchRevealKey
|
|
2505
|
-
: null
|
|
2506
|
-
}
|
|
2507
|
-
>
|
|
2508
|
-
<TimelineGroupEntry
|
|
2509
|
-
groupKey={key}
|
|
2510
|
-
group={group}
|
|
2511
|
-
nextGroup={groups[index + 1]?.group}
|
|
2512
|
-
startupDismissed={
|
|
2513
|
-
group.kind === "activity" &&
|
|
2514
|
-
group.items.some(
|
|
2515
|
-
(item) => item.turnId && turnsWithOutput.has(item.turnId),
|
|
2516
|
-
)
|
|
2517
|
-
}
|
|
2518
|
-
entranceEnabled={entranceEnabled}
|
|
2519
|
-
liveEntranceEnabled={
|
|
2520
|
-
group.kind === "activity" ? !bulkRender : undefined
|
|
2521
|
-
}
|
|
2522
|
-
context={timelineGroupEntryContext}
|
|
2523
|
-
/>
|
|
2524
|
-
</TimelineSearchRevealContext.Provider>
|
|
2525
|
-
);
|
|
2526
|
-
})}
|
|
2527
|
-
{groups.length > 0 && trailingState ? (
|
|
2528
|
-
<div data-og-timeline-trailing-state="">{trailingState}</div>
|
|
2391
|
+
});
|
|
2392
|
+
},
|
|
2393
|
+
() => {
|
|
2394
|
+
if (jumpToStartSeqRef.current === seq) {
|
|
2395
|
+
pendingJumpToStartRef.current = false;
|
|
2396
|
+
}
|
|
2397
|
+
},
|
|
2398
|
+
);
|
|
2399
|
+
}}
|
|
2400
|
+
className="pointer-events-auto rounded-full border border-og-border px-3 py-1.5 text-og-control"
|
|
2401
|
+
>
|
|
2402
|
+
{underfillRetryReady
|
|
2403
|
+
? underfillSettledAttempt?.[2]?.tailPreserved
|
|
2404
|
+
? "Load earlier activity"
|
|
2405
|
+
: "Retry earlier activity"
|
|
2406
|
+
: "Jump to start"}
|
|
2407
|
+
</button>
|
|
2408
|
+
) : null}
|
|
2409
|
+
</motion.div>
|
|
2529
2410
|
) : null}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
className="
|
|
2534
|
-
|
|
2535
|
-
<p role="status" className="max-w-prose [overflow-wrap:anywhere]">
|
|
2536
|
-
Couldn’t load later activity. {newerFailure.message}
|
|
2411
|
+
</AnimatePresence>
|
|
2412
|
+
{!groups.length
|
|
2413
|
+
? (emptyState ?? (
|
|
2414
|
+
<p className="py-10 text-center text-og-menu text-og-fg-subtle">
|
|
2415
|
+
No activity yet.
|
|
2537
2416
|
</p>
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
data-og-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
"
|
|
2589
|
-
QUESTION_PILL_PLACEMENT_CLASS[questionPillPlacement],
|
|
2590
|
-
)}
|
|
2591
|
-
>
|
|
2592
|
-
<motion.div
|
|
2593
|
-
ref={questionPillRef}
|
|
2594
|
-
layout="position"
|
|
2595
|
-
transition={{ duration: 0.18, ease: "easeOut" }}
|
|
2596
|
-
className="pointer-events-auto inline-flex max-w-[calc(50%-0.5rem)] items-center rounded-full border border-og-border bg-og-surface-3/90 text-og-control font-medium text-og-fg shadow-og-md backdrop-blur"
|
|
2417
|
+
))
|
|
2418
|
+
: null}
|
|
2419
|
+
{hasOlder && olderPrefetchArmed ? (
|
|
2420
|
+
// Overlaid, not a layout row: mounting/unmounting the sentinel
|
|
2421
|
+
// must never shift content (that shift was itself a wobble).
|
|
2422
|
+
// End at the scroll origin above the pt-16 gutter so the
|
|
2423
|
+
// observer and scrollTop cooldown share the same 400px band.
|
|
2424
|
+
<div
|
|
2425
|
+
ref={topSentinelRef}
|
|
2426
|
+
data-og-top-sentinel=""
|
|
2427
|
+
data-og-timeline-chrome=""
|
|
2428
|
+
aria-hidden="true"
|
|
2429
|
+
className="pointer-events-none absolute inset-x-0 -top-16 h-px -translate-y-full"
|
|
2430
|
+
/>
|
|
2431
|
+
) : null}
|
|
2432
|
+
{groups.map(({ group, key, entranceEnabled }, index) => {
|
|
2433
|
+
return (
|
|
2434
|
+
<TimelineSearchRevealContext.Provider
|
|
2435
|
+
key={key}
|
|
2436
|
+
value={
|
|
2437
|
+
searchItem && timelineGroupItemIds(group).includes(searchItem.id)
|
|
2438
|
+
? searchRevealKey
|
|
2439
|
+
: null
|
|
2440
|
+
}
|
|
2441
|
+
>
|
|
2442
|
+
<TimelineGroupEntry
|
|
2443
|
+
groupKey={key}
|
|
2444
|
+
group={group}
|
|
2445
|
+
nextGroup={groups[index + 1]?.group}
|
|
2446
|
+
startupDismissed={
|
|
2447
|
+
group.kind === "activity" &&
|
|
2448
|
+
group.items.some(
|
|
2449
|
+
(item) => item.turnId && turnsWithOutput.has(item.turnId),
|
|
2450
|
+
)
|
|
2451
|
+
}
|
|
2452
|
+
entranceEnabled={entranceEnabled}
|
|
2453
|
+
liveEntranceEnabled={
|
|
2454
|
+
group.kind === "activity" ? !bulkRender : undefined
|
|
2455
|
+
}
|
|
2456
|
+
context={timelineGroupEntryContext}
|
|
2457
|
+
/>
|
|
2458
|
+
</TimelineSearchRevealContext.Provider>
|
|
2459
|
+
);
|
|
2460
|
+
})}
|
|
2461
|
+
{groups.length > 0 && trailingState ? (
|
|
2462
|
+
<div data-og-timeline-trailing-state="">{trailingState}</div>
|
|
2463
|
+
) : null}
|
|
2464
|
+
{hasNewer && newerFailure?.key === newerBoundaryKey ? (
|
|
2465
|
+
<div
|
|
2466
|
+
data-og-newer-error=""
|
|
2467
|
+
className="flex flex-col items-center gap-2 px-4 py-3 text-center text-og-menu text-og-fg-muted"
|
|
2597
2468
|
>
|
|
2469
|
+
<p role="status" className="max-w-prose [overflow-wrap:anywhere]">
|
|
2470
|
+
Couldn’t load later activity. {newerFailure.message}
|
|
2471
|
+
</p>
|
|
2598
2472
|
<button
|
|
2473
|
+
ref={newerRetryButtonRef}
|
|
2599
2474
|
type="button"
|
|
2600
|
-
data-og-
|
|
2601
|
-
|
|
2602
|
-
disabled={
|
|
2603
|
-
aria-busy={
|
|
2604
|
-
|
|
2605
|
-
className="inline-flex min-w-0 items-center gap-1.5 px-3 py-1.5 hover:text-og-fg pointer-coarse:min-h-11"
|
|
2475
|
+
data-og-retry-newer=""
|
|
2476
|
+
className="min-h-11 rounded-og-md border border-og-border px-3 py-2 text-og-fg hover:bg-og-surface-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-og-accent"
|
|
2477
|
+
aria-disabled={loadingNewer || newerRetryPending}
|
|
2478
|
+
aria-busy={newerRetryPending}
|
|
2479
|
+
onClick={() => requestNewer(true)}
|
|
2606
2480
|
>
|
|
2607
|
-
|
|
2608
|
-
|
|
2481
|
+
{newerRetryPending
|
|
2482
|
+
? "Retrying later activity…"
|
|
2483
|
+
: "Retry later activity"}
|
|
2609
2484
|
</button>
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2485
|
+
</div>
|
|
2486
|
+
) : null}
|
|
2487
|
+
{hasNewer ? (
|
|
2488
|
+
<div
|
|
2489
|
+
ref={bottomSentinelRef}
|
|
2490
|
+
data-og-bottom-sentinel=""
|
|
2491
|
+
data-og-timeline-chrome=""
|
|
2492
|
+
aria-hidden="true"
|
|
2493
|
+
className="h-px w-full"
|
|
2494
|
+
/>
|
|
2495
|
+
) : null}
|
|
2496
|
+
</div>
|
|
2497
|
+
</TimelineBeforeLayout>
|
|
2498
|
+
</div>
|
|
2499
|
+
{draftAnnotations && draftAnnotations.length > 0 ? (
|
|
2500
|
+
<Suspense fallback={null}>
|
|
2501
|
+
<TimelineAnnotationMarkers
|
|
2502
|
+
rootRef={scrollRef}
|
|
2503
|
+
annotations={draftAnnotations}
|
|
2504
|
+
onSelect={onDraftAnnotationSelect}
|
|
2505
|
+
/>
|
|
2506
|
+
</Suspense>
|
|
2507
|
+
) : null}
|
|
2508
|
+
|
|
2509
|
+
<AnimatePresence>
|
|
2510
|
+
{questionNav ? (
|
|
2511
|
+
<motion.div
|
|
2512
|
+
key="question-nav"
|
|
2513
|
+
initial={{ opacity: 0, y: -6 }}
|
|
2514
|
+
animate={{ opacity: 1, y: 0 }}
|
|
2515
|
+
exit={{ opacity: 0, y: -6 }}
|
|
2516
|
+
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2517
|
+
data-og-question-nav=""
|
|
2518
|
+
// Below the pinned work-header strip (py-1.5 row, 44px on coarse
|
|
2519
|
+
// pointers), never over it: the header stays a full-width target.
|
|
2520
|
+
className="pointer-events-none absolute inset-x-0 top-11 z-10 flex justify-end px-4 sm:px-6 pointer-coarse:top-14"
|
|
2521
|
+
>
|
|
2522
|
+
<div className="pointer-events-auto inline-flex max-w-[calc(50%-0.5rem)] items-center rounded-full border border-og-border bg-og-surface-3/90 text-og-control font-medium text-og-fg shadow-og-md backdrop-blur">
|
|
2523
|
+
<button
|
|
2524
|
+
type="button"
|
|
2525
|
+
data-og-jump-to-question=""
|
|
2526
|
+
onClick={() => void jumpToLatestQuestion()}
|
|
2527
|
+
disabled={questionPending}
|
|
2528
|
+
aria-busy={questionPending}
|
|
2529
|
+
title={questionError ?? undefined}
|
|
2530
|
+
className="inline-flex min-w-0 items-center gap-1.5 px-3 py-1.5 hover:text-og-fg pointer-coarse:min-h-11"
|
|
2531
|
+
>
|
|
2532
|
+
<ArrowUpIcon aria-hidden className="size-3.5" />
|
|
2533
|
+
Latest question
|
|
2534
|
+
</button>
|
|
2535
|
+
{questionError && (
|
|
2536
|
+
<span role="status" className="px-2 text-og-xs">
|
|
2537
|
+
{questionError}
|
|
2538
|
+
</span>
|
|
2539
|
+
)}
|
|
2540
|
+
</div>
|
|
2541
|
+
</motion.div>
|
|
2542
|
+
) : null}
|
|
2543
|
+
</AnimatePresence>
|
|
2544
|
+
<AnimatePresence>
|
|
2545
|
+
{loadingNewer ? (
|
|
2546
|
+
<motion.div
|
|
2547
|
+
initial={{ opacity: 0, y: 6 }}
|
|
2548
|
+
animate={{ opacity: 1, y: 0 }}
|
|
2549
|
+
exit={{ opacity: 0, y: 6 }}
|
|
2550
|
+
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2551
|
+
data-og-loading-newer=""
|
|
2552
|
+
aria-live="polite"
|
|
2553
|
+
className="pointer-events-none absolute inset-x-0 bottom-14 z-10 flex justify-center"
|
|
2554
|
+
>
|
|
2555
|
+
<span className={LOADING_CHIP_CLASS}>
|
|
2556
|
+
<span className="og-shimmer-text">Loading later activity…</span>
|
|
2557
|
+
</span>
|
|
2558
|
+
</motion.div>
|
|
2559
|
+
) : null}
|
|
2560
|
+
</AnimatePresence>
|
|
2561
|
+
<AnimatePresence>
|
|
2562
|
+
{((!pinned && autoFollow) || hasNewer || canSkipTipCatchup) && autoFollow ? (
|
|
2563
|
+
<motion.button
|
|
2564
|
+
type="button"
|
|
2565
|
+
data-og-jump-to-latest=""
|
|
2566
|
+
initial={{ opacity: 0, y: 8 }}
|
|
2567
|
+
animate={{ opacity: 1, y: 0 }}
|
|
2568
|
+
exit={{ opacity: 0, y: 8 }}
|
|
2569
|
+
transition={{ duration: 0.15, ease: "easeOut" }}
|
|
2570
|
+
onClick={() => {
|
|
2571
|
+
// Returning to the tip explicitly releases reader-owned
|
|
2572
|
+
// prose. Clear only this timeline's selection before the
|
|
2573
|
+
// synchronous ownership check on the next commit.
|
|
2574
|
+
const viewport = scrollRef.current;
|
|
2575
|
+
const selection = viewport?.ownerDocument.getSelection();
|
|
2576
|
+
if (
|
|
2577
|
+
selection &&
|
|
2578
|
+
!selection.isCollapsed &&
|
|
2579
|
+
(viewport?.contains(selection.anchorNode) ||
|
|
2580
|
+
viewport?.contains(selection.focusNode))
|
|
2581
|
+
)
|
|
2582
|
+
selection.removeAllRanges();
|
|
2583
|
+
disclosureKeepsUnpinnedRef.current = false;
|
|
2584
|
+
if (hasNewer) {
|
|
2585
|
+
// Do not pin against the current history page — its bottom
|
|
2586
|
+
// is not the tip. The pin + snap run when the tip window
|
|
2587
|
+
// actually lands (`hasNewer` flips false).
|
|
2588
|
+
wantPinRef.current = true;
|
|
2696
2589
|
const node = scrollRef.current;
|
|
2697
|
-
if (
|
|
2698
|
-
|
|
2590
|
+
if (onJumpToLatest) {
|
|
2591
|
+
void Promise.resolve(onJumpToLatest()).then(
|
|
2592
|
+
() => {
|
|
2593
|
+
// Covers a host that flipped hasNewer before
|
|
2594
|
+
// resolving; otherwise the tip-window commit
|
|
2595
|
+
// consumes the flag.
|
|
2596
|
+
const current = scrollRef.current;
|
|
2597
|
+
if (current && wantPinRef.current && !hasNewerRef.current) {
|
|
2598
|
+
wantPinRef.current = false;
|
|
2599
|
+
applyPinned(true);
|
|
2600
|
+
snapToBottom(current);
|
|
2601
|
+
}
|
|
2602
|
+
},
|
|
2603
|
+
() => {
|
|
2604
|
+
// The tip reload failed (ordinary network error):
|
|
2605
|
+
// an armed latch would fire a surprise snap when
|
|
2606
|
+
// the reader later pages to the tip themselves.
|
|
2607
|
+
wantPinRef.current = false;
|
|
2608
|
+
},
|
|
2609
|
+
);
|
|
2610
|
+
} else if (node) {
|
|
2611
|
+
// No tip reload available: jump within the in-memory
|
|
2612
|
+
// window so the newer sentinel can page forward; the
|
|
2613
|
+
// latch pins if the tip window eventually lands.
|
|
2699
2614
|
snapToBottom(node);
|
|
2700
2615
|
}
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
const node = scrollRef.current;
|
|
2619
|
+
if (node) {
|
|
2620
|
+
applyPinned(true);
|
|
2621
|
+
snapToBottom(node);
|
|
2622
|
+
}
|
|
2623
|
+
}}
|
|
2624
|
+
className={cn(
|
|
2625
|
+
"absolute inset-x-0 bottom-4 mx-auto w-fit",
|
|
2626
|
+
"inline-flex items-center gap-1.5 rounded-full border border-og-border bg-og-surface-3/90 px-3 py-1.5",
|
|
2627
|
+
"text-og-control font-medium text-og-fg shadow-og-md backdrop-blur",
|
|
2628
|
+
"hover:border-og-border-strong",
|
|
2629
|
+
)}
|
|
2630
|
+
>
|
|
2631
|
+
<ArrowDownIcon className="size-3.5" />
|
|
2632
|
+
Jump to latest
|
|
2633
|
+
</motion.button>
|
|
2634
|
+
) : null}
|
|
2635
|
+
</AnimatePresence>
|
|
2636
|
+
</div>
|
|
2637
|
+
</TimelineAnnotationSourceRootContext.Provider>
|
|
2638
|
+
</TooltipProvider>
|
|
2639
|
+
</EntranceAnimationProvider>
|
|
2640
|
+
</TimelineComputeLabelProvider>
|
|
2641
|
+
</SeenActivityIdsProvider>
|
|
2642
|
+
</FoldMemoryProvider>
|
|
2643
|
+
</LightboxProvider>
|
|
2724
2644
|
);
|
|
2725
2645
|
// Agent-authored object links resolve through the host, never the console.
|
|
2726
2646
|
return <OpenGeniLinkProvider resolveLink={resolveLink}>{timeline}</OpenGeniLinkProvider>;
|
|
@@ -4628,7 +4548,6 @@ function formatVideoDuration(seconds: number): string {
|
|
|
4628
4548
|
|
|
4629
4549
|
function NoticeRow({ item }: { item: NoticeItem }) {
|
|
4630
4550
|
const enter = useEntranceAnimation();
|
|
4631
|
-
if (item.allowance) return <AllowanceExhaustedRow refusal={item.allowance} />;
|
|
4632
4551
|
if (item.recordedOutcome) {
|
|
4633
4552
|
return (
|
|
4634
4553
|
<details
|