@opengeni/react 0.46.1 → 0.52.1
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 +158 -14
- package/dist/artifacts-document.d.ts +3 -0
- package/dist/artifacts-document.js +17 -0
- package/dist/artifacts-document.js.map +1 -0
- package/dist/artifacts-presentation.d.ts +3 -0
- package/dist/artifacts-presentation.js +17 -0
- package/dist/artifacts-presentation.js.map +1 -0
- package/dist/artifacts-spreadsheet.d.ts +3 -0
- package/dist/artifacts-spreadsheet.js +17 -0
- package/dist/artifacts-spreadsheet.js.map +1 -0
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +170 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/chunk-6BBWWOOU.js +82 -0
- package/dist/chunk-6BBWWOOU.js.map +1 -0
- package/dist/{chunk-HFO4ERGQ.js → chunk-724BW4TW.js} +586 -35
- package/dist/chunk-724BW4TW.js.map +1 -0
- package/dist/chunk-7I2KB3OK.js +3895 -0
- package/dist/chunk-7I2KB3OK.js.map +1 -0
- package/dist/chunk-7WCLAEFV.js +2828 -0
- package/dist/chunk-7WCLAEFV.js.map +1 -0
- package/dist/{chunk-U3CSCMWA.js → chunk-FK6VG4LL.js} +9 -13
- package/dist/chunk-FK6VG4LL.js.map +1 -0
- package/dist/{chunk-SRFUT2ZU.js → chunk-FWS3KC3Z.js} +120 -64
- package/dist/chunk-FWS3KC3Z.js.map +1 -0
- package/dist/{chunk-EKZH6IDG.js → chunk-HDBG7XGO.js} +62 -1
- package/dist/chunk-HDBG7XGO.js.map +1 -0
- package/dist/chunk-HUIKIDM5.js +2148 -0
- package/dist/chunk-HUIKIDM5.js.map +1 -0
- package/dist/{chunk-LGVMUIRV.js → chunk-JAGDUCZQ.js} +1053 -390
- package/dist/chunk-JAGDUCZQ.js.map +1 -0
- package/dist/{chunk-LWR4MXSS.js → chunk-JR3QFY6B.js} +420 -35
- package/dist/chunk-JR3QFY6B.js.map +1 -0
- package/dist/chunk-JVYQRRC4.js +53 -0
- package/dist/chunk-JVYQRRC4.js.map +1 -0
- package/dist/chunk-KCGXPP5V.js +157 -0
- package/dist/chunk-KCGXPP5V.js.map +1 -0
- package/dist/{chunk-EB67J347.js → chunk-KZ73RL76.js} +111 -31
- package/dist/chunk-KZ73RL76.js.map +1 -0
- package/dist/chunk-PEI6RANI.js +3234 -0
- package/dist/chunk-PEI6RANI.js.map +1 -0
- package/dist/{chunk-3PB7MIT6.js → chunk-ROIRTM37.js} +220 -72
- package/dist/chunk-ROIRTM37.js.map +1 -0
- package/dist/chunk-VZTQ73XT.js +86 -0
- package/dist/chunk-VZTQ73XT.js.map +1 -0
- package/dist/{chunk-VCBFUSUZ.js → chunk-WSK7G5LE.js} +72 -4
- package/dist/chunk-WSK7G5LE.js.map +1 -0
- package/dist/{chunk-FGZUCXZF.js → chunk-WYA65Y3F.js} +16 -16
- package/dist/chunk-WYA65Y3F.js.map +1 -0
- package/dist/{chunk-6EDV6YFE.js → chunk-X4S56Y2H.js} +147 -35
- package/dist/chunk-X4S56Y2H.js.map +1 -0
- package/dist/client.d.ts +7 -1
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +7 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/commands/index.d.ts +3 -3
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/types.d.ts +1 -1
- package/dist/components/approval-surface.d.ts +2 -2
- package/dist/components/artifacts/artifact-surface.d.ts +18 -0
- package/dist/components/artifacts/document-editor.d.ts +172 -0
- package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
- package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
- package/dist/components/artifacts/editable-document.d.ts +24 -0
- package/dist/components/artifacts/editable-presentation.d.ts +24 -0
- package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
- package/dist/components/artifacts/index.d.ts +8 -0
- package/dist/components/artifacts/presentation-editor.d.ts +297 -0
- package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
- package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
- package/dist/components/browser-viewer.d.ts +29 -0
- package/dist/components/chat-composer.d.ts +13 -8
- package/dist/components/code-editor.d.ts +25 -3
- package/dist/components/command-palette.d.ts +2 -2
- package/dist/components/composer-transcription-control.d.ts +4 -2
- package/dist/components/composer.d.ts +29 -15
- package/dist/components/computer-viewer.d.ts +23 -0
- package/dist/components/copy-button.d.ts +2 -2
- package/dist/components/desktop-viewer.d.ts +25 -21
- package/dist/components/diff-view.d.ts +1 -1
- package/dist/components/enrollment-consent.d.ts +1 -1
- package/dist/components/enrollment-device-flow.d.ts +1 -1
- package/dist/components/file-browser.d.ts +2 -2
- package/dist/components/fleet-tile.d.ts +1 -1
- package/dist/components/generated-video-player.d.ts +10 -0
- package/dist/components/human-input-form.d.ts +1 -1
- package/dist/components/human-input-surface.d.ts +2 -2
- package/dist/components/interactive-workbench-viewers.d.ts +2 -0
- package/dist/components/machine-card.d.ts +2 -2
- package/dist/components/machine-dock-bar.d.ts +3 -3
- package/dist/components/machine-health-pill.d.ts +2 -2
- package/dist/components/machine-input-display.d.ts +1 -1
- package/dist/components/machine-metrics.d.ts +2 -2
- package/dist/components/machine-status-pill.d.ts +4 -4
- package/dist/components/machines/health.d.ts +1 -1
- package/dist/components/machines/machine-detail.d.ts +5 -3
- package/dist/components/machines/metric-history-chart.d.ts +1 -1
- package/dist/components/machines/metric-sparkline.d.ts +2 -2
- package/dist/components/machines/series.d.ts +2 -2
- package/dist/components/machines-dashboard.d.ts +5 -5
- package/dist/components/markdown.d.ts +1 -1
- package/dist/components/message-timeline.d.ts +22 -5
- package/dist/components/model-picker.d.ts +2 -2
- package/dist/components/model-policy-picker.d.ts +7 -7
- package/dist/components/pierre-diff.d.ts +1 -1
- package/dist/components/pierre-file.d.ts +1 -1
- package/dist/components/queue-draft-policy.d.ts +1 -1
- package/dist/components/queue-surface-implementation.d.ts +3 -3
- package/dist/components/queue-surface-state.d.ts +4 -4
- package/dist/components/queue-surface.d.ts +5 -5
- package/dist/components/sandbox-files.d.ts +5 -5
- package/dist/components/sandbox-terminal.d.ts +50 -4
- package/dist/components/sandbox-workspace.d.ts +30 -10
- package/dist/components/session-chrome.d.ts +4 -4
- package/dist/components/session-status.d.ts +2 -2
- package/dist/components/timeline-annotation-selection.d.ts +9 -0
- package/dist/components/timeline-annotations-dialog.d.ts +16 -0
- package/dist/components/timeline-annotations.d.ts +13 -0
- package/dist/components/tooltip.d.ts +4 -4
- package/dist/components/user-message-body.d.ts +2 -2
- package/dist/components/workbench-changes.d.ts +2 -2
- package/dist/components/workspace-dock.d.ts +3 -1
- package/dist/composer.d.ts +11 -11
- package/dist/composer.js +7 -7
- package/dist/hooks/internal.d.ts +7 -1
- package/dist/hooks/use-attached-browsers.d.ts +21 -0
- package/dist/hooks/use-available-models.d.ts +2 -2
- package/dist/hooks/use-billing-usage.d.ts +1 -1
- package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
- package/dist/hooks/use-browser-identities.d.ts +26 -0
- package/dist/hooks/use-browser-session.d.ts +28 -0
- package/dist/hooks/use-browser-sessions.d.ts +32 -0
- package/dist/hooks/use-codex-accounts.d.ts +3 -3
- package/dist/hooks/use-composer.d.ts +36 -9
- package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
- package/dist/hooks/use-computer-session.d.ts +28 -0
- package/dist/hooks/use-computer-sessions.d.ts +28 -0
- package/dist/hooks/use-desktop-stream.d.ts +1 -1
- package/dist/hooks/use-environments.d.ts +11 -5
- package/dist/hooks/use-file-attachments.d.ts +1 -1
- package/dist/hooks/use-goal.d.ts +2 -2
- package/dist/hooks/use-human-input.d.ts +2 -2
- package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
- package/dist/hooks/use-machine-chip.d.ts +2 -2
- package/dist/hooks/use-machines.d.ts +10 -3
- package/dist/hooks/use-packs.d.ts +7 -2
- package/dist/hooks/use-rigs.d.ts +1 -1
- package/dist/hooks/use-sandbox-files.d.ts +25 -4
- package/dist/hooks/use-sandbox-git.d.ts +13 -5
- package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
- package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
- package/dist/hooks/use-session-capabilities.d.ts +5 -4
- package/dist/hooks/use-session-control.d.ts +1 -1
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/hooks/use-session-lineage.d.ts +1 -1
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-session.d.ts +2 -2
- package/dist/hooks/use-slash-commands.d.ts +1 -1
- package/dist/hooks/use-terminal-stream.d.ts +8 -2
- package/dist/hooks/use-transcription.d.ts +2 -2
- package/dist/hooks/use-turn-queue.d.ts +2 -2
- package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
- package/dist/hooks/use-voice-input.d.ts +6 -4
- package/dist/hooks/use-workspace-capture.d.ts +1 -1
- package/dist/hooks/use-workspace-edit.d.ts +1 -1
- package/dist/hooks/use-workspace-sessions.d.ts +1 -1
- package/dist/hooks/use-workspaces.d.ts +1 -1
- package/dist/index.d.ts +158 -154
- package/dist/index.js +1718 -908
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +23 -0
- package/dist/interaction.js +36 -0
- package/dist/interaction.js.map +1 -0
- package/dist/interactive-workbench-viewers-KHNOHWQE.js +13 -0
- package/dist/interactive-workbench-viewers-KHNOHWQE.js.map +1 -0
- package/dist/lib/composer-responsive-context.d.ts +9 -0
- package/dist/lib/relay-wire.d.ts +4 -0
- package/dist/lib/sandbox-liveness.d.ts +9 -0
- package/dist/lib/terminal-capability.d.ts +5 -0
- package/dist/lib/use-portal-token-style.d.ts +14 -3
- package/dist/lib/xterm-theme.d.ts +1 -1
- package/dist/machines.d.ts +30 -30
- package/dist/machines.js +3 -3
- package/dist/provider.d.ts +4 -4
- package/dist/{queue-surface-implementation-J4XJFKRW.js → queue-surface-implementation-2KVJTYXH.js} +117 -64
- package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
- package/dist/realtime/dropdown-menu.d.ts +8 -8
- package/dist/realtime/realtime-control.d.ts +15 -6
- package/dist/realtime.d.ts +4 -4
- package/dist/realtime.js +30 -33
- package/dist/realtime.js.map +1 -1
- package/dist/session-context.d.ts +19 -1
- package/dist/session-ui.d.ts +14 -14
- package/dist/session-ui.js +6 -5
- package/dist/session.d.ts +28 -28
- package/dist/session.js +5 -5
- package/dist/timeline/activity-rail.d.ts +6 -3
- package/dist/timeline/disclosure-context.d.ts +1 -1
- package/dist/timeline/fleet-decision-projection.d.ts +1 -1
- package/dist/timeline/fleet-decision-row.d.ts +3 -3
- package/dist/timeline/index.d.ts +19 -19
- package/dist/timeline/parsers.d.ts +6 -3
- package/dist/timeline/projection.d.ts +3 -3
- package/dist/timeline/registry.d.ts +10 -1
- package/dist/timeline/screenshot-lightbox.d.ts +2 -2
- package/dist/timeline/shared.d.ts +21 -10
- package/dist/timeline/tool-diff.d.ts +2 -2
- package/dist/timeline/tool-renderers.d.ts +1 -1
- package/dist/timeline/turn-summary.d.ts +3 -3
- package/dist/timeline/types.d.ts +42 -2
- package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
- package/dist/voice-recording-store.d.ts +8 -2
- package/package.json +52 -7
- package/src/artifacts-document.ts +31 -0
- package/src/artifacts-presentation.ts +36 -0
- package/src/artifacts-spreadsheet.ts +21 -0
- package/src/artifacts.ts +86 -0
- package/src/client.ts +90 -1
- package/src/clipboard.ts +2 -0
- package/src/components/artifacts/artifact-surface.tsx +93 -0
- package/src/components/artifacts/document-editor.tsx +2255 -0
- package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
- package/src/components/artifacts/editable-document.tsx +657 -0
- package/src/components/artifacts/editable-presentation.tsx +1020 -0
- package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
- package/src/components/artifacts/index.ts +99 -0
- package/src/components/artifacts/presentation-editor.tsx +3148 -0
- package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
- package/src/components/browser-viewer.tsx +1592 -0
- package/src/components/chat-composer.tsx +25 -2
- package/src/components/code-editor.tsx +90 -31
- package/src/components/command-palette.tsx +1 -1
- package/src/components/composer-transcription-control.tsx +51 -22
- package/src/components/composer.tsx +49 -23
- package/src/components/computer-viewer.tsx +1139 -0
- package/src/components/copy-button.tsx +1 -1
- package/src/components/desktop-viewer.tsx +103 -75
- package/src/components/file-browser.tsx +39 -17
- package/src/components/generated-video-player.tsx +89 -0
- package/src/components/human-input-form.tsx +2 -2
- package/src/components/interactive-workbench-viewers.ts +2 -0
- package/src/components/machine-card.tsx +1 -1
- package/src/components/machine-input-display.ts +3 -0
- package/src/components/machines/machine-detail.tsx +39 -15
- package/src/components/machines-dashboard.tsx +7 -6
- package/src/components/message-timeline.tsx +327 -37
- package/src/components/model-picker.tsx +2 -2
- package/src/components/model-policy-picker.tsx +16 -15
- package/src/components/queue-surface-implementation.tsx +135 -75
- package/src/components/queue-surface-state.tsx +2 -2
- package/src/components/sandbox-files.tsx +142 -62
- package/src/components/sandbox-terminal.tsx +119 -35
- package/src/components/sandbox-workspace.tsx +394 -228
- package/src/components/session-chrome.tsx +67 -27
- package/src/components/timeline-annotation-selection.tsx +221 -0
- package/src/components/timeline-annotations-dialog.tsx +213 -0
- package/src/components/timeline-annotations.tsx +84 -0
- package/src/components/tooltip.tsx +13 -16
- package/src/components/user-message-body.tsx +1 -1
- package/src/components/workbench-changes.tsx +192 -35
- package/src/components/workspace-dock.tsx +13 -5
- package/src/composer.ts +3 -0
- package/src/hooks/internal.ts +132 -38
- package/src/hooks/use-attached-browsers.ts +152 -0
- package/src/hooks/use-browser-frame-stream.ts +316 -0
- package/src/hooks/use-browser-identities.ts +225 -0
- package/src/hooks/use-browser-session.ts +476 -0
- package/src/hooks/use-browser-sessions.ts +281 -0
- package/src/hooks/use-codex-accounts.ts +4 -4
- package/src/hooks/use-composer.ts +769 -24
- package/src/hooks/use-computer-frame-stream.ts +311 -0
- package/src/hooks/use-computer-session.ts +442 -0
- package/src/hooks/use-computer-sessions.ts +245 -0
- package/src/hooks/use-environments.ts +24 -5
- package/src/hooks/use-file-attachments.ts +70 -32
- package/src/hooks/use-goal.ts +17 -5
- package/src/hooks/use-machine-chip.ts +5 -3
- package/src/hooks/use-machines.ts +79 -3
- package/src/hooks/use-packs.ts +51 -0
- package/src/hooks/use-sandbox-files.ts +382 -88
- package/src/hooks/use-sandbox-git.ts +208 -46
- package/src/hooks/use-sandbox-terminal.ts +3 -2
- package/src/hooks/use-session-capabilities.ts +94 -70
- package/src/hooks/use-session-events.ts +18 -4
- package/src/hooks/use-terminal-stream.ts +102 -23
- package/src/hooks/use-transcription.ts +4 -14
- package/src/hooks/use-turn-queue.ts +17 -5
- package/src/hooks/use-video-artifact-playback.ts +28 -0
- package/src/hooks/use-voice-input.ts +163 -19
- package/src/hooks/use-windowed-sections.ts +9 -7
- package/src/hooks/use-workspace-edit.ts +2 -1
- package/src/index.ts +21 -6
- package/src/interaction.ts +71 -0
- package/src/lib/composer-responsive-context.ts +15 -0
- package/src/lib/relay-wire.ts +4 -0
- package/src/lib/sandbox-liveness.ts +11 -0
- package/src/lib/terminal-capability.ts +10 -0
- package/src/lib/use-portal-token-style.ts +60 -15
- package/src/provider.tsx +52 -7
- package/src/realtime/dropdown-menu.tsx +15 -19
- package/src/realtime/realtime-control.tsx +16 -5
- package/src/session-context.ts +90 -0
- package/src/timeline/activity-rail.tsx +31 -6
- package/src/timeline/index.ts +4 -1
- package/src/timeline/parsers.ts +46 -20
- package/src/timeline/projection.ts +233 -13
- package/src/timeline/registry.ts +19 -0
- package/src/timeline/screenshot-lightbox.tsx +30 -9
- package/src/timeline/shared.tsx +59 -11
- package/src/timeline/tool-renderers.tsx +421 -24
- package/src/timeline/turn-summary.tsx +2 -2
- package/src/timeline/types.ts +58 -2
- package/src/voice-recording-store.ts +15 -0
- package/styles/compiled.css +5298 -0
- package/styles/compiled.d.ts +2 -0
- package/styles/effective-tokens.css +27 -0
- package/styles/index.css +18 -0
- package/styles/responsive.css +208 -0
- package/styles/responsive.d.ts +2 -0
- package/styles/tokens.css +50 -17
- package/dist/chunk-3PB7MIT6.js.map +0 -1
- package/dist/chunk-6EDV6YFE.js.map +0 -1
- package/dist/chunk-EB67J347.js.map +0 -1
- package/dist/chunk-EKZH6IDG.js.map +0 -1
- package/dist/chunk-FGZUCXZF.js.map +0 -1
- package/dist/chunk-HFO4ERGQ.js.map +0 -1
- package/dist/chunk-JB5725BI.js +0 -48
- package/dist/chunk-JB5725BI.js.map +0 -1
- package/dist/chunk-LGVMUIRV.js.map +0 -1
- package/dist/chunk-LWR4MXSS.js.map +0 -1
- package/dist/chunk-SRFUT2ZU.js.map +0 -1
- package/dist/chunk-U3CSCMWA.js.map +0 -1
- package/dist/chunk-VCBFUSUZ.js.map +0 -1
- package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
- package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
- package/dist/queue-surface-implementation-J4XJFKRW.js.map +0 -1
package/src/timeline/parsers.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
normalizeMcpOutput,
|
|
3
|
+
parseGeneratedImageReceipt,
|
|
4
|
+
type GeneratedImageReceipt,
|
|
5
|
+
type GitFileDiff,
|
|
6
|
+
type RetainedArtifactMetadata,
|
|
7
|
+
} from "@opengeni/sdk";
|
|
2
8
|
import { tryParseJson } from "../lib/format";
|
|
3
9
|
|
|
10
|
+
export type { GeneratedImageReceipt };
|
|
11
|
+
|
|
4
12
|
/* ----------------------------------------------------------------------------
|
|
5
13
|
Pure parsers for the provider-native tool shapes that the timeline renders.
|
|
6
14
|
|
|
@@ -437,25 +445,6 @@ export function isApplyPatch(item: { name: string; raw: unknown }): boolean {
|
|
|
437
445
|
return name === "apply_patch_call" || name === "apply_patch" || name.endsWith("__apply_patch");
|
|
438
446
|
}
|
|
439
447
|
|
|
440
|
-
/* --- secret redaction ------------------------------------------------------- */
|
|
441
|
-
|
|
442
|
-
const SECRET_KEY = /^(value|secret|token|password|api[_-]?key|signing[_-]?key)$/i;
|
|
443
|
-
|
|
444
|
-
/** Deep-redact secret-looking values so arguments never leak a key into the UI. */
|
|
445
|
-
export function redactSecrets(value: unknown): unknown {
|
|
446
|
-
if (Array.isArray(value)) {
|
|
447
|
-
return value.map(redactSecrets);
|
|
448
|
-
}
|
|
449
|
-
if (value && typeof value === "object") {
|
|
450
|
-
const out: Record<string, unknown> = {};
|
|
451
|
-
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
452
|
-
out[k] = SECRET_KEY.test(k) ? "••••" : redactSecrets(v);
|
|
453
|
-
}
|
|
454
|
-
return out;
|
|
455
|
-
}
|
|
456
|
-
return value;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
448
|
/** Parse tool arguments that may arrive as a JSON string or an object. */
|
|
460
449
|
export function parseToolArgs(args: unknown): Record<string, unknown> {
|
|
461
450
|
if (args == null) {
|
|
@@ -558,6 +547,43 @@ export function screenshotDataUrl(out: unknown): string | null {
|
|
|
558
547
|
return null;
|
|
559
548
|
}
|
|
560
549
|
|
|
550
|
+
/** Parse the closed retained-screenshot receipt carried by new tool events. */
|
|
551
|
+
export function retainedScreenshotMetadata(out: unknown): RetainedArtifactMetadata | null {
|
|
552
|
+
if (typeof out === "string" && (out.startsWith("{") || out.startsWith("["))) {
|
|
553
|
+
const parsed = tryParseJson(out);
|
|
554
|
+
if (parsed !== undefined && parsed !== out) return retainedScreenshotMetadata(parsed);
|
|
555
|
+
}
|
|
556
|
+
if (Array.isArray(out)) {
|
|
557
|
+
for (const entry of out) {
|
|
558
|
+
const metadata = retainedScreenshotMetadata(entry);
|
|
559
|
+
if (metadata) return metadata;
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
if (!out || typeof out !== "object") return null;
|
|
564
|
+
const value = out as Record<string, unknown>;
|
|
565
|
+
if (typeof value.artifactId !== "string" || typeof value.available !== "boolean") return null;
|
|
566
|
+
if (!value.available) {
|
|
567
|
+
return typeof value.reason === "string" ? (value as unknown as RetainedArtifactMetadata) : null;
|
|
568
|
+
}
|
|
569
|
+
return value.kind === "computer_screenshot" &&
|
|
570
|
+
value.contentType === "image/png" &&
|
|
571
|
+
typeof value.originalBytes === "number" &&
|
|
572
|
+
typeof value.sha256 === "string" &&
|
|
573
|
+
value.dimensions !== null &&
|
|
574
|
+
typeof value.dimensions === "object" &&
|
|
575
|
+
value.retention !== null &&
|
|
576
|
+
typeof value.retention === "object" &&
|
|
577
|
+
(value.retention as Record<string, unknown>).policy === "session_screenshot"
|
|
578
|
+
? (value as unknown as RetainedArtifactMetadata)
|
|
579
|
+
: null;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/** Parse the closed permanent generated-image receipt from native/function tools. */
|
|
583
|
+
export function generatedImageReceipt(out: unknown): GeneratedImageReceipt | null {
|
|
584
|
+
return parseGeneratedImageReceipt(out);
|
|
585
|
+
}
|
|
586
|
+
|
|
561
587
|
export type TimelineMediaPreview = {
|
|
562
588
|
type: "media_preview";
|
|
563
589
|
mediaType: string;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
parseMediaGenerationResult,
|
|
3
|
+
type SessionEvent,
|
|
4
|
+
type SessionStatus,
|
|
5
|
+
type TimelineAnnotation,
|
|
6
|
+
} from "@opengeni/sdk";
|
|
7
|
+
import fleetDecisionItem from "./fleet-decision-projection";
|
|
3
8
|
import {
|
|
4
9
|
CREDIT_EXHAUSTION_MESSAGE,
|
|
5
10
|
humanizeFailureReason,
|
|
@@ -21,6 +26,7 @@ import type {
|
|
|
21
26
|
TimelineItem,
|
|
22
27
|
TurnEndItem,
|
|
23
28
|
ToolCallItem,
|
|
29
|
+
ToolCallTruncation,
|
|
24
30
|
WorkerItem,
|
|
25
31
|
} from "./types";
|
|
26
32
|
|
|
@@ -61,6 +67,56 @@ const WORKER_MESSAGE_TOOL = "session_send_message";
|
|
|
61
67
|
* render as machine-input rows.
|
|
62
68
|
*/
|
|
63
69
|
const LANDMARK_ONLY_TOOL_LEAVES = new Set(["memory_save", "memory_correct"]);
|
|
70
|
+
const TIMELINE_ANNOTATION_ANSI_SEQUENCE = new RegExp("\\u001B\\[[0-?]*[ -/]*[@-~]", "g");
|
|
71
|
+
|
|
72
|
+
function safeTimelineAnnotationJson(value: unknown): string {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.stringify(value) ?? "";
|
|
75
|
+
} catch {
|
|
76
|
+
return String(value ?? "");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function timelineAnnotationToolOutputValue(value: unknown, depth = 0): string {
|
|
81
|
+
if (depth > 8 || value === null || value === undefined) {
|
|
82
|
+
return value == null ? "" : safeTimelineAnnotationJson(value);
|
|
83
|
+
}
|
|
84
|
+
if (typeof value === "string") return value;
|
|
85
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
86
|
+
return safeTimelineAnnotationJson(value);
|
|
87
|
+
}
|
|
88
|
+
const record = value as Record<string, unknown>;
|
|
89
|
+
if (record.type === "text" && typeof record.text === "string") return record.text;
|
|
90
|
+
if (Array.isArray(record.content)) {
|
|
91
|
+
const text = record.content.find(
|
|
92
|
+
(part) =>
|
|
93
|
+
part !== null &&
|
|
94
|
+
typeof part === "object" &&
|
|
95
|
+
(part as { type?: unknown }).type === "text" &&
|
|
96
|
+
typeof (part as { text?: unknown }).text === "string",
|
|
97
|
+
) as { text: string } | undefined;
|
|
98
|
+
if (text) return text.text;
|
|
99
|
+
}
|
|
100
|
+
if ("structuredContent" in record) {
|
|
101
|
+
return timelineAnnotationToolOutputValue(record.structuredContent, depth + 1);
|
|
102
|
+
}
|
|
103
|
+
if ("result" in record) return timelineAnnotationToolOutputValue(record.result, depth + 1);
|
|
104
|
+
return safeTimelineAnnotationJson(value);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Browser mirror of the server's canonical annotation source projection. */
|
|
108
|
+
function timelineAnnotationToolOutputText(output: unknown): string | null {
|
|
109
|
+
const raw = timelineAnnotationToolOutputValue(output);
|
|
110
|
+
const marker = raw.indexOf("\nOutput:\n");
|
|
111
|
+
const text = (
|
|
112
|
+
marker >= 0
|
|
113
|
+
? raw.slice(marker + "\nOutput:\n".length)
|
|
114
|
+
: raw.startsWith("Output:\n")
|
|
115
|
+
? raw.slice("Output:\n".length)
|
|
116
|
+
: raw
|
|
117
|
+
).replace(TIMELINE_ANNOTATION_ANSI_SEQUENCE, "");
|
|
118
|
+
return text.length > 0 ? text : null;
|
|
119
|
+
}
|
|
64
120
|
|
|
65
121
|
export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
66
122
|
const items: TimelineItem[] = [];
|
|
@@ -134,6 +190,15 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
134
190
|
kind: "user-message",
|
|
135
191
|
id: event.id,
|
|
136
192
|
text: voiceMessage?.text ?? stringValue(payload.text),
|
|
193
|
+
annotations: timelineAnnotations(payload.annotations),
|
|
194
|
+
annotationSource: {
|
|
195
|
+
kind: "user_message",
|
|
196
|
+
eventId: event.id,
|
|
197
|
+
eventType: "user.message",
|
|
198
|
+
sequence: event.sequence,
|
|
199
|
+
turnId,
|
|
200
|
+
text: voiceMessage?.text ?? stringValue(payload.text),
|
|
201
|
+
},
|
|
137
202
|
...(voiceMessage ? { presentation: voiceMessage.presentation } : {}),
|
|
138
203
|
resources: resourceRefs(payload.resources),
|
|
139
204
|
tools: toolRefs(payload.tools),
|
|
@@ -212,6 +277,17 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
212
277
|
// Completion time is what the footer shows ("finished at"); keep the
|
|
213
278
|
// first-delta stamp only until this event arrives.
|
|
214
279
|
open.occurredAt = event.occurredAt;
|
|
280
|
+
open.annotationSource =
|
|
281
|
+
text && open.text === text
|
|
282
|
+
? {
|
|
283
|
+
kind: "assistant_message",
|
|
284
|
+
eventId: event.id,
|
|
285
|
+
eventType: "agent.message.completed",
|
|
286
|
+
sequence: event.sequence,
|
|
287
|
+
turnId,
|
|
288
|
+
text,
|
|
289
|
+
}
|
|
290
|
+
: undefined;
|
|
215
291
|
// The SDK can emit a hosted-tool item only after its provider-native
|
|
216
292
|
// operation has completed, even though answer deltas were already
|
|
217
293
|
// streamed. The completed message event is the durable ordering
|
|
@@ -231,6 +307,14 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
231
307
|
text,
|
|
232
308
|
streaming: false,
|
|
233
309
|
occurredAt: event.occurredAt,
|
|
310
|
+
annotationSource: {
|
|
311
|
+
kind: "assistant_message",
|
|
312
|
+
eventId: event.id,
|
|
313
|
+
eventType: "agent.message.completed",
|
|
314
|
+
sequence: event.sequence,
|
|
315
|
+
turnId,
|
|
316
|
+
text,
|
|
317
|
+
},
|
|
234
318
|
});
|
|
235
319
|
}
|
|
236
320
|
break;
|
|
@@ -312,6 +396,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
312
396
|
name,
|
|
313
397
|
arguments: args,
|
|
314
398
|
output: undefined,
|
|
399
|
+
truncation: null,
|
|
315
400
|
// The provider-native item drives the per-tool renderers (apply_patch
|
|
316
401
|
// operation, computer_call action, web_search providerData, …).
|
|
317
402
|
raw: payload.raw,
|
|
@@ -336,8 +421,30 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
336
421
|
}
|
|
337
422
|
// An output carrying an explicit error flag (or an MCP isError result)
|
|
338
423
|
// settles the tool to "failed" so the renderer can surface it loudly.
|
|
424
|
+
const truncation = toolCallTruncation(payload);
|
|
339
425
|
target.status = isErrorOutput(payload) ? "failed" : "complete";
|
|
340
|
-
target.output = payload
|
|
426
|
+
target.output = Object.prototype.hasOwnProperty.call(payload, "output")
|
|
427
|
+
? payload.output
|
|
428
|
+
: truncation && typeof payload.preview === "string"
|
|
429
|
+
? payload.preview
|
|
430
|
+
: payload.output;
|
|
431
|
+
target.truncation = truncation;
|
|
432
|
+
const sourceText = timelineAnnotationToolOutputText(
|
|
433
|
+
Object.prototype.hasOwnProperty.call(payload, "output")
|
|
434
|
+
? payload.output
|
|
435
|
+
: payload.preview,
|
|
436
|
+
);
|
|
437
|
+
if (sourceText !== null) {
|
|
438
|
+
target.annotationSource = {
|
|
439
|
+
kind: "tool_output",
|
|
440
|
+
eventId: event.id,
|
|
441
|
+
eventType: "agent.toolCall.output",
|
|
442
|
+
sequence: event.sequence,
|
|
443
|
+
turnId,
|
|
444
|
+
text: sourceText,
|
|
445
|
+
label: target.name,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
341
448
|
break;
|
|
342
449
|
}
|
|
343
450
|
|
|
@@ -540,6 +647,19 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
540
647
|
|
|
541
648
|
case "tool.auth_needed":
|
|
542
649
|
case "credential.auth_needed": {
|
|
650
|
+
const capability = capabilityAuthorizationRequest(payload.capability);
|
|
651
|
+
if (
|
|
652
|
+
event.type === "tool.auth_needed" &&
|
|
653
|
+
!stringValue(payload.toolName) &&
|
|
654
|
+
stringValue(payload.serverId) !== "codex_apps"
|
|
655
|
+
) {
|
|
656
|
+
// Historical optional-MCP initialize/tools-list credential misses are
|
|
657
|
+
// setup availability, not evidence of a concrete conversational tool
|
|
658
|
+
// call. Keep the raw event in Debug/audit but do not manufacture an
|
|
659
|
+
// actionable reconnect card in the transcript. Actual tools/call auth
|
|
660
|
+
// failures carry a concrete toolName and remain visible.
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
543
663
|
// Keep the whole structured payload — the renderer turns it into a clean
|
|
544
664
|
// inline reconnect card, and the app starts the recovery flow off the
|
|
545
665
|
// connectionId/resource. Losing it to a plain-text notice was the ugly
|
|
@@ -549,6 +669,12 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
549
669
|
kind: "auth-needed",
|
|
550
670
|
id: event.id,
|
|
551
671
|
turnId,
|
|
672
|
+
serverId: typeof payload.serverId === "string" ? payload.serverId : null,
|
|
673
|
+
source: capability
|
|
674
|
+
? "capability"
|
|
675
|
+
: event.type === "tool.auth_needed"
|
|
676
|
+
? "tool"
|
|
677
|
+
: "credential",
|
|
552
678
|
providerDomain: stringValue(payload.providerDomain),
|
|
553
679
|
connectionId: typeof payload.connectionId === "string" ? payload.connectionId : null,
|
|
554
680
|
reason: authNeededReason(payload.reason),
|
|
@@ -557,6 +683,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
557
683
|
toolName: typeof payload.toolName === "string" ? payload.toolName : null,
|
|
558
684
|
authorizationUrl:
|
|
559
685
|
typeof payload.authorizationUrl === "string" ? payload.authorizationUrl : null,
|
|
686
|
+
capability,
|
|
560
687
|
occurredAt: event.occurredAt,
|
|
561
688
|
});
|
|
562
689
|
break;
|
|
@@ -1301,6 +1428,7 @@ function machineInputMembers(value: unknown): MachineInputBatchItem["members"] {
|
|
|
1301
1428
|
"agent_message",
|
|
1302
1429
|
"agent_steer_instruction",
|
|
1303
1430
|
"child_terminal_result",
|
|
1431
|
+
"media_generation_result",
|
|
1304
1432
|
]);
|
|
1305
1433
|
const classifications = new Set<MachineInputBatchItem["members"][number]["classification"]>([
|
|
1306
1434
|
"success",
|
|
@@ -1319,16 +1447,24 @@ function machineInputMembers(value: unknown): MachineInputBatchItem["members"] {
|
|
|
1319
1447
|
member.classification as MachineInputBatchItem["members"][number]["classification"],
|
|
1320
1448
|
) &&
|
|
1321
1449
|
typeof member.sourceId === "string"
|
|
1322
|
-
?
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
kind
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1450
|
+
? (() => {
|
|
1451
|
+
const kind = member.kind as MachineInputBatchItem["members"][number]["kind"];
|
|
1452
|
+
const result =
|
|
1453
|
+
kind === "media_generation_result"
|
|
1454
|
+
? parseMediaGenerationResult(member.result)
|
|
1455
|
+
: null;
|
|
1456
|
+
return [
|
|
1457
|
+
{
|
|
1458
|
+
id: member.id,
|
|
1459
|
+
kind,
|
|
1460
|
+
classification:
|
|
1461
|
+
member.classification as MachineInputBatchItem["members"][number]["classification"],
|
|
1462
|
+
sourceId: member.sourceId,
|
|
1463
|
+
summary: stringValue(member.summary),
|
|
1464
|
+
...(result ? { result } : {}),
|
|
1465
|
+
},
|
|
1466
|
+
];
|
|
1467
|
+
})()
|
|
1332
1468
|
: [];
|
|
1333
1469
|
})
|
|
1334
1470
|
: [];
|
|
@@ -1364,6 +1500,25 @@ function resourceRefs(value: unknown): import("@opengeni/sdk").ResourceRef[] {
|
|
|
1364
1500
|
});
|
|
1365
1501
|
}
|
|
1366
1502
|
|
|
1503
|
+
function timelineAnnotations(value: unknown): TimelineAnnotation[] {
|
|
1504
|
+
if (!Array.isArray(value)) return [];
|
|
1505
|
+
return value.filter((candidate): candidate is TimelineAnnotation => {
|
|
1506
|
+
const annotation = asRecord(candidate);
|
|
1507
|
+
const source = asRecord(annotation.source);
|
|
1508
|
+
return (
|
|
1509
|
+
typeof annotation.id === "string" &&
|
|
1510
|
+
typeof annotation.ordinal === "number" &&
|
|
1511
|
+
typeof annotation.quote === "string" &&
|
|
1512
|
+
typeof annotation.note === "string" &&
|
|
1513
|
+
typeof source.eventId === "string" &&
|
|
1514
|
+
typeof source.eventType === "string" &&
|
|
1515
|
+
typeof source.sequence === "number" &&
|
|
1516
|
+
typeof source.startOffset === "number" &&
|
|
1517
|
+
typeof source.endOffset === "number"
|
|
1518
|
+
);
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1367
1522
|
function toolRefs(value: unknown): import("@opengeni/sdk").ToolRef[] {
|
|
1368
1523
|
if (!Array.isArray(value)) {
|
|
1369
1524
|
return [];
|
|
@@ -1426,6 +1581,31 @@ function isErrorOutput(payload: Record<string, unknown>): boolean {
|
|
|
1426
1581
|
);
|
|
1427
1582
|
}
|
|
1428
1583
|
|
|
1584
|
+
function toolCallTruncation(payload: Record<string, unknown>): ToolCallTruncation | null {
|
|
1585
|
+
const truncation = asRecord(payload.truncation);
|
|
1586
|
+
const fullEvidence = asRecord(truncation.fullEvidence);
|
|
1587
|
+
const surface = stringValue(truncation.surface);
|
|
1588
|
+
const reason = stringValue(truncation.reason);
|
|
1589
|
+
if (
|
|
1590
|
+
truncation.truncated !== true ||
|
|
1591
|
+
!surface ||
|
|
1592
|
+
!reason ||
|
|
1593
|
+
typeof fullEvidence.available !== "boolean"
|
|
1594
|
+
) {
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
truncated: true,
|
|
1599
|
+
surface,
|
|
1600
|
+
reason,
|
|
1601
|
+
omittedBytes: numberOrNull(truncation.omittedBytes),
|
|
1602
|
+
fullEvidence: {
|
|
1603
|
+
available: fullEvidence.available,
|
|
1604
|
+
reason: stringValue(fullEvidence.reason) || null,
|
|
1605
|
+
},
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1429
1609
|
function findOpenCall(
|
|
1430
1610
|
items: TimelineItem[],
|
|
1431
1611
|
callId: string | null,
|
|
@@ -1549,6 +1729,37 @@ function authNeededReason(value: unknown): AuthNeededItem["reason"] {
|
|
|
1549
1729
|
: null;
|
|
1550
1730
|
}
|
|
1551
1731
|
|
|
1732
|
+
function capabilityAuthorizationRequest(
|
|
1733
|
+
value: unknown,
|
|
1734
|
+
): NonNullable<AuthNeededItem["capability"]> | null {
|
|
1735
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1736
|
+
const record = value as Record<string, unknown>;
|
|
1737
|
+
const kind = record.kind;
|
|
1738
|
+
const source = record.source;
|
|
1739
|
+
const action = record.action;
|
|
1740
|
+
if (
|
|
1741
|
+
typeof record.id !== "string" ||
|
|
1742
|
+
typeof record.name !== "string" ||
|
|
1743
|
+
!["pack", "mcp", "api", "skill", "plugin"].includes(String(kind)) ||
|
|
1744
|
+
!["built_in", "library", "configured", "public_registry", "registry", "manual"].includes(
|
|
1745
|
+
String(source),
|
|
1746
|
+
) ||
|
|
1747
|
+
!["connect", "add_credentials", "enable"].includes(String(action)) ||
|
|
1748
|
+
typeof record.rationale !== "string"
|
|
1749
|
+
) {
|
|
1750
|
+
return null;
|
|
1751
|
+
}
|
|
1752
|
+
return {
|
|
1753
|
+
id: record.id,
|
|
1754
|
+
name: record.name,
|
|
1755
|
+
kind: kind as NonNullable<AuthNeededItem["capability"]>["kind"],
|
|
1756
|
+
source: source as NonNullable<AuthNeededItem["capability"]>["source"],
|
|
1757
|
+
action: action as NonNullable<AuthNeededItem["capability"]>["action"],
|
|
1758
|
+
rationale: record.rationale,
|
|
1759
|
+
requiredVariables: stringList(record.requiredVariables),
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1552
1763
|
function stringList(value: unknown): string[] {
|
|
1553
1764
|
return Array.isArray(value)
|
|
1554
1765
|
? value.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0)
|
|
@@ -1609,6 +1820,15 @@ export function extractSessionRef(value: unknown, depth = 0): string | null {
|
|
|
1609
1820
|
return null;
|
|
1610
1821
|
}
|
|
1611
1822
|
const record = value as Record<string, unknown>;
|
|
1823
|
+
const receiptResource = asRecord(record.resource);
|
|
1824
|
+
if (
|
|
1825
|
+
record.receiptVersion === "mcp-mutation-receipt.v1" &&
|
|
1826
|
+
receiptResource.type === "session" &&
|
|
1827
|
+
typeof receiptResource.id === "string" &&
|
|
1828
|
+
looksLikeId(receiptResource.id)
|
|
1829
|
+
) {
|
|
1830
|
+
return receiptResource.id;
|
|
1831
|
+
}
|
|
1612
1832
|
if (typeof record.sessionId === "string" && looksLikeId(record.sessionId)) {
|
|
1613
1833
|
return record.sessionId;
|
|
1614
1834
|
}
|
package/src/timeline/registry.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RetainedArtifactReference, VideoArtifactPlaybackSource } from "@opengeni/sdk";
|
|
1
2
|
import type { ComponentType } from "react";
|
|
2
3
|
import { mcpToolLeaf } from "./tool-display-name";
|
|
3
4
|
import type { ToolCallItem } from "./types";
|
|
@@ -22,8 +23,26 @@ import type { ToolCallItem } from "./types";
|
|
|
22
23
|
|
|
23
24
|
export type ToolRendererProps = {
|
|
24
25
|
item: ToolCallItem;
|
|
26
|
+
loadRetainedScreenshot?: RetainedScreenshotLoader | undefined;
|
|
27
|
+
loadRetainedArtifact?: RetainedArtifactLoader | undefined;
|
|
25
28
|
};
|
|
26
29
|
|
|
30
|
+
export type RetainedScreenshotLoader = (
|
|
31
|
+
artifact: RetainedArtifactReference,
|
|
32
|
+
signal: AbortSignal,
|
|
33
|
+
) => Promise<Uint8Array | null>;
|
|
34
|
+
|
|
35
|
+
export type RetainedArtifactLoader = (
|
|
36
|
+
artifact: RetainedArtifactReference,
|
|
37
|
+
signal: AbortSignal,
|
|
38
|
+
) => Promise<Uint8Array | { url: string } | null>;
|
|
39
|
+
|
|
40
|
+
/** Mint an expiring source for native browser playback without loading video bytes in JS. */
|
|
41
|
+
export type VideoArtifactPlaybackLoader = (
|
|
42
|
+
artifactId: string,
|
|
43
|
+
signal: AbortSignal,
|
|
44
|
+
) => Promise<VideoArtifactPlaybackSource>;
|
|
45
|
+
|
|
27
46
|
export type ToolRenderer = ComponentType<ToolRendererProps>;
|
|
28
47
|
|
|
29
48
|
/** A registry entry: which key it matches and the component that renders it. */
|
|
@@ -3,6 +3,7 @@ import { AnimatePresence, motion } from "motion/react";
|
|
|
3
3
|
import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from "react";
|
|
4
4
|
import { Dialog } from "radix-ui";
|
|
5
5
|
import { cn } from "../lib/cn";
|
|
6
|
+
import { usePortalTokenStyle } from "../lib/use-portal-token-style";
|
|
6
7
|
|
|
7
8
|
/* ----------------------------------------------------------------------------
|
|
8
9
|
Screenshot lightbox
|
|
@@ -18,7 +19,7 @@ import { cn } from "../lib/cn";
|
|
|
18
19
|
-------------------------------------------------------------------------- */
|
|
19
20
|
|
|
20
21
|
type LightboxController = {
|
|
21
|
-
open: (src: string, caption?: string) => void;
|
|
22
|
+
open: (src: string, caption?: string, source?: HTMLElement | null, label?: string) => void;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const LightboxContext = createContext<LightboxController | null>(null);
|
|
@@ -58,10 +59,25 @@ export function LightboxProvider({ children }: { children: ReactNode }) {
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
function LightboxRoot({ children }: { children: ReactNode }) {
|
|
61
|
-
const [state, setState] = useState<{
|
|
62
|
+
const [state, setState] = useState<{
|
|
63
|
+
src: string;
|
|
64
|
+
caption?: string;
|
|
65
|
+
label: string;
|
|
66
|
+
} | null>(null);
|
|
67
|
+
const [tokenSource, setTokenSource] = useState<HTMLElement | null>(null);
|
|
68
|
+
const portalStyle = usePortalTokenStyle(tokenSource);
|
|
62
69
|
|
|
63
|
-
const open = useCallback(
|
|
64
|
-
|
|
70
|
+
const open = useCallback(
|
|
71
|
+
(src: string, caption?: string, source?: HTMLElement | null, label = "Screenshot") => {
|
|
72
|
+
setTokenSource(source ?? null);
|
|
73
|
+
setState(caption ? { src, caption, label } : { src, label });
|
|
74
|
+
},
|
|
75
|
+
[],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const close = useCallback(() => {
|
|
79
|
+
setState(null);
|
|
80
|
+
setTokenSource(null);
|
|
65
81
|
}, []);
|
|
66
82
|
|
|
67
83
|
const controller = useMemo<LightboxController>(() => ({ open }), [open]);
|
|
@@ -69,7 +85,7 @@ function LightboxRoot({ children }: { children: ReactNode }) {
|
|
|
69
85
|
return (
|
|
70
86
|
<LightboxContext.Provider value={controller}>
|
|
71
87
|
{children}
|
|
72
|
-
<Dialog.Root open={state !== null} onOpenChange={(next) => !next &&
|
|
88
|
+
<Dialog.Root open={state !== null} onOpenChange={(next) => !next && close()}>
|
|
73
89
|
<AnimatePresence>
|
|
74
90
|
{state !== null ? (
|
|
75
91
|
<Dialog.Portal forceMount>
|
|
@@ -80,12 +96,13 @@ function LightboxRoot({ children }: { children: ReactNode }) {
|
|
|
80
96
|
exit={{ opacity: 0 }}
|
|
81
97
|
transition={{ duration: 0.15 }}
|
|
82
98
|
className="og-root fixed inset-0 z-50 bg-black/90 backdrop-blur-md"
|
|
99
|
+
style={portalStyle}
|
|
83
100
|
/>
|
|
84
101
|
</Dialog.Overlay>
|
|
85
102
|
<Dialog.Content
|
|
86
103
|
asChild
|
|
87
104
|
forceMount
|
|
88
|
-
aria-label=
|
|
105
|
+
aria-label={state.label}
|
|
89
106
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
|
90
107
|
>
|
|
91
108
|
<motion.div
|
|
@@ -101,12 +118,16 @@ function LightboxRoot({ children }: { children: ReactNode }) {
|
|
|
101
118
|
// figcaption's "click outside to close" affordance is real.
|
|
102
119
|
onClick={(event) => {
|
|
103
120
|
if (event.target === event.currentTarget) {
|
|
104
|
-
|
|
121
|
+
close();
|
|
105
122
|
}
|
|
106
123
|
}}
|
|
107
124
|
className="og-root fixed inset-0 z-50 flex items-center justify-center p-6 sm:p-12"
|
|
125
|
+
style={portalStyle}
|
|
108
126
|
>
|
|
109
|
-
<Dialog.Title className="sr-only">
|
|
127
|
+
<Dialog.Title className="sr-only">{state.label}</Dialog.Title>
|
|
128
|
+
<Dialog.Description className="sr-only">
|
|
129
|
+
{state.caption ?? `Expanded ${state.label.toLowerCase()}`}
|
|
130
|
+
</Dialog.Description>
|
|
110
131
|
{/* The figure is one self-contained object: image, caption, and
|
|
111
132
|
its own close control. The close button anchors to a wrapper
|
|
112
133
|
sized to the IMAGE (w-fit), so it hugs the real top-right
|
|
@@ -117,7 +138,7 @@ function LightboxRoot({ children }: { children: ReactNode }) {
|
|
|
117
138
|
{/* A plain <img>: this SDK is framework-agnostic, with no host Image component. */}
|
|
118
139
|
<img
|
|
119
140
|
src={state.src}
|
|
120
|
-
alt={state.caption ??
|
|
141
|
+
alt={state.caption ?? state.label}
|
|
121
142
|
className="min-h-0 max-h-[82vh] w-auto max-w-full rounded-og-md border border-white/10 object-contain shadow-og-lg"
|
|
122
143
|
/>
|
|
123
144
|
<Dialog.Close
|