@opengeni/react 5.2.0-canary.1 → 6.0.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +3 -1
- package/dist/artifacts.js.map +1 -1
- package/dist/{chunk-O4NQNMPG.js → chunk-CC7AVRIJ.js} +89 -32
- package/dist/chunk-CC7AVRIJ.js.map +1 -0
- package/dist/{chunk-2TKI3WEG.js → chunk-WYWRYUNQ.js} +12 -1
- package/dist/chunk-WYWRYUNQ.js.map +1 -0
- package/dist/connect.js +11 -19
- package/dist/connect.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/session-ui.js +2 -2
- package/dist/timeline/retained-image.d.ts +2 -0
- package/package.json +3 -3
- package/src/artifacts.ts +1 -1
- package/src/components/message-timeline.tsx +42 -12
- package/src/connection-catalog.tsx +8 -21
- package/src/timeline/retained-image.ts +12 -0
- package/src/timeline/tool-renderers.tsx +67 -16
- package/dist/chunk-2TKI3WEG.js.map +0 -1
- package/dist/chunk-O4NQNMPG.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0-canary.0",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -133,8 +133,8 @@
|
|
|
133
133
|
"@dnd-kit/utilities": "3.2.2",
|
|
134
134
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
135
135
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
136
|
-
"@opengeni/connect": "^0.2.0-canary.
|
|
137
|
-
"@opengeni/sdk": "^
|
|
136
|
+
"@opengeni/connect": "^0.2.0-canary.9",
|
|
137
|
+
"@opengeni/sdk": "^6.0.0-canary.0",
|
|
138
138
|
"clsx": "^2.1.1",
|
|
139
139
|
"dequal": "2.0.3",
|
|
140
140
|
"lucide-react": "^1.8.0",
|
package/src/artifacts.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { inlineHtmlDocument } from "./components/artifacts/inline-html-document";
|
|
2
|
-
export { useRetainedImageObjectUrl } from "./timeline/retained-image";
|
|
2
|
+
export { isRetainedImageContentType, useRetainedImageObjectUrl } from "./timeline/retained-image";
|
|
3
3
|
/** Public artifact surfaces. Import from `@opengeni/react/artifacts`. */
|
|
4
4
|
export {
|
|
5
5
|
ArtifactSurface,
|
|
@@ -2,6 +2,10 @@ import { RollingActivity } from "../timeline/rolling-activity";
|
|
|
2
2
|
import { GenieLoadingOptionsContext, type GenieLoadingOptions } from "../timeline/genie-loading";
|
|
3
3
|
import { ChildSessionLink } from "./child-session-link";
|
|
4
4
|
import { useStartupDetails } from "../timeline/startup-preference";
|
|
5
|
+
import { parseSandboxFileArtifactReceipt } from "@opengeni/sdk";
|
|
6
|
+
import { unwrapMcpOutput } from "../timeline/parsers";
|
|
7
|
+
import { isRetainedImageContentType } from "../timeline/retained-image";
|
|
8
|
+
import { mcpToolLeaf } from "../timeline/tool-display-name";
|
|
5
9
|
import type {
|
|
6
10
|
DraftTimelineAnnotation,
|
|
7
11
|
MediaGenerationResult,
|
|
@@ -2537,9 +2541,15 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2537
2541
|
// Settled (or live-fold) activity clusters get a chip. Inside an expanded
|
|
2538
2542
|
// turn that is the second layer — quiet nested chips under the outer turn
|
|
2539
2543
|
// summary when contiguous activity naturally clusters (≥2 only).
|
|
2544
|
+
const containsPresentedImage = timelineGroupContainsPresentedImage(group);
|
|
2545
|
+
const hasRememberedImageFold =
|
|
2546
|
+
group.kind === "activity" && containsPresentedImage && foldMemory?.has(group.id);
|
|
2547
|
+
// Primary images stay visible through live narration and the turn wrap.
|
|
2548
|
+
// Manual collapse still belongs to TurnSummary's existing fold memory.
|
|
2540
2549
|
const activityShouldFold =
|
|
2541
|
-
group.kind === "activity" &&
|
|
2542
|
-
|
|
2550
|
+
group.kind === "activity" &&
|
|
2551
|
+
!containsPresentedImage &&
|
|
2552
|
+
!!(group.outcome || (foldLiveCluster && clusterIsSettled(group)));
|
|
2543
2553
|
// Latch live→folded so a top-level shell that was already mounted open can
|
|
2544
2554
|
// start the settle beat without remounting bare rail → wrapper.
|
|
2545
2555
|
const liveActivitySettle = useLiveSettleFold(activityShouldFold && !insideTurn);
|
|
@@ -2548,7 +2558,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2548
2558
|
group.kind === "turn" &&
|
|
2549
2559
|
(group.outcome === "failed" ||
|
|
2550
2560
|
timelineGroupContainsAuthNeeded(group) ||
|
|
2551
|
-
|
|
2561
|
+
containsPresentedImage);
|
|
2552
2562
|
// activity-* → turn-* remount: carry resting state so settleFold does not
|
|
2553
2563
|
// re-open a chip the reader already watched collapse.
|
|
2554
2564
|
if (group.kind === "turn" && foldMemory && !insideTurn) {
|
|
@@ -2589,6 +2599,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2589
2599
|
if (
|
|
2590
2600
|
turnSummary?.rolling &&
|
|
2591
2601
|
!startupDetails &&
|
|
2602
|
+
!hasRememberedImageFold &&
|
|
2592
2603
|
group.items.filter((item) => item.kind !== "startup-phase").length === 1
|
|
2593
2604
|
) {
|
|
2594
2605
|
return (
|
|
@@ -2620,8 +2631,12 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2620
2631
|
!startupDetails &&
|
|
2621
2632
|
visibleActivity.length === 1 &&
|
|
2622
2633
|
visibleActivity[0]?.kind === "reasoning";
|
|
2634
|
+
// Untouched images stay primary output, but a reader-owned image fold
|
|
2635
|
+
// must keep its shell across a multi-cluster wrap. A bare rail would
|
|
2636
|
+
// bypass the remembered choice; retaining either state also lets the
|
|
2637
|
+
// reader reopen and close the same chip after settlement.
|
|
2623
2638
|
const useNestedChip =
|
|
2624
|
-
nestClusterChips && activityShouldFold
|
|
2639
|
+
nestClusterChips && (activityShouldFold || hasRememberedImageFold) && !singleThought;
|
|
2625
2640
|
if (!useNestedChip) {
|
|
2626
2641
|
return (
|
|
2627
2642
|
<ActivityRail
|
|
@@ -2663,16 +2678,24 @@ const TimelineGroupView = memo(function TimelineGroupView({
|
|
|
2663
2678
|
// flips settleFold (collapse) instead of remounting bare rail → wrapper.
|
|
2664
2679
|
return (
|
|
2665
2680
|
<TurnSummary
|
|
2681
|
+
// Apply the primary-output default when a rolling activity first
|
|
2682
|
+
// produces an image; the stable foldKey still preserves user choices.
|
|
2683
|
+
key={containsPresentedImage ? "primary-image" : "activity"}
|
|
2666
2684
|
items={group.items}
|
|
2667
2685
|
outcome={group.outcome}
|
|
2668
2686
|
failureText={group.failureText}
|
|
2669
2687
|
defaultOpen={
|
|
2670
|
-
group.outcome === "failed" ||
|
|
2688
|
+
group.outcome === "failed" ||
|
|
2689
|
+
containsPresentedImage ||
|
|
2690
|
+
(!turnSummary?.rolling && !activityShouldFold)
|
|
2671
2691
|
? true
|
|
2672
2692
|
: undefined
|
|
2673
2693
|
}
|
|
2674
2694
|
liveHeader={
|
|
2675
|
-
turnSummary?.rolling &&
|
|
2695
|
+
turnSummary?.rolling &&
|
|
2696
|
+
!containsPresentedImage &&
|
|
2697
|
+
!group.outcome &&
|
|
2698
|
+
!foldLiveCluster ? (
|
|
2676
2699
|
<RollingActivity
|
|
2677
2700
|
items={group.items}
|
|
2678
2701
|
toolRegistry={toolRegistry}
|
|
@@ -2862,17 +2885,24 @@ function timelineGroupContainsAuthNeeded(group: TimelineGroup): boolean {
|
|
|
2862
2885
|
}
|
|
2863
2886
|
}
|
|
2864
2887
|
|
|
2865
|
-
/**
|
|
2866
|
-
function
|
|
2888
|
+
/** Deliberately published images are primary output, not incidental screenshots. */
|
|
2889
|
+
function timelineGroupContainsPresentedImage(group: TimelineGroup): boolean {
|
|
2867
2890
|
switch (group.kind) {
|
|
2868
2891
|
case "item":
|
|
2869
2892
|
return false;
|
|
2870
2893
|
case "activity":
|
|
2871
|
-
return group.items.some(
|
|
2872
|
-
(item
|
|
2873
|
-
|
|
2894
|
+
return group.items.some((item) => {
|
|
2895
|
+
if (item.kind !== "tool-call") return false;
|
|
2896
|
+
const name = mcpToolLeaf(item.name);
|
|
2897
|
+
if (name === "generate_image" || name === "image_generation_call") return true;
|
|
2898
|
+
if (item.status !== "complete" || name !== "sandbox_file_publish") return false;
|
|
2899
|
+
const output = unwrapMcpOutput(item.output);
|
|
2900
|
+
if (output.isError) return false;
|
|
2901
|
+
const receipt = parseSandboxFileArtifactReceipt(output.text);
|
|
2902
|
+
return receipt !== null && isRetainedImageContentType(receipt.artifact.contentType);
|
|
2903
|
+
});
|
|
2874
2904
|
case "turn":
|
|
2875
|
-
return group.groups.some(
|
|
2905
|
+
return group.groups.some(timelineGroupContainsPresentedImage);
|
|
2876
2906
|
}
|
|
2877
2907
|
}
|
|
2878
2908
|
|
|
@@ -26,22 +26,11 @@ export type ConnectionCatalogProps = {
|
|
|
26
26
|
emptyMessage?: string;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
function
|
|
30
|
-
option,
|
|
31
|
-
name = option.name,
|
|
32
|
-
}: {
|
|
33
|
-
option: ConnectionCatalogOption;
|
|
34
|
-
name?: string;
|
|
35
|
-
}) {
|
|
29
|
+
function ConnectionDetailsIndicator() {
|
|
36
30
|
return (
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
aria-label={`View ${name} details`}
|
|
41
|
-
onClick={option.onOpen}
|
|
42
|
-
>
|
|
43
|
-
<span aria-hidden>{option.connected ? "›" : "+"}</span>
|
|
44
|
-
</button>
|
|
31
|
+
<span aria-hidden className="og-connection-group-chevron">
|
|
32
|
+
›
|
|
33
|
+
</span>
|
|
45
34
|
);
|
|
46
35
|
}
|
|
47
36
|
|
|
@@ -54,10 +43,9 @@ export function ConnectionOptionRow({ option }: { option: ConnectionCatalogOptio
|
|
|
54
43
|
<span>{option.description}</span>
|
|
55
44
|
</span>
|
|
56
45
|
<span className="og-connection-catalog-status">{option.status}</span>
|
|
46
|
+
{!option.action ? <ConnectionDetailsIndicator /> : null}
|
|
57
47
|
</button>
|
|
58
|
-
<span className="og-connection-option-action">
|
|
59
|
-
{option.action ?? <ConnectionDetailsAction option={option} />}
|
|
60
|
-
</span>
|
|
48
|
+
{option.action ? <span className="og-connection-option-action">{option.action}</span> : null}
|
|
61
49
|
</div>
|
|
62
50
|
);
|
|
63
51
|
}
|
|
@@ -91,10 +79,9 @@ export function ConnectionServiceRow({ service }: { service: ConnectionCatalogSe
|
|
|
91
79
|
onClick={service.options[0]!.onOpen}
|
|
92
80
|
>
|
|
93
81
|
{identity}
|
|
82
|
+
{!service.options[0]?.action ? <ConnectionDetailsIndicator /> : null}
|
|
94
83
|
</button>
|
|
95
|
-
{service.options[0]?.action
|
|
96
|
-
<ConnectionDetailsAction option={service.options[0]!} name={service.name} />
|
|
97
|
-
)}
|
|
84
|
+
{service.options[0]?.action}
|
|
98
85
|
</div>
|
|
99
86
|
) : (
|
|
100
87
|
<details className="og-connection-catalog-service">
|
|
@@ -7,6 +7,18 @@ type RetainedImageState =
|
|
|
7
7
|
| { kind: "unavailable"; label: string }
|
|
8
8
|
| { kind: "error"; message: string };
|
|
9
9
|
|
|
10
|
+
/** Shared browser preview allowlist; other published files remain downloads. */
|
|
11
|
+
export function isRetainedImageContentType(contentType: string): boolean {
|
|
12
|
+
return [
|
|
13
|
+
"image/png",
|
|
14
|
+
"image/jpeg",
|
|
15
|
+
"image/gif",
|
|
16
|
+
"image/webp",
|
|
17
|
+
"image/avif",
|
|
18
|
+
"image/svg+xml",
|
|
19
|
+
].includes(contentType);
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
export function useRetainedImageObjectUrl(
|
|
11
23
|
artifact: RetainedArtifactReference,
|
|
12
24
|
load: ToolRendererProps["loadRetainedArtifact"],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { KnowledgeReceiptRow } from "./knowledge-receipt";
|
|
2
|
-
import { useRetainedImageObjectUrl } from "./retained-image";
|
|
2
|
+
import { isRetainedImageContentType, useRetainedImageObjectUrl } from "./retained-image";
|
|
3
3
|
import {
|
|
4
4
|
parseSandboxFileArtifactReceipt,
|
|
5
5
|
type GitFileDiff,
|
|
@@ -783,9 +783,12 @@ function RetainedSessionImageDisclosure({
|
|
|
783
783
|
batched,
|
|
784
784
|
failed,
|
|
785
785
|
cancelled,
|
|
786
|
+
filename,
|
|
787
|
+
defaultOpen,
|
|
788
|
+
children,
|
|
786
789
|
}: {
|
|
787
790
|
artifact: RetainedArtifactReference;
|
|
788
|
-
load: ToolRendererProps["
|
|
791
|
+
load: ToolRendererProps["loadRetainedArtifact"];
|
|
789
792
|
title: string;
|
|
790
793
|
caption: string;
|
|
791
794
|
noun: "image" | "screenshot";
|
|
@@ -794,15 +797,19 @@ function RetainedSessionImageDisclosure({
|
|
|
794
797
|
batched: string | null;
|
|
795
798
|
failed: boolean;
|
|
796
799
|
cancelled: boolean;
|
|
800
|
+
filename?: string;
|
|
801
|
+
defaultOpen?: boolean;
|
|
802
|
+
children?: ReactNode;
|
|
797
803
|
}) {
|
|
798
804
|
const state = useRetainedImageObjectUrl(artifact, load);
|
|
799
|
-
const downloadFilename = retainedImageFilename(artifact);
|
|
805
|
+
const downloadFilename = filename ?? retainedImageFilename(artifact);
|
|
800
806
|
|
|
801
807
|
return (
|
|
802
808
|
<ActivityDisclosure
|
|
803
809
|
icon={icon}
|
|
804
810
|
iconTone={failed ? "failed" : state.kind === "ready" ? "accent" : "muted"}
|
|
805
811
|
title={title}
|
|
812
|
+
defaultOpen={defaultOpen}
|
|
806
813
|
failed={failed}
|
|
807
814
|
cancelled={cancelled}
|
|
808
815
|
preview={
|
|
@@ -852,6 +859,7 @@ function RetainedSessionImageDisclosure({
|
|
|
852
859
|
</BodyNote>
|
|
853
860
|
)}
|
|
854
861
|
{batched ? <BodyNote>batched: {batched}</BodyNote> : null}
|
|
862
|
+
{children}
|
|
855
863
|
</ActivityDisclosure>
|
|
856
864
|
);
|
|
857
865
|
}
|
|
@@ -945,6 +953,22 @@ function SandboxFilePublishRenderer({ item, loadRetainedArtifact }: ToolRenderer
|
|
|
945
953
|
return <GenericRenderer item={item} />;
|
|
946
954
|
}
|
|
947
955
|
|
|
956
|
+
// The closed SDK receipt has already checked the workspace-qualified route.
|
|
957
|
+
const workspaceId = /^\/v1\/workspaces\/([0-9a-f-]+)\/artifacts\//.exec(
|
|
958
|
+
receipt.artifact.retrieval.path,
|
|
959
|
+
)?.[1];
|
|
960
|
+
const openLink = workspaceId ? (
|
|
961
|
+
<a
|
|
962
|
+
href={`/workspaces/${workspaceId}/artifacts/files/${receipt.artifact.artifactId}`}
|
|
963
|
+
aria-label={`Open ${receipt.filename} in Artifacts`}
|
|
964
|
+
className="inline-flex min-h-7 items-center rounded-og-sm px-2 text-og-sm font-medium text-og-accent-strong hover:bg-og-surface-2 hover:underline focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-10"
|
|
965
|
+
onClick={(event) => event.stopPropagation()}
|
|
966
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
967
|
+
>
|
|
968
|
+
Open in Artifacts
|
|
969
|
+
</a>
|
|
970
|
+
) : null;
|
|
971
|
+
|
|
948
972
|
const download = async () => {
|
|
949
973
|
if (!loadRetainedArtifact) {
|
|
950
974
|
setDownloadState("error");
|
|
@@ -977,6 +1001,44 @@ function SandboxFilePublishRenderer({ item, loadRetainedArtifact }: ToolRenderer
|
|
|
977
1001
|
}
|
|
978
1002
|
};
|
|
979
1003
|
|
|
1004
|
+
const downloadButton = (
|
|
1005
|
+
<button
|
|
1006
|
+
type="button"
|
|
1007
|
+
onClick={() => void download()}
|
|
1008
|
+
disabled={downloadState === "loading"}
|
|
1009
|
+
className="inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2.5 py-1.5 text-og-sm font-medium text-og-fg transition-colors hover:border-og-border-strong hover:bg-og-surface-2 disabled:cursor-wait disabled:opacity-60"
|
|
1010
|
+
>
|
|
1011
|
+
<DownloadIcon className="size-3.5" />
|
|
1012
|
+
{downloadState === "loading"
|
|
1013
|
+
? "Preparing…"
|
|
1014
|
+
: downloadState === "error"
|
|
1015
|
+
? "Retry download"
|
|
1016
|
+
: "Download"}
|
|
1017
|
+
</button>
|
|
1018
|
+
);
|
|
1019
|
+
|
|
1020
|
+
if (isRetainedImageContentType(receipt.artifact.contentType)) {
|
|
1021
|
+
return (
|
|
1022
|
+
<RetainedSessionImageDisclosure
|
|
1023
|
+
artifact={receipt.artifact}
|
|
1024
|
+
load={loadRetainedArtifact}
|
|
1025
|
+
title={`Published ${receipt.filename}`}
|
|
1026
|
+
caption={receipt.filename}
|
|
1027
|
+
noun="image"
|
|
1028
|
+
icon={<ImageIcon className={ICON_SIZE} />}
|
|
1029
|
+
lightboxLabel="Image"
|
|
1030
|
+
batched={null}
|
|
1031
|
+
failed={false}
|
|
1032
|
+
cancelled={false}
|
|
1033
|
+
filename={receipt.filename}
|
|
1034
|
+
defaultOpen
|
|
1035
|
+
>
|
|
1036
|
+
{downloadButton}
|
|
1037
|
+
{openLink}
|
|
1038
|
+
</RetainedSessionImageDisclosure>
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
980
1042
|
return (
|
|
981
1043
|
<ActivityDisclosure
|
|
982
1044
|
icon={<DownloadIcon className={ICON_SIZE} />}
|
|
@@ -985,19 +1047,8 @@ function SandboxFilePublishRenderer({ item, loadRetainedArtifact }: ToolRenderer
|
|
|
985
1047
|
defaultOpen
|
|
986
1048
|
preview={formatBytes(receipt.artifact.originalBytes)}
|
|
987
1049
|
>
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
onClick={() => void download()}
|
|
991
|
-
disabled={downloadState === "loading"}
|
|
992
|
-
className="inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2.5 py-1.5 text-og-sm font-medium text-og-fg transition-colors hover:border-og-border-strong hover:bg-og-surface-2 disabled:cursor-wait disabled:opacity-60"
|
|
993
|
-
>
|
|
994
|
-
<DownloadIcon className="size-3.5" />
|
|
995
|
-
{downloadState === "loading"
|
|
996
|
-
? "Preparing…"
|
|
997
|
-
: downloadState === "error"
|
|
998
|
-
? "Retry download"
|
|
999
|
-
: "Download"}
|
|
1000
|
-
</button>
|
|
1050
|
+
{downloadButton}
|
|
1051
|
+
{openLink}
|
|
1001
1052
|
</ActivityDisclosure>
|
|
1002
1053
|
);
|
|
1003
1054
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/timeline/retained-image.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { RetainedArtifactReference } from \"@opengeni/sdk\";\nimport type { ToolRendererProps } from \"./registry\";\ntype RetainedImageState =\n | { kind: \"loading\" }\n | { kind: \"ready\"; url: string }\n | { kind: \"unavailable\"; label: string }\n | { kind: \"error\"; message: string };\n\nexport function useRetainedImageObjectUrl(\n artifact: RetainedArtifactReference,\n load: ToolRendererProps[\"loadRetainedArtifact\"],\n): RetainedImageState {\n const [state, setState] = useState<RetainedImageState>({ kind: \"loading\" });\n // Function outputs are commonly serialized JSON. Parsing them creates a new\n // object on every render, so depend on the immutable wire value rather than\n // object identity; otherwise the loader can refetch after its own setState.\n const artifactValue = retainedArtifactValue(artifact);\n const stableArtifactRef = useRef({ value: artifactValue, artifact });\n if (stableArtifactRef.current.value !== artifactValue) {\n stableArtifactRef.current = { value: artifactValue, artifact };\n }\n const stableArtifact = stableArtifactRef.current.artifact;\n useEffect(() => {\n if (!load) {\n setState({ kind: \"unavailable\", label: \"retrieval is not configured\" });\n return;\n }\n const controller = new AbortController();\n let objectUrl: string | null = null;\n setState({ kind: \"loading\" });\n void load(stableArtifact, controller.signal)\n .then((source) => {\n if (controller.signal.aborted) return;\n if (!source) {\n setState({ kind: \"unavailable\", label: \"bytes are unavailable\" });\n return;\n }\n if (!(source instanceof Uint8Array)) {\n if (!source.url.trim()) {\n setState({ kind: \"unavailable\", label: \"URL is unavailable\" });\n return;\n }\n setState({ kind: \"ready\", url: source.url });\n return;\n }\n objectUrl = URL.createObjectURL(\n new Blob([source as unknown as BlobPart], { type: stableArtifact.contentType }),\n );\n setState({ kind: \"ready\", url: objectUrl });\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n const status =\n error && typeof error === \"object\" && \"status\" in error\n ? Number((error as { status?: unknown }).status)\n : null;\n setState(\n status === 404\n ? { kind: \"unavailable\", label: \"deleted\" }\n : status === 410\n ? { kind: \"unavailable\", label: \"expired or unavailable\" }\n : { kind: \"error\", message: \"retrieval failed\" },\n );\n });\n return () => {\n controller.abort();\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n };\n }, [stableArtifact, load]);\n return state;\n}\n\nfunction retainedArtifactValue(artifact: RetainedArtifactReference): string {\n return [\n artifact.artifactId,\n artifact.kind,\n artifact.contentType,\n artifact.originalBytes,\n artifact.sha256,\n artifact.retainedAt,\n artifact.dimensions?.width ?? \"\",\n artifact.dimensions?.height ?? \"\",\n artifact.retention.policy,\n artifact.retention.expiresAt ?? \"\",\n artifact.retrieval.method,\n artifact.retrieval.path,\n artifact.retrieval.acceptRanges,\n artifact.retrieval.maxRangeBytes,\n ].join(\"\\0\");\n}\n"],"mappings":";AAAA,SAAS,WAAW,QAAQ,gBAAgB;AASrC,SAAS,0BACd,UACA,MACoB;AACpB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA6B,EAAE,MAAM,UAAU,CAAC;AAI1E,QAAM,gBAAgB,sBAAsB,QAAQ;AACpD,QAAM,oBAAoB,OAAO,EAAE,OAAO,eAAe,SAAS,CAAC;AACnE,MAAI,kBAAkB,QAAQ,UAAU,eAAe;AACrD,sBAAkB,UAAU,EAAE,OAAO,eAAe,SAAS;AAAA,EAC/D;AACA,QAAM,iBAAiB,kBAAkB,QAAQ;AACjD,YAAU,MAAM;AACd,QAAI,CAAC,MAAM;AACT,eAAS,EAAE,MAAM,eAAe,OAAO,8BAA8B,CAAC;AACtE;AAAA,IACF;AACA,UAAM,aAAa,IAAI,gBAAgB;AACvC,QAAI,YAA2B;AAC/B,aAAS,EAAE,MAAM,UAAU,CAAC;AAC5B,SAAK,KAAK,gBAAgB,WAAW,MAAM,EACxC,KAAK,CAAC,WAAW;AAChB,UAAI,WAAW,OAAO,QAAS;AAC/B,UAAI,CAAC,QAAQ;AACX,iBAAS,EAAE,MAAM,eAAe,OAAO,wBAAwB,CAAC;AAChE;AAAA,MACF;AACA,UAAI,EAAE,kBAAkB,aAAa;AACnC,YAAI,CAAC,OAAO,IAAI,KAAK,GAAG;AACtB,mBAAS,EAAE,MAAM,eAAe,OAAO,qBAAqB,CAAC;AAC7D;AAAA,QACF;AACA,iBAAS,EAAE,MAAM,SAAS,KAAK,OAAO,IAAI,CAAC;AAC3C;AAAA,MACF;AACA,kBAAY,IAAI;AAAA,QACd,IAAI,KAAK,CAAC,MAA6B,GAAG,EAAE,MAAM,eAAe,YAAY,CAAC;AAAA,MAChF;AACA,eAAS,EAAE,MAAM,SAAS,KAAK,UAAU,CAAC;AAAA,IAC5C,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAI,WAAW,OAAO,QAAS;AAC/B,YAAM,SACJ,SAAS,OAAO,UAAU,YAAY,YAAY,QAC9C,OAAQ,MAA+B,MAAM,IAC7C;AACN;AAAA,QACE,WAAW,MACP,EAAE,MAAM,eAAe,OAAO,UAAU,IACxC,WAAW,MACT,EAAE,MAAM,eAAe,OAAO,yBAAyB,IACvD,EAAE,MAAM,SAAS,SAAS,mBAAmB;AAAA,MACrD;AAAA,IACF,CAAC;AACH,WAAO,MAAM;AACX,iBAAW,MAAM;AACjB,UAAI,UAAW,KAAI,gBAAgB,SAAS;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,gBAAgB,IAAI,CAAC;AACzB,SAAO;AACT;AAEA,SAAS,sBAAsB,UAA6C;AAC1E,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS,YAAY,SAAS;AAAA,IAC9B,SAAS,YAAY,UAAU;AAAA,IAC/B,SAAS,UAAU;AAAA,IACnB,SAAS,UAAU,aAAa;AAAA,IAChC,SAAS,UAAU;AAAA,IACnB,SAAS,UAAU;AAAA,IACnB,SAAS,UAAU;AAAA,IACnB,SAAS,UAAU;AAAA,EACrB,EAAE,KAAK,IAAI;AACb;","names":[]}
|