@opengeni/react 5.0.5-canary.3 → 5.2.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.
Files changed (81) hide show
  1. package/README.md +13 -1
  2. package/dist/artifacts.d.ts +2 -0
  3. package/dist/artifacts.js +1391 -2
  4. package/dist/artifacts.js.map +1 -1
  5. package/dist/chunk-2TKI3WEG.js +73 -0
  6. package/dist/chunk-2TKI3WEG.js.map +1 -0
  7. package/dist/{chunk-V3WX3TQG.js → chunk-45XEUVTK.js} +2 -2
  8. package/dist/{chunk-M2HNWZYR.js → chunk-IHQII5WO.js} +69 -18
  9. package/dist/chunk-IHQII5WO.js.map +1 -0
  10. package/dist/{chunk-HUMIRRYI.js → chunk-IYCR4343.js} +19 -19
  11. package/dist/{chunk-HUMIRRYI.js.map → chunk-IYCR4343.js.map} +1 -1
  12. package/dist/{chunk-6J56T3MG.js → chunk-NYESIADM.js} +14 -5
  13. package/dist/chunk-NYESIADM.js.map +1 -0
  14. package/dist/{chunk-7S2KWUJD.js → chunk-QXJCNVSD.js} +2283 -2215
  15. package/dist/chunk-QXJCNVSD.js.map +1 -0
  16. package/dist/{chunk-BV5A4OTE.js → chunk-T5FRXMC3.js} +21 -2
  17. package/dist/{chunk-BV5A4OTE.js.map → chunk-T5FRXMC3.js.map} +1 -1
  18. package/dist/{chunk-BRFMGY4L.js → chunk-UGMKPUEM.js} +28 -3
  19. package/dist/chunk-UGMKPUEM.js.map +1 -0
  20. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  21. package/dist/components/artifacts/published-html-artifact-frame.d.ts +2 -0
  22. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  23. package/dist/components/markdown.d.ts +13 -1
  24. package/dist/components/message-timeline.d.ts +4 -1
  25. package/dist/components/session-conversation.d.ts +3 -0
  26. package/dist/components/user-message-body.d.ts +10 -1
  27. package/dist/composer.js +2 -2
  28. package/dist/{computer-viewer-7TEZC6Y2.js → computer-viewer-AA4JH2AP.js} +2 -2
  29. package/dist/connect.js +1 -1
  30. package/dist/index.d.ts +3 -2
  31. package/dist/index.js +9 -6
  32. package/dist/index.js.map +1 -1
  33. package/dist/interaction.js +1 -1
  34. package/dist/{platform-activity-row-27LKAWZD.js → platform-activity-row-UW4UVVM4.js} +6 -3
  35. package/dist/platform-activity-row-UW4UVVM4.js.map +1 -0
  36. package/dist/session-ui.d.ts +1 -1
  37. package/dist/session-ui.js +6 -5
  38. package/dist/session-ui.js.map +1 -1
  39. package/dist/session.js +2 -2
  40. package/dist/sites-ui.d.ts +2 -1
  41. package/dist/sites.js +12 -4
  42. package/dist/sites.js.map +1 -1
  43. package/dist/timeline/activity-rail.d.ts +1 -0
  44. package/dist/timeline/platform-activity-row.d.ts +1 -1
  45. package/dist/timeline/retained-image.d.ts +16 -0
  46. package/dist/timeline/rolling-activity.d.ts +9 -0
  47. package/dist/timeline/shared.d.ts +4 -1
  48. package/dist/timeline/turn-summary.d.ts +4 -1
  49. package/package.json +4 -4
  50. package/src/artifacts.ts +2 -0
  51. package/src/components/artifacts/inline-html-document.ts +24 -0
  52. package/src/components/artifacts/published-html-artifact-frame.tsx +32 -2
  53. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  54. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  55. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  56. package/src/components/copy-button.tsx +7 -2
  57. package/src/components/markdown.tsx +111 -34
  58. package/src/components/message-timeline.tsx +61 -4
  59. package/src/components/session-conversation.tsx +5 -0
  60. package/src/components/user-message-body.tsx +20 -2
  61. package/src/hooks/use-composer.ts +15 -2
  62. package/src/hooks/use-computer-session.ts +36 -1
  63. package/src/index.ts +10 -2
  64. package/src/session-ui.ts +4 -1
  65. package/src/sites-ui.tsx +23 -6
  66. package/src/timeline/activity-rail.tsx +20 -3
  67. package/src/timeline/platform-activity-row.tsx +5 -2
  68. package/src/timeline/retained-image.ts +91 -0
  69. package/src/timeline/rolling-activity.tsx +83 -0
  70. package/src/timeline/shared.tsx +33 -12
  71. package/src/timeline/tool-renderers.tsx +7 -91
  72. package/src/timeline/turn-summary.tsx +21 -15
  73. package/styles/compiled.css +122 -0
  74. package/styles/index.css +49 -0
  75. package/dist/chunk-6J56T3MG.js.map +0 -1
  76. package/dist/chunk-7S2KWUJD.js.map +0 -1
  77. package/dist/chunk-BRFMGY4L.js.map +0 -1
  78. package/dist/chunk-M2HNWZYR.js.map +0 -1
  79. package/dist/platform-activity-row-27LKAWZD.js.map +0 -1
  80. /package/dist/{chunk-V3WX3TQG.js.map → chunk-45XEUVTK.js.map} +0 -0
  81. /package/dist/{computer-viewer-7TEZC6Y2.js.map → computer-viewer-AA4JH2AP.js.map} +0 -0
