@opengeni/react 6.1.0 → 7.0.0-canary.35625901699001
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 +83 -2
- package/dist/approvals.d.ts +2 -1
- package/dist/artifacts-document.js +1 -1
- package/dist/artifacts-presentation.js +1 -1
- package/dist/artifacts-spreadsheet.js +1 -1
- package/dist/artifacts.js +3 -3
- package/dist/{browser-viewer-NTD6UIPR.js → browser-viewer-LGH4IP47.js} +3 -3
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/{chunk-NOSYBS2D.js → chunk-2VHARTBL.js} +2 -1
- package/dist/{chunk-VRBL3F5E.js → chunk-5GAL23FF.js} +10 -2
- package/dist/{chunk-VRBL3F5E.js.map → chunk-5GAL23FF.js.map} +1 -1
- package/dist/chunk-5UONOQ3Y.js +635 -0
- package/dist/chunk-5UONOQ3Y.js.map +1 -0
- package/dist/{chunk-NYESIADM.js → chunk-AKOMC3VO.js} +3 -3
- package/dist/chunk-AKOMC3VO.js.map +1 -0
- package/dist/chunk-BRM6FVZA.js +302 -0
- package/dist/chunk-BRM6FVZA.js.map +1 -0
- package/dist/{chunk-KANFU34B.js → chunk-DBPVUGQT.js} +11 -2
- package/dist/chunk-DBPVUGQT.js.map +1 -0
- package/dist/chunk-FHIGTPME.js +1589 -0
- package/dist/chunk-FHIGTPME.js.map +1 -0
- package/dist/{chunk-5M6VTFJ5.js → chunk-GBPCZH3Y.js} +2 -2
- package/dist/{chunk-YBM6CQRU.js → chunk-GEN54DU5.js} +4 -4
- package/dist/{chunk-UMECRJRV.js → chunk-HJ5VJ4JB.js} +21 -12
- package/dist/chunk-HJ5VJ4JB.js.map +1 -0
- package/dist/{chunk-R4KRSFGA.js → chunk-IP4BUBKH.js} +3 -3
- package/dist/{chunk-NLTS7JHI.js → chunk-JPHDWKZE.js} +5 -5
- package/dist/{chunk-TDSEILUH.js → chunk-MI6ALLLD.js} +1297 -1535
- package/dist/chunk-MI6ALLLD.js.map +1 -0
- package/dist/{chunk-ICA6R6TW.js → chunk-OWHPKAOV.js} +28 -20
- package/dist/chunk-OWHPKAOV.js.map +1 -0
- package/dist/{chunk-24M4YBCL.js → chunk-PNAVEK2U.js} +4 -4
- package/dist/{chunk-L2NBTKRX.js → chunk-TXCBE32L.js} +203 -19
- package/dist/chunk-TXCBE32L.js.map +1 -0
- package/dist/{chunk-45XEUVTK.js → chunk-UXE5SSNN.js} +293 -463
- package/dist/chunk-UXE5SSNN.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-VR4Z73EU.js} +203 -9
- package/dist/chunk-VR4Z73EU.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-XMXRI5JK.js} +65 -18
- package/dist/chunk-XMXRI5JK.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
- package/dist/components/chat-composer.d.ts +13 -3
- package/dist/components/markdown.d.ts +6 -3
- package/dist/components/message-timeline.d.ts +7 -2
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/session-capability-frame.d.ts +24 -0
- package/dist/components/session-chrome.d.ts +1 -2
- package/dist/components/session-commands.d.ts +6 -0
- package/dist/components/session-connection-request.d.ts +8 -0
- package/dist/components/session-conversation.d.ts +3 -0
- package/dist/components/session-mcp-capability-card.d.ts +22 -0
- package/dist/components/timeline-search.d.ts +34 -0
- package/dist/composer.js +7 -7
- package/dist/connect-account-identity.d.ts +8 -0
- package/dist/connect-accounts.d.ts +5 -0
- package/dist/connect-chooser.d.ts +6 -0
- package/dist/connect-panel.d.ts +9 -2
- package/dist/connect-setup.d.ts +5 -1
- package/dist/connect.d.ts +5 -21
- package/dist/connect.js +1268 -584
- package/dist/connect.js.map +1 -1
- package/dist/connection-catalog.d.ts +6 -1
- package/dist/connection-discovery.d.ts +12 -0
- package/dist/connection-installed.d.ts +1 -0
- package/dist/connection-logo.d.ts +5 -2
- package/dist/connection-service-logo.d.ts +8 -0
- package/dist/connection-service-presentation.d.ts +13 -0
- package/dist/connection-skeleton.d.ts +5 -0
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/hooks/use-codex-accounts.d.ts +11 -5
- package/dist/hooks/use-connect.d.ts +22 -0
- package/dist/hooks/use-session-events.d.ts +5 -0
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-workspace-sessions.d.ts +2 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +771 -793
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/machines.js +2 -2
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/{model-policy-picker-menu-3R336DP2.js → model-policy-picker-menu-RVOQ4ZZG.js} +4 -4
- package/dist/model-policy.d.ts +6 -2
- package/dist/model-policy.js +1 -1
- package/dist/{platform-activity-row-DUE2W74J.js → platform-activity-row-HLOH7FW4.js} +9 -4
- package/dist/platform-activity-row-HLOH7FW4.js.map +1 -0
- package/dist/plugin-discovery.d.ts +6 -0
- package/dist/{queue-surface-implementation-LK3S4BIW.js → queue-surface-implementation-3RIFK2BS.js} +4 -4
- package/dist/realtime.js +22 -22
- package/dist/realtime.js.map +1 -1
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +12 -0
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +14 -12
- package/dist/session.d.ts +1 -0
- package/dist/session.js +6 -4
- package/dist/skill-discovery.d.ts +11 -0
- package/dist/timeline/compaction-copy.d.ts +10 -0
- package/dist/timeline/projection.d.ts +3 -1
- package/dist/timeline/tool-display-name.d.ts +2 -1
- package/dist/timeline/turn-summary.d.ts +2 -2
- package/dist/timeline/types.d.ts +24 -2
- package/dist/{timeline-annotation-markers-DRSTLEM6.js → timeline-annotation-markers-OY6OWR3V.js} +2 -2
- package/dist/{timeline-annotation-selection-IHLUGVND.js → timeline-annotation-selection-BOXA2TIN.js} +2 -2
- package/dist/{timeline-annotations-dialog-6Q53N5FG.js → timeline-annotations-dialog-RGY3MHL3.js} +3 -3
- package/package.json +3 -3
- package/src/approvals.ts +8 -1
- package/src/assets/README.md +8 -0
- package/src/assets/gmail.ico +0 -0
- package/src/assets/outlook.ico +0 -0
- package/src/capability-catalog-row.tsx +94 -0
- package/src/capability-logo-fallback.ts +22 -0
- package/src/client.ts +0 -9
- package/src/components/approval-surface.tsx +12 -2
- package/src/components/artifacts/document-editor.tsx +63 -6
- package/src/components/artifacts/presentation-editor.tsx +214 -9
- package/src/components/artifacts/spreadsheet-canvas.ts +3 -8
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +10 -11
- package/src/components/chat-composer.tsx +26 -3
- package/src/components/composer.tsx +10 -4
- package/src/components/human-input-form.tsx +12 -2
- package/src/components/markdown.tsx +133 -9
- package/src/components/message-timeline.tsx +104 -36
- package/src/components/model-picker.tsx +11 -3
- package/src/components/model-policy-picker-menu.tsx +10 -3
- package/src/components/preview-loading.tsx +154 -0
- package/src/components/sandbox-workspace.tsx +20 -36
- package/src/components/session-capability-frame.tsx +163 -0
- package/src/components/session-chrome.tsx +46 -77
- package/src/components/session-commands.tsx +18 -0
- package/src/components/session-connection-request.tsx +97 -0
- package/src/components/session-conversation.tsx +10 -5
- package/src/components/session-mcp-capability-card.tsx +532 -0
- package/src/components/session-status.tsx +2 -2
- package/src/components/timeline-anchor.tsx +9 -8
- package/src/components/timeline-search.tsx +168 -0
- package/src/components/user-message-body.tsx +7 -0
- package/src/connect-account-identity.tsx +34 -0
- package/src/connect-accounts.tsx +114 -52
- package/src/connect-chooser.tsx +171 -39
- package/src/connect-panel.tsx +73 -9
- package/src/connect-setup.tsx +72 -25
- package/src/connect.ts +13 -22
- package/src/connection-assets.d.ts +4 -0
- package/src/connection-catalog.tsx +61 -39
- package/src/connection-discovery.tsx +235 -0
- package/src/connection-installed.tsx +2 -0
- package/src/connection-logo.tsx +18 -10
- package/src/connection-service-logo.tsx +55 -0
- package/src/connection-service-presentation.ts +46 -0
- package/src/connection-skeleton.tsx +16 -0
- package/src/conversation-timeline.ts +1 -0
- package/src/discovery-cache.ts +42 -0
- package/src/hooks/use-codex-accounts.ts +33 -14
- package/src/hooks/use-composer.ts +4 -5
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-session-events.ts +114 -1
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-workspace-sessions.ts +10 -0
- package/src/index.ts +22 -2
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-policy.ts +21 -0
- package/src/plugin-details.tsx +8 -5
- package/src/plugin-discovery.tsx +92 -72
- package/src/realtime/realtime-control.tsx +6 -8
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +83 -0
- package/src/session-ui.ts +2 -0
- package/src/session.ts +1 -0
- package/src/skill-discovery.tsx +101 -56
- package/src/timeline/compaction-copy.ts +40 -0
- package/src/timeline/platform-activity-row.tsx +8 -3
- package/src/timeline/projection.ts +78 -2
- package/src/timeline/rolling-activity.tsx +4 -2
- package/src/timeline/shared.tsx +6 -1
- package/src/timeline/startup-timings.tsx +1 -1
- package/src/timeline/tool-display-name.ts +8 -6
- package/src/timeline/tool-renderers.tsx +5 -3
- package/src/timeline/turn-summary.tsx +17 -3
- package/src/timeline/types.ts +24 -2
- package/styles/compiled.css +325 -27
- package/styles/connect.css +90 -15
- package/styles/index.css +46 -0
- package/styles/session-capability.css +42 -0
- package/dist/chunk-45XEUVTK.js.map +0 -1
- package/dist/chunk-HQV2I5HV.js +0 -124
- package/dist/chunk-HQV2I5HV.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-ICA6R6TW.js.map +0 -1
- package/dist/chunk-IHQII5WO.js +0 -886
- package/dist/chunk-IHQII5WO.js.map +0 -1
- package/dist/chunk-KANFU34B.js.map +0 -1
- package/dist/chunk-L2NBTKRX.js.map +0 -1
- package/dist/chunk-NYESIADM.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-TDSEILUH.js.map +0 -1
- package/dist/chunk-UMECRJRV.js.map +0 -1
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/platform-activity-row-DUE2W74J.js.map +0 -1
- package/src/hooks/use-packs.ts +0 -157
- /package/dist/{browser-viewer-NTD6UIPR.js.map → browser-viewer-LGH4IP47.js.map} +0 -0
- /package/dist/{chunk-NOSYBS2D.js.map → chunk-2VHARTBL.js.map} +0 -0
- /package/dist/{chunk-5M6VTFJ5.js.map → chunk-GBPCZH3Y.js.map} +0 -0
- /package/dist/{chunk-YBM6CQRU.js.map → chunk-GEN54DU5.js.map} +0 -0
- /package/dist/{chunk-R4KRSFGA.js.map → chunk-IP4BUBKH.js.map} +0 -0
- /package/dist/{chunk-NLTS7JHI.js.map → chunk-JPHDWKZE.js.map} +0 -0
- /package/dist/{chunk-24M4YBCL.js.map → chunk-PNAVEK2U.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-3R336DP2.js.map → model-policy-picker-menu-RVOQ4ZZG.js.map} +0 -0
- /package/dist/{queue-surface-implementation-LK3S4BIW.js.map → queue-surface-implementation-3RIFK2BS.js.map} +0 -0
- /package/dist/{timeline-annotation-markers-DRSTLEM6.js.map → timeline-annotation-markers-OY6OWR3V.js.map} +0 -0
- /package/dist/{timeline-annotation-selection-IHLUGVND.js.map → timeline-annotation-selection-BOXA2TIN.js.map} +0 -0
- /package/dist/{timeline-annotations-dialog-6Q53N5FG.js.map → timeline-annotations-dialog-RGY3MHL3.js.map} +0 -0
|
@@ -19,15 +19,19 @@ import ReactMarkdown, {
|
|
|
19
19
|
type UrlTransform,
|
|
20
20
|
} from "react-markdown";
|
|
21
21
|
import remarkGfm from "remark-gfm";
|
|
22
|
+
import { PanelsTopLeftIcon } from "lucide-react";
|
|
23
|
+
import { ActivityDisclosure } from "../timeline/shared";
|
|
22
24
|
import { cn } from "../lib/cn";
|
|
23
25
|
import { tableElementToTsv } from "../lib/clipboard";
|
|
24
26
|
import { prefersReducedMotion } from "../lib/motion";
|
|
25
27
|
import { MOTION_INSPECT_SCALE } from "../lib/motion-inspect";
|
|
26
28
|
import { CopyButton } from "./copy-button";
|
|
29
|
+
import { PreviewLoading } from "./preview-loading";
|
|
27
30
|
import type { observeMarkdownTableLayout } from "./markdown-table-layout";
|
|
28
31
|
import { softenStreamingMarkdown } from "./soften-streaming-markdown";
|
|
29
32
|
import { createStreamReveal, rehypeStreamReveal, type StreamReveal } from "./stream-reveal";
|
|
30
33
|
import { TooltipProvider } from "./tooltip";
|
|
34
|
+
import { searchMatchOffset, type TimelineSearchTarget } from "./timeline-search";
|
|
31
35
|
|
|
32
36
|
/**
|
|
33
37
|
* The default renderer for chat message bodies in {@link MessageTimeline}.
|
|
@@ -46,11 +50,16 @@ import { TooltipProvider } from "./tooltip";
|
|
|
46
50
|
export type MarkdownInteractiveBlock = { kind: "html" | "site"; content: string };
|
|
47
51
|
const InteractiveContext = createContext<{
|
|
48
52
|
source: string;
|
|
53
|
+
streaming?: boolean | undefined;
|
|
49
54
|
render?: (block: MarkdownInteractiveBlock) => ReactNode;
|
|
50
55
|
renderImage?: (image: { src: string; alt: string }) => ReactNode;
|
|
51
56
|
}>({ source: "" });
|
|
52
57
|
|
|
53
58
|
export type MarkdownProps = {
|
|
59
|
+
/** Resolve a retained-file reference to the host's authenticated artifact page. */
|
|
60
|
+
artifactHref?: ((artifactId: string) => string) | undefined;
|
|
61
|
+
/** Reveals a bounded source excerpt; closing Find retains it until explicit restore. */
|
|
62
|
+
searchTarget?: TimelineSearchTarget | null | undefined;
|
|
54
63
|
renderImage?: ((image: { src: string; alt: string }) => ReactNode) | undefined;
|
|
55
64
|
/** Host opt-in for assistant-authored interactive fences. */
|
|
56
65
|
renderInteractiveBlock?: ((block: MarkdownInteractiveBlock) => ReactNode) | undefined;
|
|
@@ -210,10 +219,46 @@ const baseComponents: Components = {
|
|
|
210
219
|
const MARKDOWN_LINK_CLASS =
|
|
211
220
|
"break-words font-medium text-og-accent-strong underline-offset-2 hover:underline";
|
|
212
221
|
|
|
213
|
-
function markdownComponents(
|
|
222
|
+
function markdownComponents(
|
|
223
|
+
onSandboxFile: MarkdownProps["onSandboxFile"],
|
|
224
|
+
artifactHref: MarkdownProps["artifactHref"],
|
|
225
|
+
): Components {
|
|
214
226
|
return {
|
|
215
227
|
...baseComponents,
|
|
228
|
+
p: ({ children, node, ...props }) => {
|
|
229
|
+
const containsArtifact = node?.children.some(
|
|
230
|
+
(child) =>
|
|
231
|
+
child.type === "element" &&
|
|
232
|
+
child.tagName === "img" &&
|
|
233
|
+
typeof child.properties.src === "string" &&
|
|
234
|
+
retainedImageId(child.properties.src),
|
|
235
|
+
);
|
|
236
|
+
const Tag = containsArtifact ? "div" : "p";
|
|
237
|
+
return (
|
|
238
|
+
<Tag className="my-2.5 leading-7 first:mt-0 last:mb-0" {...props}>
|
|
239
|
+
{children}
|
|
240
|
+
</Tag>
|
|
241
|
+
);
|
|
242
|
+
},
|
|
216
243
|
a: ({ children, href, ...props }) => {
|
|
244
|
+
const artifactId = href ? retainedImageId(href) : null;
|
|
245
|
+
if (artifactId) {
|
|
246
|
+
const destination = artifactHref?.(artifactId);
|
|
247
|
+
return destination ? (
|
|
248
|
+
<a
|
|
249
|
+
className={MARKDOWN_LINK_CLASS}
|
|
250
|
+
href={defaultUrlTransform(destination)}
|
|
251
|
+
target="_blank"
|
|
252
|
+
rel="noreferrer noopener"
|
|
253
|
+
>
|
|
254
|
+
{children}
|
|
255
|
+
</a>
|
|
256
|
+
) : (
|
|
257
|
+
<span title="This artifact requires a workspace-aware host">
|
|
258
|
+
{children} (artifact unavailable)
|
|
259
|
+
</span>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
217
262
|
const location = sandboxFileLocationFromHref(href);
|
|
218
263
|
if (location !== null) {
|
|
219
264
|
return (
|
|
@@ -225,7 +270,7 @@ function markdownComponents(onSandboxFile: MarkdownProps["onSandboxFile"]): Comp
|
|
|
225
270
|
if (isSandboxHref(href) || !href) {
|
|
226
271
|
return (
|
|
227
272
|
<span
|
|
228
|
-
className="break-words
|
|
273
|
+
className="break-words text-og-fg-subtle"
|
|
229
274
|
aria-disabled="true"
|
|
230
275
|
title={
|
|
231
276
|
isSandboxHref(href)
|
|
@@ -233,7 +278,7 @@ function markdownComponents(onSandboxFile: MarkdownProps["onSandboxFile"]): Comp
|
|
|
233
278
|
: "This link is unavailable"
|
|
234
279
|
}
|
|
235
280
|
>
|
|
236
|
-
{children}
|
|
281
|
+
{children} (link unavailable)
|
|
237
282
|
</span>
|
|
238
283
|
);
|
|
239
284
|
}
|
|
@@ -364,7 +409,7 @@ function MarkdownImage({ src, alt }: ComponentPropsWithoutRef<"img">) {
|
|
|
364
409
|
}
|
|
365
410
|
|
|
366
411
|
const markdownUrlTransform: UrlTransform = (url, key, node) =>
|
|
367
|
-
(key === "href" && node.tagName === "a" && isSandboxHref(url)) ||
|
|
412
|
+
(key === "href" && node.tagName === "a" && (isSandboxHref(url) || retainedImageId(url))) ||
|
|
368
413
|
(key === "src" && node.tagName === "img" && retainedImageId(url))
|
|
369
414
|
? url
|
|
370
415
|
: defaultUrlTransform(url);
|
|
@@ -406,7 +451,7 @@ function fenceLanguage(children: ReactNode): string | null {
|
|
|
406
451
|
}
|
|
407
452
|
|
|
408
453
|
function InteractiveCodeBlock({ children, node }: ComponentPropsWithoutRef<"pre"> & ExtraProps) {
|
|
409
|
-
const { source, render } = useContext(InteractiveContext);
|
|
454
|
+
const { source, render, streaming } = useContext(InteractiveContext);
|
|
410
455
|
const language = fenceLanguage(children);
|
|
411
456
|
if (render && (language === "opengeni-html" || language === "opengeni-site")) {
|
|
412
457
|
const start = node?.position?.start.offset;
|
|
@@ -428,7 +473,23 @@ function InteractiveCodeBlock({ children, node }: ComponentPropsWithoutRef<"pre"
|
|
|
428
473
|
closing &&
|
|
429
474
|
closing.length >= opening[1]!.length &&
|
|
430
475
|
[...closing].every((c) => c === opening[1]![0]);
|
|
431
|
-
if (!complete)
|
|
476
|
+
if (!complete) {
|
|
477
|
+
return (
|
|
478
|
+
<div role="status" aria-live="polite" aria-busy={streaming === true} className="my-3">
|
|
479
|
+
{streaming ? (
|
|
480
|
+
<PreviewLoading />
|
|
481
|
+
) : (
|
|
482
|
+
<ActivityDisclosure
|
|
483
|
+
icon={<PanelsTopLeftIcon aria-hidden className="size-3.5" />}
|
|
484
|
+
title="Preview incomplete"
|
|
485
|
+
running={false}
|
|
486
|
+
expandable={false}
|
|
487
|
+
preview="Generation stopped before the preview was ready."
|
|
488
|
+
/>
|
|
489
|
+
)}
|
|
490
|
+
</div>
|
|
491
|
+
);
|
|
492
|
+
}
|
|
432
493
|
return (
|
|
433
494
|
<>
|
|
434
495
|
{render({
|
|
@@ -523,6 +584,7 @@ function MarkdownTable({ children, className, ...props }: ComponentPropsWithoutR
|
|
|
523
584
|
|
|
524
585
|
function MarkdownImpl({
|
|
525
586
|
children,
|
|
587
|
+
artifactHref,
|
|
526
588
|
className,
|
|
527
589
|
streaming = false,
|
|
528
590
|
onSandboxFile,
|
|
@@ -617,15 +679,19 @@ function MarkdownImpl({
|
|
|
617
679
|
// fences don't snap to final GFM one commit before the crystallize morph.
|
|
618
680
|
// Reveal identity still tracks the true source (`children`).
|
|
619
681
|
const parseText = streaming || revealActive ? softenStreamingMarkdown(children) : children;
|
|
620
|
-
const components = useMemo(
|
|
682
|
+
const components = useMemo(
|
|
683
|
+
() => markdownComponents(onSandboxFile, artifactHref),
|
|
684
|
+
[onSandboxFile, artifactHref],
|
|
685
|
+
);
|
|
621
686
|
|
|
622
687
|
const interactiveContext = useMemo(
|
|
623
688
|
() => ({
|
|
624
689
|
source: children,
|
|
690
|
+
streaming,
|
|
625
691
|
...(renderInteractiveBlock ? { render: renderInteractiveBlock } : {}),
|
|
626
692
|
...(renderImage ? { renderImage } : {}),
|
|
627
693
|
}),
|
|
628
|
-
[children, renderInteractiveBlock, renderImage],
|
|
694
|
+
[children, streaming, renderInteractiveBlock, renderImage],
|
|
629
695
|
);
|
|
630
696
|
|
|
631
697
|
// `min-w-0` lets the prose shrink inside flex parents (message bubbles) so
|
|
@@ -656,4 +722,62 @@ function MarkdownImpl({
|
|
|
656
722
|
}
|
|
657
723
|
|
|
658
724
|
/** Memoized so streaming re-renders of the parent don't re-parse settled bodies. */
|
|
659
|
-
export const Markdown = memo(
|
|
725
|
+
export const Markdown = memo(function SearchableMarkdown(props: MarkdownProps) {
|
|
726
|
+
const [retained, setRetained] = useState<{ text: string; target: TimelineSearchTarget } | null>(
|
|
727
|
+
null,
|
|
728
|
+
);
|
|
729
|
+
if (
|
|
730
|
+
props.searchTarget &&
|
|
731
|
+
(retained?.target !== props.searchTarget || retained.text !== props.children)
|
|
732
|
+
) {
|
|
733
|
+
setRetained({ text: props.children, target: props.searchTarget });
|
|
734
|
+
}
|
|
735
|
+
// Closing Find only removes the highlight. Retain the source window so a huge
|
|
736
|
+
// formatted message cannot replace it and unexpectedly move the reading point.
|
|
737
|
+
const target = props.searchTarget ?? (retained?.text === props.children ? retained.target : null);
|
|
738
|
+
if (!target) return <MarkdownImpl {...props} />;
|
|
739
|
+
const offset = searchMatchOffset(props.children, target);
|
|
740
|
+
if (offset < 0)
|
|
741
|
+
return props.searchTarget ? (
|
|
742
|
+
<div role="status">This match is no longer in the message source.</div>
|
|
743
|
+
) : (
|
|
744
|
+
<MarkdownImpl {...props} />
|
|
745
|
+
);
|
|
746
|
+
// Bound mounted text even for multi-megabyte messages. Do not split a UTF-16 pair
|
|
747
|
+
// at excerpt edges; the selected range itself is validated against the source.
|
|
748
|
+
let start = Math.max(0, offset - 240);
|
|
749
|
+
let end = Math.min(props.children.length, offset + target.query.length + 240);
|
|
750
|
+
if (start > 0 && /[\uDC00-\uDFFF]/.test(props.children[start]!)) start--;
|
|
751
|
+
if (end < props.children.length && /[\uDC00-\uDFFF]/.test(props.children[end]!)) end++;
|
|
752
|
+
const MatchTag = props.searchTarget ? "mark" : "span";
|
|
753
|
+
return (
|
|
754
|
+
<div className={cn("og-markdown-body min-w-0 break-words", props.className)}>
|
|
755
|
+
<div data-og-annotation-chrome="" className="text-xs opacity-70">
|
|
756
|
+
Match in message source · Excerpt stays in place when Find closes
|
|
757
|
+
<button
|
|
758
|
+
type="button"
|
|
759
|
+
disabled={!!props.searchTarget}
|
|
760
|
+
title={props.searchTarget ? "Close Find to show the formatted message" : undefined}
|
|
761
|
+
className="ml-2 inline-flex min-h-7 items-center rounded-og-sm px-1.5 text-og-xs font-medium text-og-fg-muted outline-hidden hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/45 pointer-coarse:min-h-11 disabled:opacity-50"
|
|
762
|
+
onClick={() => setRetained(null)}
|
|
763
|
+
>
|
|
764
|
+
Show formatted message
|
|
765
|
+
</button>
|
|
766
|
+
</div>
|
|
767
|
+
<div style={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}>
|
|
768
|
+
{start > 0 ? "…" : ""}
|
|
769
|
+
{props.children.slice(start, offset)}
|
|
770
|
+
<MatchTag
|
|
771
|
+
data-og-search-occurrence={target.occurrence ?? 0}
|
|
772
|
+
data-og-search-sequence={target.sequence}
|
|
773
|
+
data-og-search-query={target.query}
|
|
774
|
+
data-og-search-offset={offset}
|
|
775
|
+
>
|
|
776
|
+
{props.children.slice(offset, offset + target.query.length)}
|
|
777
|
+
</MatchTag>
|
|
778
|
+
{props.children.slice(offset + target.query.length, end)}
|
|
779
|
+
{end < props.children.length ? "…" : ""}
|
|
780
|
+
</div>
|
|
781
|
+
</div>
|
|
782
|
+
);
|
|
783
|
+
});
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { RollingActivity } from "../timeline/rolling-activity";
|
|
2
|
+
import {
|
|
3
|
+
isTimelineSearchTarget,
|
|
4
|
+
TimelineSearchRevealContext,
|
|
5
|
+
useTimelineSearchNavigation,
|
|
6
|
+
type TimelineSearchTarget,
|
|
7
|
+
} from "./timeline-search";
|
|
2
8
|
import { GenieLoadingOptionsContext, type GenieLoadingOptions } from "../timeline/genie-loading";
|
|
3
9
|
import { ChildSessionLink } from "./child-session-link";
|
|
4
10
|
import { useStartupDetails } from "../timeline/startup-preference";
|
|
5
11
|
import { parseSandboxFileArtifactReceipt } from "@opengeni/sdk";
|
|
6
12
|
import { unwrapMcpOutput } from "../timeline/parsers";
|
|
13
|
+
import { compactionSkipSubtitle } from "../timeline/compaction-copy";
|
|
7
14
|
import { isRetainedImageContentType } from "../timeline/retained-image";
|
|
8
15
|
import { mcpToolLeaf } from "../timeline/tool-display-name";
|
|
9
16
|
import type {
|
|
@@ -134,6 +141,8 @@ const TimelineAnnotationSelection = lazy(() => import("./timeline-annotation-sel
|
|
|
134
141
|
const TimelineAnnotationMarkers = lazy(() => import("./timeline-annotation-markers"));
|
|
135
142
|
|
|
136
143
|
export type MessageTimelineProps = {
|
|
144
|
+
/** Exact durable search hit; clearing it removes highlighting without moving the reader. */
|
|
145
|
+
searchTarget?: TimelineSearchTarget | null | undefined;
|
|
137
146
|
/** Localized user-message disclosure actions, including custom UserMessageBody renderers. */
|
|
138
147
|
userMessageDisclosureLabels?: UserMessageDisclosureLabels | undefined;
|
|
139
148
|
/** Raw session events (projected internally) … */
|
|
@@ -146,7 +155,11 @@ export type MessageTimelineProps = {
|
|
|
146
155
|
renderMessageActions?: ((item: AgentMessageItem | UserMessageItem) => ReactNode) | undefined;
|
|
147
156
|
/** Plug a markdown renderer for message bodies (e.g. streamdown). */
|
|
148
157
|
renderMessageText?:
|
|
149
|
-
| ((
|
|
158
|
+
| ((
|
|
159
|
+
text: string,
|
|
160
|
+
item: AgentMessageItem | UserMessageItem,
|
|
161
|
+
context: { searchTarget: TimelineSearchTarget | null },
|
|
162
|
+
) => ReactNode)
|
|
150
163
|
| undefined;
|
|
151
164
|
/** Drill into a spawned worker session. */
|
|
152
165
|
onOpenSession?: ((sessionId: string) => void) | undefined;
|
|
@@ -413,6 +426,7 @@ function cssEscapeAttribute(value: string): string {
|
|
|
413
426
|
*/
|
|
414
427
|
export function MessageTimeline({
|
|
415
428
|
userMessageDisclosureLabels,
|
|
429
|
+
searchTarget,
|
|
416
430
|
events,
|
|
417
431
|
items,
|
|
418
432
|
status: _status,
|
|
@@ -457,6 +471,46 @@ export function MessageTimeline({
|
|
|
457
471
|
(item) => item.kind !== "auth-needed" || shouldRenderAuthNeeded(item),
|
|
458
472
|
);
|
|
459
473
|
}, [items, events, shouldRenderAuthNeeded]);
|
|
474
|
+
const searchItem = searchTarget
|
|
475
|
+
? resolvedItems.find((item) => isTimelineSearchTarget(item, searchTarget))
|
|
476
|
+
: undefined;
|
|
477
|
+
const searchRevealKey =
|
|
478
|
+
searchItem && searchTarget
|
|
479
|
+
? JSON.stringify([
|
|
480
|
+
searchItem.id,
|
|
481
|
+
searchTarget.query,
|
|
482
|
+
searchTarget.occurrence ?? 0,
|
|
483
|
+
searchTarget.offset ?? null,
|
|
484
|
+
])
|
|
485
|
+
: null;
|
|
486
|
+
const searchTargetRef = useRef(searchTarget);
|
|
487
|
+
searchTargetRef.current = searchTarget;
|
|
488
|
+
const renderSearchableMessageText = useMemo(
|
|
489
|
+
() =>
|
|
490
|
+
renderMessageText
|
|
491
|
+
? (text: string, item: AgentMessageItem | UserMessageItem) =>
|
|
492
|
+
renderMessageText(text, item, {
|
|
493
|
+
searchTarget: searchItem?.id === item.id ? (searchTarget ?? null) : null,
|
|
494
|
+
})
|
|
495
|
+
: (text: string, item: AgentMessageItem | UserMessageItem) => {
|
|
496
|
+
const body = (
|
|
497
|
+
<Markdown
|
|
498
|
+
searchTarget={searchItem?.id === item.id ? searchTarget : null}
|
|
499
|
+
streaming={item.kind === "agent-message" && item.streaming}
|
|
500
|
+
>
|
|
501
|
+
{text}
|
|
502
|
+
</Markdown>
|
|
503
|
+
);
|
|
504
|
+
return item.kind === "user-message" ? (
|
|
505
|
+
<UserMessageBody messageId={item.id} text={text}>
|
|
506
|
+
{body}
|
|
507
|
+
</UserMessageBody>
|
|
508
|
+
) : (
|
|
509
|
+
body
|
|
510
|
+
);
|
|
511
|
+
},
|
|
512
|
+
[renderMessageText, searchItem?.id, searchTarget],
|
|
513
|
+
);
|
|
460
514
|
// Event-window identity is independent of projected rows (partial messages
|
|
461
515
|
// can acquire a different first-delta id when older text arrives).
|
|
462
516
|
const sourceItems = events ?? items;
|
|
@@ -1005,7 +1059,7 @@ export function MessageTimeline({
|
|
|
1005
1059
|
userMessageDisclosureContext,
|
|
1006
1060
|
behavior: {
|
|
1007
1061
|
renderMessageActions,
|
|
1008
|
-
renderMessageText,
|
|
1062
|
+
renderMessageText: renderSearchableMessageText,
|
|
1009
1063
|
onOpenSession,
|
|
1010
1064
|
onMemoryClick,
|
|
1011
1065
|
onReconnect,
|
|
@@ -1028,7 +1082,7 @@ export function MessageTimeline({
|
|
|
1028
1082
|
onReconnect,
|
|
1029
1083
|
renderAuthNeeded,
|
|
1030
1084
|
renderMessageActions,
|
|
1031
|
-
|
|
1085
|
+
renderSearchableMessageText,
|
|
1032
1086
|
resolveProviderLogo,
|
|
1033
1087
|
toolRegistry,
|
|
1034
1088
|
genieLoading,
|
|
@@ -1037,6 +1091,19 @@ export function MessageTimeline({
|
|
|
1037
1091
|
],
|
|
1038
1092
|
);
|
|
1039
1093
|
|
|
1094
|
+
const releaseSearchPin = useCallback(() => {
|
|
1095
|
+
applyPinned(false);
|
|
1096
|
+
stopFollow();
|
|
1097
|
+
setRevealed(true);
|
|
1098
|
+
}, [applyPinned, stopFollow]);
|
|
1099
|
+
useTimelineSearchNavigation(
|
|
1100
|
+
scrollRef,
|
|
1101
|
+
searchItem?.id,
|
|
1102
|
+
searchTarget,
|
|
1103
|
+
releaseSearchPin,
|
|
1104
|
+
resolvedItems,
|
|
1105
|
+
);
|
|
1106
|
+
|
|
1040
1107
|
const requestOlderIfUnderfilled = useCallback(
|
|
1041
1108
|
(node: HTMLElement, retry?: OlderLoadAttempt) => {
|
|
1042
1109
|
let currentAttempt = olderLoadAttemptRef.current;
|
|
@@ -1707,6 +1774,8 @@ export function MessageTimeline({
|
|
|
1707
1774
|
// at what just became the live bottom — paging forward to the tip must not
|
|
1708
1775
|
// strand them unpinned watching new content grow below.
|
|
1709
1776
|
useEffect(() => {
|
|
1777
|
+
// Closing find is paint-only; it must not trigger this history-to-tip rule.
|
|
1778
|
+
if (searchTargetRef.current) return;
|
|
1710
1779
|
if (hasNewer) {
|
|
1711
1780
|
stopFollow();
|
|
1712
1781
|
applyPinned(false);
|
|
@@ -2100,23 +2169,31 @@ export function MessageTimeline({
|
|
|
2100
2169
|
) : null}
|
|
2101
2170
|
{groups.map(({ group, key, entranceEnabled }, index) => {
|
|
2102
2171
|
return (
|
|
2103
|
-
<
|
|
2172
|
+
<TimelineSearchRevealContext.Provider
|
|
2104
2173
|
key={key}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
group.kind === "activity" &&
|
|
2110
|
-
group.items.some(
|
|
2111
|
-
(item) => item.turnId && turnsWithOutput.has(item.turnId),
|
|
2112
|
-
)
|
|
2113
|
-
}
|
|
2114
|
-
entranceEnabled={entranceEnabled}
|
|
2115
|
-
liveEntranceEnabled={
|
|
2116
|
-
group.kind === "activity" ? !bulkRender : undefined
|
|
2174
|
+
value={
|
|
2175
|
+
searchItem && timelineGroupItemIds(group).includes(searchItem.id)
|
|
2176
|
+
? searchRevealKey
|
|
2177
|
+
: null
|
|
2117
2178
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2179
|
+
>
|
|
2180
|
+
<TimelineGroupEntry
|
|
2181
|
+
groupKey={key}
|
|
2182
|
+
group={group}
|
|
2183
|
+
nextGroup={groups[index + 1]?.group}
|
|
2184
|
+
startupDismissed={
|
|
2185
|
+
group.kind === "activity" &&
|
|
2186
|
+
group.items.some(
|
|
2187
|
+
(item) => item.turnId && turnsWithOutput.has(item.turnId),
|
|
2188
|
+
)
|
|
2189
|
+
}
|
|
2190
|
+
entranceEnabled={entranceEnabled}
|
|
2191
|
+
liveEntranceEnabled={
|
|
2192
|
+
group.kind === "activity" ? !bulkRender : undefined
|
|
2193
|
+
}
|
|
2194
|
+
context={timelineGroupEntryContext}
|
|
2195
|
+
/>
|
|
2196
|
+
</TimelineSearchRevealContext.Provider>
|
|
2120
2197
|
);
|
|
2121
2198
|
})}
|
|
2122
2199
|
{groups.length > 0 && trailingState ? (
|
|
@@ -2455,7 +2532,9 @@ type TimelineGroupEntryContext = {
|
|
|
2455
2532
|
|
|
2456
2533
|
type TimelineGroupBehaviorProps = {
|
|
2457
2534
|
renderMessageActions: MessageTimelineProps["renderMessageActions"];
|
|
2458
|
-
renderMessageText:
|
|
2535
|
+
renderMessageText:
|
|
2536
|
+
| ((text: string, item: AgentMessageItem | UserMessageItem) => ReactNode)
|
|
2537
|
+
| undefined;
|
|
2459
2538
|
onOpenSession: MessageTimelineProps["onOpenSession"];
|
|
2460
2539
|
onMemoryClick: MessageTimelineProps["onMemoryClick"];
|
|
2461
2540
|
onReconnect: MessageTimelineProps["onReconnect"];
|
|
@@ -3260,7 +3339,7 @@ function CompactionRow({ item }: { item: ContextCompactionItem }) {
|
|
|
3260
3339
|
item.phase === "compacted"
|
|
3261
3340
|
? "Chat history above is unchanged"
|
|
3262
3341
|
: item.phase === "skipped"
|
|
3263
|
-
? compactionSkipSubtitle(item.skipReason)
|
|
3342
|
+
? compactionSkipSubtitle(item.skipReason, item.providerRejection)
|
|
3264
3343
|
: null;
|
|
3265
3344
|
const pill =
|
|
3266
3345
|
item.phase === "skipped" && item.skipReason === "summarization_failed"
|
|
@@ -3290,21 +3369,6 @@ function CompactionRow({ item }: { item: ContextCompactionItem }) {
|
|
|
3290
3369
|
);
|
|
3291
3370
|
}
|
|
3292
3371
|
|
|
3293
|
-
function compactionSkipSubtitle(reason: string | null): string {
|
|
3294
|
-
switch (reason) {
|
|
3295
|
-
case "no_history":
|
|
3296
|
-
return "No active history to compact";
|
|
3297
|
-
case "replacement_not_smaller":
|
|
3298
|
-
return "Checkpoint would not reduce memory size";
|
|
3299
|
-
case "replacement_unchanged":
|
|
3300
|
-
return "Checkpoint made no progress";
|
|
3301
|
-
case "summarization_failed":
|
|
3302
|
-
return "Request it again to retry. Chat history is unchanged.";
|
|
3303
|
-
default:
|
|
3304
|
-
return "Compaction was not needed. Chat history is unchanged.";
|
|
3305
|
-
}
|
|
3306
|
-
}
|
|
3307
|
-
|
|
3308
3372
|
/** Hover-reveal clock beside the copy control (sent / finished). */
|
|
3309
3373
|
function MessageFooterTime({ occurredAt }: { occurredAt: string }) {
|
|
3310
3374
|
return (
|
|
@@ -3355,7 +3419,10 @@ function UserMessageRow({
|
|
|
3355
3419
|
>
|
|
3356
3420
|
<div className={MESSAGE_BUBBLE_CLASS}>
|
|
3357
3421
|
{item.text ? (
|
|
3358
|
-
<div
|
|
3422
|
+
<div
|
|
3423
|
+
data-og-search-item={item.id}
|
|
3424
|
+
data-og-annotation-source-key={item.annotationSource?.eventId}
|
|
3425
|
+
>
|
|
3359
3426
|
{renderMessageText ? (
|
|
3360
3427
|
renderMessageText(item.text, item)
|
|
3361
3428
|
) : (
|
|
@@ -3578,6 +3645,7 @@ function AgentMessageRow({
|
|
|
3578
3645
|
>
|
|
3579
3646
|
<div
|
|
3580
3647
|
data-og-wide-table-message=""
|
|
3648
|
+
data-og-search-item={item.id}
|
|
3581
3649
|
data-og-annotation-source-key={item.annotationSource?.eventId}
|
|
3582
3650
|
>
|
|
3583
3651
|
{body}
|
|
@@ -59,7 +59,14 @@ function providerGroups(models: ClientModel[], codexOnly: boolean) {
|
|
|
59
59
|
optionDisabled: codexOnly && !isCodexClientModel(model),
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
const groups = [...byProvider.values()];
|
|
63
|
+
if (codexOnly) {
|
|
64
|
+
const codexIndex = groups.findIndex((group) =>
|
|
65
|
+
group.models.some((model) => !model.optionDisabled && isCodexClientModel(model)),
|
|
66
|
+
);
|
|
67
|
+
if (codexIndex > 0) groups.unshift(...groups.splice(codexIndex, 1));
|
|
68
|
+
}
|
|
69
|
+
return groups;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
export function ModelPicker({
|
|
@@ -78,8 +85,9 @@ export function ModelPicker({
|
|
|
78
85
|
[rows, codexOnly],
|
|
79
86
|
);
|
|
80
87
|
const billingGroups = useMemo(
|
|
81
|
-
() =>
|
|
82
|
-
|
|
88
|
+
() =>
|
|
89
|
+
effectiveRows.length > 0 ? groupPickerRowsByBillingClass(effectiveRows, { codexOnly }) : [],
|
|
90
|
+
[effectiveRows, codexOnly],
|
|
83
91
|
);
|
|
84
92
|
const providerGroupList = useMemo(() => providerGroups(models, codexOnly), [models, codexOnly]);
|
|
85
93
|
const useBillingGroups = Boolean(effectiveRows.length);
|
|
@@ -34,7 +34,10 @@ export function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {
|
|
|
34
34
|
`${row.label} ${row.id} ${row.providerLabel} ${row.billingClassLabel} ${payerSummaryForModel(row.catalog)}`.toLowerCase();
|
|
35
35
|
return words.every((word) => text.includes(word));
|
|
36
36
|
});
|
|
37
|
-
const
|
|
37
|
+
const matchingIds = new Set(filtered.map((row) => row.id));
|
|
38
|
+
const groups = groupPickerRowsByBillingClass(rows, { codexOnly: props.codexOnly === true })
|
|
39
|
+
.map((group) => ({ ...group, rows: group.rows.filter((row) => matchingIds.has(row.id)) }))
|
|
40
|
+
.filter((group) => group.rows.length > 0);
|
|
38
41
|
const choose = (row: ClientPickerModelRow) => {
|
|
39
42
|
if (!row.selectable || props.disabled) return;
|
|
40
43
|
if (row.id !== props.model) props.onModelChange(row.id);
|
|
@@ -139,8 +142,12 @@ export function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {
|
|
|
139
142
|
) : (
|
|
140
143
|
<>
|
|
141
144
|
{groups.map((group) => (
|
|
142
|
-
<section
|
|
143
|
-
|
|
145
|
+
<section
|
|
146
|
+
key={group.billingClass}
|
|
147
|
+
aria-label={group.label}
|
|
148
|
+
className="py-2.5 first:pt-1 [&+section]:border-t [&+section]:border-og-border"
|
|
149
|
+
>
|
|
150
|
+
<div className="flex items-center gap-2 px-2.5 py-1.5 text-og-control font-semibold text-og-fg">
|
|
144
151
|
<BillingClassMark billingClass={group.billingClass} aria-label="" />
|
|
145
152
|
{group.label}
|
|
146
153
|
</div>
|