@@ -0,0 +1,83 @@
1
+ import { renderActivity } from "./activity-rail";
2
+ import { AnimatePresence, motion, useReducedMotion } from "motion/react";
3
+ import { WrenchIcon } from "lucide-react";
4
+ import { Suspense, useEffect, useState } from "react";
5
+ import { CompactActivityContext } from "./shared";
6
+ import { defaultToolRegistry } from "./tool-renderers";
7
+ import type { ToolRegistry } from "./registry";
8
+ import { toolDisplayName } from "./tool-display-name";
9
+ import type { ActivityItem } from "./types";
10
+
11
+ /** One stable viewport; updates replace its content without moving the conversation. */
12
+ export function RollingActivity({
13
+ items,
14
+ toolRegistry = defaultToolRegistry,
15
+ previousItem,
16
+ }: {
17
+ items: ActivityItem[];
18
+ /** The standalone item visible immediately before this reel mounted. */
19
+ previousItem?: ActivityItem | undefined;
20
+ toolRegistry?: ToolRegistry;
21
+ }) {
22
+ const reduced = useReducedMotion();
23
+ const [mounted, setMounted] = useState(false);
24
+ useEffect(() => setMounted(true), []);
25
+ const work = items.filter((item) => item.kind !== "startup-phase");
26
+ const active = work.filter((item) =>
27
+ item.kind === "reasoning" ? item.streaming : "status" in item && item.status === "running",
28
+ );
29
+ // Advance with the event order; finishing a parallel tool must not replay an older one.
30
+ const item = !mounted && previousItem ? previousItem : work.at(-1);
31
+ if (!item) return null;
32
+ const earlierCount = Math.max(
33
+ 0,
34
+ work.findIndex((entry) => entry.id === item.id),
35
+ );
36
+ const fallback = (
37
+ <span className="og-rolling-label">
38
+ <WrenchIcon className="size-3.5" />
39
+ <span>{item.kind === "tool-call" ? toolDisplayName(item.name) : "Working"}</span>
40
+ </span>
41
+ );
42
+ return (
43
+ <span
44
+ className="og-rolling-status"
45
+ data-running={active.some((entry) => entry.id === item.id) ? "true" : undefined}
46
+ >
47
+ <span className="sr-only">
48
+ {item.kind === "tool-call"
49
+ ? toolDisplayName(item.name)
50
+ : item.kind === "reasoning"
51
+ ? "Thinking"
52
+ : "Working"}
53
+ </span>
54
+ <span className="og-rolling-window" aria-hidden="true">
55
+ <AnimatePresence initial={false} mode="sync">
56
+ <motion.span
57
+ key={item.id}
58
+ className="og-rolling-face"
59
+ initial={{
60
+ opacity: 0,
61
+ y: reduced ? 0 : 24,
62
+ }}
63
+ animate={{ opacity: 1, y: 0 }}
64
+ exit={{
65
+ opacity: 0,
66
+ y: reduced ? 0 : -24,
67
+ }}
68
+ transition={{ duration: reduced ? 0 : 0.4, ease: [0.22, 1, 0.36, 1] }}
69
+ >
70
+ <CompactActivityContext.Provider value={true}>
71
+ <Suspense fallback={fallback}>
72
+ {renderActivity(item, toolRegistry, undefined, undefined, undefined, undefined)}
73
+ </Suspense>
74
+ </CompactActivityContext.Provider>
75
+ </motion.span>
76
+ </AnimatePresence>
77
+ </span>
78
+ {earlierCount > 0 ? (
79
+ <span className="og-rolling-count">{`+${earlierCount} earlier`}</span>
80
+ ) : null}
81
+ </span>
82
+ );
83
+ }
@@ -55,6 +55,8 @@ export function ToolCallTruncationProvider({
55
55
  }
56
56
 
57
57
  export type ActivityDisclosureProps = {
58
+ /** Override the rolling preview; null keeps rapidly streaming details out of the header. */
59
+ compactPreview?: ReactNode;
58
60
  icon: ReactNode;
59
61
  /** Icon tint. Defaults to the muted foreground; renderers pass accent/failed. */
60
62
  iconTone?: "accent" | "failed" | "running" | "muted" | undefined;
@@ -116,6 +118,8 @@ const ICON_TONE: Record<NonNullable<ActivityDisclosureProps["iconTone"]>, string
116
118
  * inline media, and at most one right-gutter settle chip. Compact by default;
117
119
  * the body mounts only when expanded.
118
120
  */
121
+ export const CompactActivityContext = createContext(false);
122
+
119
123
  export function ActivityDisclosure({
120
124
  icon,
121
125
  iconTone: iconToneProp = "muted",
@@ -123,6 +127,7 @@ export function ActivityDisclosure({
123
127
  titleMono,
124
128
  running,
125
129
  preview,
130
+ compactPreview,
126
131
  media,
127
132
  chip: chipProp,
128
133
  failed,
@@ -150,6 +155,21 @@ export function ActivityDisclosure({
150
155
  const forcedDefaultOpen = useForcedDefaultOpen();
151
156
  const [open, setOpen] = useState(defaultOpen ?? forcedDefaultOpen ?? false);
152
157
  const truncation = useContext(ToolCallTruncationContext);
158
+ const compact = useContext(CompactActivityContext);
159
+ if (compact)
160
+ return (
161
+ <span className="og-rolling-label">
162
+ <span className={cn("shrink-0", ICON_TONE[iconTone])}>{icon}</span>
163
+ <span className="og-command-reel">
164
+ <span className="og-reel-title">{title}</span>
165
+ {(compactPreview === undefined ? preview : compactPreview) ? (
166
+ <span className="og-reel-preview text-og-fg-subtle">
167
+ {compactPreview === undefined ? preview : compactPreview}
168
+ </span>
169
+ ) : null}
170
+ </span>
171
+ </span>
172
+ );
153
173
  const hasBody = expandable && (children != null || truncation != null);
154
174
 
155
175
  // The preview is detail-on-demand: it is suppressed once the row is open so a
@@ -189,20 +209,21 @@ export function ActivityDisclosure({
189
209
  <span className="size-3.5 shrink-0" />
190
210
  )}
191
211
  <span className={cn("shrink-0", ICON_TONE[iconTone])}>{icon}</span>
192
- <span
193
- className={cn(
194
- "min-w-0 shrink truncate text-og-base font-medium",
195
- titleMono && "font-og-mono text-og-sm font-normal",
196
- running && "og-shimmer-text",
212
+ <span className={cn("og-command-reel", running && "og-command-reel-running")}>
213
+ <span
214
+ className={cn(
215
+ "min-w-0 shrink truncate text-og-base font-medium",
216
+ titleMono && "font-og-mono text-og-sm font-normal",
217
+ )}
218
+ >
219
+ {title}
220
+ </span>
221
+ {previewVisible && !media ? (
222
+ <span className="min-w-0 flex-1 truncate text-og-sm text-og-fg-subtle">{preview}</span>
223
+ ) : (
224
+ <span className="flex-1" />
197
225
  )}
198
- >
199
- {title}
200
226
  </span>
201
- {previewVisible && !media ? (
202
- <span className="min-w-0 flex-1 truncate text-og-sm text-og-fg-subtle">{preview}</span>
203
- ) : (
204
- <span className="flex-1" />
205
- )}
206
227
  {/* The right gutter carries at most ONE signal: the media thumbnail, else a
207
228
  terse settle chip (hidden once expanded — the body owns the detail). */}
208
229
  {media && !open ? (
@@ -1,3 +1,4 @@
1
+ import { useRetainedImageObjectUrl } from "./retained-image";
1
2
  import {
2
3
  parseSandboxFileArtifactReceipt,
3
4
  type GitFileDiff,
@@ -34,7 +35,7 @@ import {
34
35
  VideoIcon,
35
36
  WrenchIcon,
36
37
  } from "lucide-react";
37
- import { useEffect, useRef, useState, type ReactNode } from "react";
38
+ import { useContext, useState, type ReactNode } from "react";
38
39
  import { formatBytes, stringifyPayload, tryParseJson } from "../lib/format";
39
40
  import { useTimelineComputeLabel } from "./compute-label";
40
41
  import {
@@ -71,6 +72,7 @@ import {
71
72
  TermBlock,
72
73
  Thumbnail,
73
74
  ActivityDisclosure,
75
+ CompactActivityContext,
74
76
  type DisclosureChip,
75
77
  } from "./shared";
76
78
  import { RawPatch, ToolDiff } from "./tool-diff";
@@ -96,9 +98,12 @@ const ICON_SIZE = "size-3.5";
96
98
  * left-to-right.
97
99
  */
98
100
  function RunningPreview({ children }: { children: ReactNode }) {
101
+ const compact = useContext(CompactActivityContext);
99
102
  return (
100
103
  <span className="inline-flex items-center gap-1.5">
101
- <span className="size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-status-running" />
104
+ {!compact && (
105
+ <span className="size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-status-running" />
106
+ )}
102
107
  <span className="min-w-0 truncate">{children}</span>
103
108
  </span>
104
109
  );
@@ -766,95 +771,6 @@ function ComputerCallRenderer({ item, loadRetainedScreenshot }: ToolRendererProp
766
771
  );
767
772
  }
768
773
 
769
- type RetainedImageState =
770
- | { kind: "loading" }
771
- | { kind: "ready"; url: string }
772
- | { kind: "unavailable"; label: string }
773
- | { kind: "error"; message: string };
774
-
775
- function useRetainedImageObjectUrl(
776
- artifact: RetainedArtifactReference,
777
- load: ToolRendererProps["loadRetainedArtifact"],
778
- ): RetainedImageState {
779
- const [state, setState] = useState<RetainedImageState>({ kind: "loading" });
780
- // Function outputs are commonly serialized JSON. Parsing them creates a new
781
- // object on every render, so depend on the immutable wire value rather than
782
- // object identity; otherwise the loader can refetch after its own setState.
783
- const artifactValue = retainedArtifactValue(artifact);
784
- const stableArtifactRef = useRef({ value: artifactValue, artifact });
785
- if (stableArtifactRef.current.value !== artifactValue) {
786
- stableArtifactRef.current = { value: artifactValue, artifact };
787
- }
788
- const stableArtifact = stableArtifactRef.current.artifact;
789
- useEffect(() => {
790
- if (!load) {
791
- setState({ kind: "unavailable", label: "retrieval is not configured" });
792
- return;
793
- }
794
- const controller = new AbortController();
795
- let objectUrl: string | null = null;
796
- setState({ kind: "loading" });
797
- void load(stableArtifact, controller.signal)
798
- .then((source) => {
799
- if (controller.signal.aborted) return;
800
- if (!source) {
801
- setState({ kind: "unavailable", label: "bytes are unavailable" });
802
- return;
803
- }
804
- if (!(source instanceof Uint8Array)) {
805
- if (!source.url.trim()) {
806
- setState({ kind: "unavailable", label: "URL is unavailable" });
807
- return;
808
- }
809
- setState({ kind: "ready", url: source.url });
810
- return;
811
- }
812
- objectUrl = URL.createObjectURL(
813
- new Blob([source as unknown as BlobPart], { type: stableArtifact.contentType }),
814
- );
815
- setState({ kind: "ready", url: objectUrl });
816
- })
817
- .catch((error: unknown) => {
818
- if (controller.signal.aborted) return;
819
- const status =
820
- error && typeof error === "object" && "status" in error
821
- ? Number((error as { status?: unknown }).status)
822
- : null;
823
- setState(
824
- status === 404
825
- ? { kind: "unavailable", label: "deleted" }
826
- : status === 410
827
- ? { kind: "unavailable", label: "expired or unavailable" }
828
- : { kind: "error", message: "retrieval failed" },
829
- );
830
- });
831
- return () => {
832
- controller.abort();
833
- if (objectUrl) URL.revokeObjectURL(objectUrl);
834
- };
835
- }, [stableArtifact, load]);
836
- return state;
837
- }
838
-
839
- function retainedArtifactValue(artifact: RetainedArtifactReference): string {
840
- return [
841
- artifact.artifactId,
842
- artifact.kind,
843
- artifact.contentType,
844
- artifact.originalBytes,
845
- artifact.sha256,
846
- artifact.retainedAt,
847
- artifact.dimensions?.width ?? "",
848
- artifact.dimensions?.height ?? "",
849
- artifact.retention.policy,
850
- artifact.retention.expiresAt ?? "",
851
- artifact.retrieval.method,
852
- artifact.retrieval.path,
853
- artifact.retrieval.acceptRanges,
854
- artifact.retrieval.maxRangeBytes,
855
- ].join("\0");
856
- }
857
-
858
774
  function RetainedSessionImageDisclosure({
859
775
  artifact,
860
776
  load,
@@ -115,6 +115,8 @@ type ReplaceTurnSummaryFacets = Readonly<{
115
115
  export type TurnSummaryFacetConfiguration = ModifyTurnSummaryFacets | ReplaceTurnSummaryFacets;
116
116
 
117
117
  export type TurnSummaryOptions = Readonly<{
118
+ /** Experimental compact live activity reel. */
119
+ rolling?: boolean;
118
120
  facets?: TurnSummaryFacetConfiguration;
119
121
  }>;
120
122
 
@@ -133,6 +135,7 @@ export type TurnSummaryProps = {
133
135
  durationMs?: number | undefined;
134
136
  /** Start expanded. */
135
137
  defaultOpen?: boolean | undefined;
138
+ liveHeader?: ReactNode;
136
139
  /**
137
140
  * A nested fold — a cluster or sub-turn INSIDE an already-expanded turn. It
138
141
  * drops the bordered/filled chip and renders as a plain disclosure node on the
@@ -187,6 +190,7 @@ export function TurnSummary({
187
190
  failureText,
188
191
  durationMs,
189
192
  defaultOpen,
193
+ liveHeader,
190
194
  bare,
191
195
  facets: facetConfiguration,
192
196
  settleFold,
@@ -443,7 +447,7 @@ export function TurnSummary({
443
447
  />
444
448
  {/* Completion is the quiet default and needs no repeated glyph. Failed,
445
449
  cancelled, and still-running folds retain a visible state marker. */}
446
- {outcome === "complete" ? null : (
450
+ {outcome === "complete" || (liveHeader && !outcome) ? null : (
447
451
  <span
448
452
  className={cn(
449
453
  "inline-flex shrink-0 items-center justify-center",
@@ -463,21 +467,23 @@ export function TurnSummary({
463
467
  <span
464
468
  className={cn("min-w-0 flex-1 truncate", bare ? "text-og-sm" : "text-og-fg-muted")}
465
469
  >
466
- {facets.map(({ facet, result }, index) => (
467
- <FacetRenderBoundary key={facet.id}>
468
- <>
469
- {index > 0 ? " · " : null}
470
- <span aria-label={result.ariaLabel} title={result.title}>
471
- {result.icon ? (
472
- <span aria-hidden className="mr-1 inline-flex align-[-0.125em]">
473
- {result.icon}
470
+ {liveHeader && !open
471
+ ? liveHeader
472
+ : facets.map(({ facet, result }, index) => (
473
+ <FacetRenderBoundary key={facet.id}>
474
+ <>
475
+ {index > 0 ? " · " : null}
476
+ <span aria-label={result.ariaLabel} title={result.title}>
477
+ {result.icon ? (
478
+ <span aria-hidden className="mr-1 inline-flex align-[-0.125em]">
479
+ {result.icon}
480
+ </span>
481
+ ) : null}
482
+ {result.content}
474
483
  </span>
475
- ) : null}
476
- {result.content}
477
- </span>
478
- </>
479
- </FacetRenderBoundary>
480
- ))}
484
+ </>
485
+ </FacetRenderBoundary>
486
+ ))}
481
487
  {outcome === "failed" && failureText ? (
482
488
  <span className="text-og-status-failed"> · {failureText}</span>
483
489
  ) : null}
@@ -443,6 +443,8 @@
443
443
  }
444
444
  /*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */
445
445
  :where(.og-root) {
446
+ --font-sans: var(--og-font-sans);
447
+ --font-mono: var(--og-font-mono);
446
448
  --color-black: #000;
447
449
  --color-white: #fff;
448
450
  --spacing: 0.25rem;
@@ -469,7 +471,11 @@
469
471
  --leading-tight: 1.25;
470
472
  --leading-snug: 1.375;
471
473
  --leading-relaxed: 1.625;
474
+ --radius-sm: var(--og-radius-sm);
475
+ --radius-md: var(--og-radius-md);
476
+ --radius-lg: var(--og-radius-lg);
472
477
  --radius-2xl: 1rem;
478
+ --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
473
479
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
474
480
  --blur-sm: 8px;
475
481
  --blur-md: 12px;
@@ -911,6 +917,110 @@
911
917
  animation: none;
912
918
  }
913
919
  }
920
+ :where(.og-root).og-rolling-status,:where(.og-root) .og-rolling-status {
921
+ display: flex;
922
+ align-items: center;
923
+ gap: 12px;
924
+ min-width: 0;
925
+ width: 100%;
926
+ }
927
+ :where(.og-root).og-rolling-window,:where(.og-root) .og-rolling-window {
928
+ position: relative;
929
+ display: block;
930
+ flex: 1;
931
+ min-width: 0;
932
+ height: 24px;
933
+ overflow: hidden;
934
+ }
935
+ :where(.og-root).og-rolling-face,:where(.og-root) .og-rolling-face {
936
+ position: absolute;
937
+ inset: 0;
938
+ display: flex;
939
+ align-items: center;
940
+ }
941
+ :where(.og-root).og-rolling-label,:where(.og-root) .og-rolling-label {
942
+ display: flex;
943
+ align-items: center;
944
+ gap: 8px;
945
+ min-width: 0;
946
+ width: 100%;
947
+ font-size: 13px;
948
+ font-weight: 450;
949
+ line-height: 20px;
950
+ color: var(--og-color-fg-muted);
951
+ }
952
+ :where(.og-root).og-rolling-label > svg,:where(.og-root) .og-rolling-label > svg {
953
+ flex-shrink: 0;
954
+ }
955
+ :where(.og-root).og-rolling-count,:where(.og-root) .og-rolling-count {
956
+ flex-shrink: 0;
957
+ font-size: 11px;
958
+ color: var(--og-color-fg-subtle);
959
+ font-variant-numeric: tabular-nums;
960
+ }
961
+ :where(.og-root).og-command-reel,:where(.og-root) .og-command-reel {
962
+ display: flex;
963
+ flex: 1;
964
+ align-items: center;
965
+ gap: 8px;
966
+ min-width: 0;
967
+ transform-origin: 50% 50%;
968
+ }
969
+ :where(.og-root).og-reasoning-preview,:where(.og-root) .og-reasoning-preview,:where(.og-root) .og-reasoning-preview :is(div,p,h1,h2,h3,h4,h5,h6,ul,ol,li,blockquote,pre),:where(.og-root) .og-reasoning-preview :is(div,p,h1,h2,h3,h4,h5,h6,ul,ol,li,blockquote,pre) {
970
+ display: inline;
971
+ margin: 0;
972
+ padding: 0;
973
+ font-size: inherit;
974
+ line-height: inherit;
975
+ }
976
+ :where(.og-root).og-reasoning-preview br,:where(.og-root) .og-reasoning-preview br {
977
+ display: none;
978
+ }
979
+ :where(.og-root).og-reasoning-preview :is(p,li,h1,h2,h3,h4)::after,:where(.og-root) .og-reasoning-preview :is(p,li,h1,h2,h3,h4)::after {
980
+ content: " ";
981
+ }
982
+ :where(.og-root).og-reel-title,:where(.og-root) .og-reel-title {
983
+ flex: 0 0 auto;
984
+ min-width: 0;
985
+ max-width: 75%;
986
+ overflow: hidden;
987
+ text-overflow: ellipsis;
988
+ white-space: nowrap;
989
+ }
990
+ :where(.og-root).og-reel-title:only-child,:where(.og-root) .og-reel-title:only-child {
991
+ max-width: 100%;
992
+ }
993
+ :where(.og-root).og-reel-preview,:where(.og-root) .og-reel-preview {
994
+ flex: 1 1 0%;
995
+ min-width: 0;
996
+ overflow: hidden;
997
+ text-overflow: ellipsis;
998
+ white-space: nowrap;
999
+ }
1000
+ :where(.og-root).og-command-reel-running,:where(.og-root) .og-command-reel-running,:where(.og-root) .og-rolling-status[data-running="true"] .og-command-reel,:where(.og-root) .og-rolling-status[data-running="true"] .og-command-reel {
1001
+ background-image: linear-gradient(90deg, light-dark(#898c94,#686b76) 42%, light-dark(#f0f1f4,#fff) 50%, light-dark(#898c94,#686b76) 58%);
1002
+ background-size: 300% 100%;
1003
+ background-position: 100% 0;
1004
+ background-clip: text;
1005
+ -webkit-background-clip: text;
1006
+ -webkit-text-fill-color: transparent;
1007
+ animation: og-activity-sweep 3.6s linear infinite;
1008
+ }
1009
+ @keyframes og-activity-sweep {
1010
+ from {
1011
+ background-position: 100% 0;
1012
+ }
1013
+ to {
1014
+ background-position: 0% 0;
1015
+ }
1016
+ }
1017
+ @media (prefers-reduced-motion:reduce), (forced-colors:active) {
1018
+ :where(.og-root).og-command-reel-running,:where(.og-root) .og-command-reel-running,:where(.og-root) .og-rolling-status[data-running="true"] .og-command-reel,:where(.og-root) .og-rolling-status[data-running="true"] .og-command-reel {
1019
+ animation: none;
1020
+ background: none;
1021
+ -webkit-text-fill-color: currentColor;
1022
+ }
1023
+ }
914
1024
  :where(.og-root).pointer-events-auto,:where(.og-root) .pointer-events-auto {
915
1025
  pointer-events: auto;
916
1026
  }
@@ -1717,6 +1827,9 @@
1717
1827
  :where(.og-root).flex-\[1_1_5rem\],:where(.og-root) .flex-\[1_1_5rem\] {
1718
1828
  flex: 1 1 5rem;
1719
1829
  }
1830
+ :where(.og-root).flex-shrink,:where(.og-root) .flex-shrink {
1831
+ flex-shrink: 1;
1832
+ }
1720
1833
  :where(.og-root).shrink,:where(.og-root) .shrink {
1721
1834
  flex-shrink: 1;
1722
1835
  }
@@ -3207,6 +3320,9 @@
3207
3320
  :where(.og-root).text-balance,:where(.og-root) .text-balance {
3208
3321
  text-wrap: balance;
3209
3322
  }
3323
+ :where(.og-root).text-nowrap,:where(.og-root) .text-nowrap {
3324
+ text-wrap: nowrap;
3325
+ }
3210
3326
  :where(.og-root).\[overflow-wrap\:anywhere\],:where(.og-root) .\[overflow-wrap\:anywhere\] {
3211
3327
  overflow-wrap: anywhere;
3212
3328
  }
@@ -3231,6 +3347,9 @@
3231
3347
  :where(.og-root).whitespace-pre-wrap,:where(.og-root) .whitespace-pre-wrap {
3232
3348
  white-space: pre-wrap;
3233
3349
  }
3350
+ :where(.og-root).text-destructive,:where(.og-root) .text-destructive {
3351
+ color: var(--og-color-danger);
3352
+ }
3234
3353
  :where(.og-root).text-fg,:where(.og-root) .text-fg {
3235
3354
  color: var(--og-color-fg);
3236
3355
  }
@@ -3240,6 +3359,9 @@
3240
3359
  :where(.og-root).text-fg-subtle,:where(.og-root) .text-fg-subtle {
3241
3360
  color: var(--og-color-fg-subtle);
3242
3361
  }
3362
+ :where(.og-root).text-muted,:where(.og-root) .text-muted {
3363
+ color: var(--color-surface-2);
3364
+ }
3243
3365
  :where(.og-root).text-og-accent,:where(.og-root) .text-og-accent {
3244
3366
  color: var(--og-color-accent);
3245
3367
  }
package/styles/index.css CHANGED
@@ -594,3 +594,52 @@
594
594
  @media (prefers-reduced-motion: reduce) {
595
595
  .og-genie-phrase { animation: none; }
596
596
  }
597
+
598
+ /* A single softly lit reel: motion stays inside the header's fixed footprint. */
599
+ .og-rolling-status { display:flex; align-items:center; gap:12px; min-width:0; width:100%; }
600
+ .og-rolling-window { position:relative; display:block; flex:1; min-width:0; height:24px; overflow:hidden; }
601
+ .og-rolling-face { position:absolute; inset:0; display:flex; align-items:center; }
602
+ .og-rolling-label { display:flex; align-items:center; gap:8px; min-width:0; width:100%; font-size:13px; font-weight:450; line-height:20px; color:var(--og-color-fg-muted); }
603
+ .og-rolling-label > svg { flex-shrink:0; }
604
+ .og-rolling-count { flex-shrink:0; font-size:11px; color:var(--og-color-fg-subtle); font-variant-numeric:tabular-nums; }
605
+
606
+ /* The complete text line rolls as one surface, without moving its layout box. */
607
+ .og-command-reel { display:flex; flex:1; align-items:center; gap:8px; min-width:0; transform-origin:50% 50%; }
608
+ /* Render reasoning Markdown as one clipped preview; full formatting lives inside. */
609
+ .og-reasoning-preview,
610
+ .og-reasoning-preview :is(div,p,h1,h2,h3,h4,h5,h6,ul,ol,li,blockquote,pre) {
611
+ display: inline;
612
+ margin: 0;
613
+ padding: 0;
614
+ font-size: inherit;
615
+ line-height: inherit;
616
+ }
617
+ .og-reasoning-preview br { display: none; }
618
+ .og-reasoning-preview :is(p,li,h1,h2,h3,h4)::after { content: " "; }
619
+
620
+ /* Details consume leftover width; they never push the activity name away. */
621
+ .og-reel-title { flex:0 0 auto; min-width:0; max-width:75%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
622
+ .og-reel-title:only-child { max-width:100%; }
623
+ .og-reel-preview { flex:1 1 0%; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
624
+
625
+ /* Focused beam: one shared light source clipped to the entire text row. */
626
+ .og-command-reel-running,
627
+ .og-rolling-status[data-running="true"] .og-command-reel {
628
+ background-image:linear-gradient(90deg, light-dark(#898c94,#686b76) 42%, light-dark(#f0f1f4,#fff) 50%, light-dark(#898c94,#686b76) 58%);
629
+ background-size:300% 100%;
630
+ background-position:100% 0;
631
+ background-clip:text;
632
+ -webkit-background-clip:text;
633
+ -webkit-text-fill-color:transparent;
634
+ animation:og-activity-sweep 3.6s linear infinite;
635
+ }
636
+ @keyframes og-activity-sweep {
637
+ from { background-position:100% 0; }
638
+ to { background-position:0% 0; }
639
+ }
640
+ @media (prefers-reduced-motion:reduce), (forced-colors:active) {
641
+ .og-command-reel-running,
642
+ .og-rolling-status[data-running="true"] .og-command-reel {
643
+ animation:none; background:none; -webkit-text-fill-color:currentColor;
644
+ }
645
+ }