@opengeni/react 3.5.1-canary.0 → 3.7.0-canary.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.
Files changed (87) hide show
  1. package/dist/artifacts.d.ts +1 -1
  2. package/dist/artifacts.js +248 -6
  3. package/dist/artifacts.js.map +1 -1
  4. package/dist/{browser-viewer-E5DDEFQ4.js → browser-viewer-NTD6UIPR.js} +3 -3
  5. package/dist/{chunk-W2RGYBCV.js → chunk-24M4YBCL.js} +4 -4
  6. package/dist/{chunk-WY3MELVJ.js → chunk-5IXTJTK3.js} +1438 -397
  7. package/dist/chunk-5IXTJTK3.js.map +1 -0
  8. package/dist/chunk-5PQJOCX5.js +902 -0
  9. package/dist/chunk-5PQJOCX5.js.map +1 -0
  10. package/dist/{chunk-UNCXXIQR.js → chunk-INGB2N5R.js} +4310 -3982
  11. package/dist/chunk-INGB2N5R.js.map +1 -0
  12. package/dist/{chunk-TJFMMDQU.js → chunk-MOWYEBCQ.js} +1839 -17
  13. package/dist/chunk-MOWYEBCQ.js.map +1 -0
  14. package/dist/chunk-PAJU5KKL.js +331 -0
  15. package/dist/chunk-PAJU5KKL.js.map +1 -0
  16. package/dist/{chunk-VTOREEXF.js → chunk-PLYRL5ER.js} +24 -2
  17. package/dist/{chunk-VTOREEXF.js.map → chunk-PLYRL5ER.js.map} +1 -1
  18. package/dist/{chunk-YVCHAQZY.js → chunk-ZFOFY5ST.js} +27 -349
  19. package/dist/chunk-ZFOFY5ST.js.map +1 -0
  20. package/dist/client.d.ts +1 -1
  21. package/dist/components/artifacts/index.d.ts +1 -1
  22. package/dist/components/artifacts/published-html-artifact-frame.d.ts +46 -3
  23. package/dist/components/composer.d.ts +2 -0
  24. package/dist/components/session-chrome.d.ts +38 -5
  25. package/dist/components/session-commands-panel.d.ts +6 -0
  26. package/dist/components/session-conversation.d.ts +14 -0
  27. package/dist/components/timeline-anchor.d.ts +21 -0
  28. package/dist/composer.js +2 -3
  29. package/dist/conversation-timeline.d.ts +5 -0
  30. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-YZ3ZKWMM.js} +2 -1
  31. package/dist/fleet-decision-row-YZ3ZKWMM.js.map +1 -0
  32. package/dist/hooks/use-file-attachments.d.ts +6 -0
  33. package/dist/hooks/use-sandbox-files.d.ts +4 -1
  34. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  35. package/dist/index.d.ts +4 -1
  36. package/dist/index.js +823 -965
  37. package/dist/index.js.map +1 -1
  38. package/dist/interaction.js +2 -2
  39. package/dist/lib/format.d.ts +5 -0
  40. package/dist/machines.js +3 -2
  41. package/dist/session-ui.d.ts +3 -0
  42. package/dist/session-ui.js +14 -4
  43. package/dist/session.js +19 -19
  44. package/dist/timeline/registry.d.ts +2 -0
  45. package/dist/timeline/types.d.ts +4 -2
  46. package/package.json +2 -2
  47. package/src/artifacts.ts +3 -0
  48. package/src/client.ts +2 -1
  49. package/src/components/artifacts/index.ts +3 -0
  50. package/src/components/artifacts/published-html-artifact-frame.tsx +307 -4
  51. package/src/components/composer.tsx +89 -25
  52. package/src/components/machine-input-display.ts +6 -0
  53. package/src/components/message-timeline.tsx +351 -204
  54. package/src/components/sandbox-workspace.tsx +11 -1
  55. package/src/components/session-chrome.tsx +501 -128
  56. package/src/components/session-commands-panel.tsx +113 -0
  57. package/src/components/session-conversation.tsx +141 -0
  58. package/src/components/timeline-anchor.tsx +91 -0
  59. package/src/conversation-timeline.ts +83 -0
  60. package/src/hooks/use-file-attachments.ts +34 -0
  61. package/src/hooks/use-sandbox-files.ts +68 -20
  62. package/src/hooks/use-session-background-commands.ts +21 -10
  63. package/src/hooks/use-session-events.ts +56 -39
  64. package/src/hooks/use-session.ts +23 -2
  65. package/src/hooks/use-workspace-edit.ts +23 -5
  66. package/src/index.ts +4 -0
  67. package/src/lib/format.ts +32 -0
  68. package/src/session-ui.ts +3 -0
  69. package/src/timeline/fleet-decision-projection.ts +2 -1
  70. package/src/timeline/fleet-decision-row.tsx +1 -0
  71. package/src/timeline/projection.ts +157 -19
  72. package/src/timeline/registry.ts +15 -3
  73. package/src/timeline/tool-renderers.tsx +117 -1
  74. package/src/timeline/types.ts +12 -1
  75. package/styles/compiled.css +495 -411
  76. package/styles/index.css +23 -0
  77. package/dist/chunk-7EAFGICR.js +0 -302
  78. package/dist/chunk-7EAFGICR.js.map +0 -1
  79. package/dist/chunk-IB27C53Y.js +0 -2582
  80. package/dist/chunk-IB27C53Y.js.map +0 -1
  81. package/dist/chunk-TJFMMDQU.js.map +0 -1
  82. package/dist/chunk-UNCXXIQR.js.map +0 -1
  83. package/dist/chunk-WY3MELVJ.js.map +0 -1
  84. package/dist/chunk-YVCHAQZY.js.map +0 -1
  85. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  86. /package/dist/{browser-viewer-E5DDEFQ4.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
  87. /package/dist/{chunk-W2RGYBCV.js.map → chunk-24M4YBCL.js.map} +0 -0
package/styles/index.css CHANGED
@@ -17,6 +17,29 @@
17
17
  @import "./tokens.css";
18
18
  @import "./effective-tokens.css";
19
19
 
20
+ /* Standalone embeds do not necessarily load Tailwind Preflight. Keep the
21
+ control reset inside SDK roots, before utilities, without resetting the host.
22
+ :where keeps controls at the same specificity as component utilities. */
23
+ @layer base {
24
+ .og-root :where([hidden]:not([hidden="until-found"])) {
25
+ display: none !important;
26
+ }
27
+ .og-root :where(button, input, textarea, select) {
28
+ box-sizing: border-box;
29
+ margin: 0;
30
+ padding: 0;
31
+ border: 0 solid;
32
+ border-radius: 0;
33
+ background-color: transparent;
34
+ color: inherit;
35
+ font: inherit;
36
+ letter-spacing: inherit;
37
+ }
38
+ .og-root :where(button, input[type="button"], input[type="submit"], input[type="reset"]) {
39
+ appearance: button;
40
+ }
41
+ }
42
+
20
43
  @theme inline {
21
44
  --font-og-sans: var(--og-font-sans);
22
45
  --font-og-mono: var(--og-font-mono);
@@ -1,302 +0,0 @@
1
- import {
2
- usePortalTokenStyle
3
- } from "./chunk-VZTQ73XT.js";
4
- import {
5
- cn
6
- } from "./chunk-22KP6LR5.js";
7
-
8
- // src/components/tooltip.tsx
9
- import { Tooltip as TooltipPrimitive } from "radix-ui";
10
- import {
11
- createContext,
12
- useContext,
13
- useLayoutEffect,
14
- useMemo,
15
- useRef,
16
- useState
17
- } from "react";
18
- import { jsx, jsxs } from "react/jsx-runtime";
19
- var TooltipSourceContext = createContext(null);
20
- function TooltipProvider({
21
- delayDuration = 300,
22
- ...props
23
- }) {
24
- return /* @__PURE__ */ jsx(TooltipPrimitive.Provider, { delayDuration, ...props });
25
- }
26
- function Tooltip({ children, ...props }) {
27
- const { onOpenChange, ...rootProps } = props;
28
- const sourceRef = useRef(null);
29
- const sourcePublishedRef = useRef(false);
30
- const [source, setSource] = useState(null);
31
- const ref = useRef((node) => {
32
- sourceRef.current = node;
33
- if (sourcePublishedRef.current) setSource(node);
34
- }).current;
35
- useLayoutEffect(() => {
36
- if (rootProps.open === true || rootProps.defaultOpen === true) {
37
- sourcePublishedRef.current = true;
38
- setSource(sourceRef.current);
39
- }
40
- }, [rootProps.defaultOpen, rootProps.open]);
41
- const sourceContext = useMemo(() => ({ source, ref }), [ref, source]);
42
- return /* @__PURE__ */ jsx(TooltipSourceContext.Provider, { value: sourceContext, children: /* @__PURE__ */ jsx(
43
- TooltipPrimitive.Root,
44
- {
45
- "data-slot": "tooltip",
46
- onOpenChange: (open) => {
47
- if (open) {
48
- sourcePublishedRef.current = true;
49
- setSource(sourceRef.current);
50
- }
51
- onOpenChange?.(open);
52
- },
53
- ...rootProps,
54
- children
55
- }
56
- ) });
57
- }
58
- function TooltipTrigger({
59
- asChild = true,
60
- ...props
61
- }) {
62
- const sourceRef = useContext(TooltipSourceContext);
63
- return /* @__PURE__ */ jsx(
64
- TooltipPrimitive.Trigger,
65
- {
66
- "data-slot": "tooltip-trigger",
67
- asChild,
68
- ref: (node) => {
69
- sourceRef?.ref(node);
70
- },
71
- ...props
72
- }
73
- );
74
- }
75
- var tooltipSurfaceStyle = {
76
- backgroundColor: "var(--og-color-fg)",
77
- color: "var(--og-color-bg)"
78
- };
79
- function TooltipContent({
80
- className,
81
- sideOffset = 6,
82
- children,
83
- style,
84
- ...props
85
- }) {
86
- const source = useContext(TooltipSourceContext);
87
- const portalStyle = usePortalTokenStyle(source?.source ?? null);
88
- return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
89
- TooltipPrimitive.Content,
90
- {
91
- "data-slot": "tooltip-content",
92
- sideOffset,
93
- className: cn(
94
- "og-root z-50 w-fit max-w-xs origin-[var(--radix-tooltip-content-transform-origin)]",
95
- "rounded-og-md px-2.5 py-1.5 text-og-xs leading-4 text-balance shadow-og-md",
96
- "animate-og-enter data-[state=closed]:opacity-0 data-[state=closed]:transition-opacity",
97
- className
98
- ),
99
- style: { ...portalStyle, ...tooltipSurfaceStyle, ...style },
100
- ...props,
101
- children: [
102
- children,
103
- /* @__PURE__ */ jsx(
104
- TooltipPrimitive.Arrow,
105
- {
106
- className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]",
107
- style: {
108
- backgroundColor: "var(--og-color-fg)",
109
- fill: "var(--og-color-fg)"
110
- }
111
- }
112
- )
113
- ]
114
- }
115
- ) });
116
- }
117
-
118
- // src/timeline/screenshot-lightbox.tsx
119
- import { DownloadIcon, XIcon } from "lucide-react";
120
- import { AnimatePresence, motion } from "motion/react";
121
- import {
122
- createContext as createContext2,
123
- useCallback,
124
- useContext as useContext2,
125
- useEffect,
126
- useMemo as useMemo2,
127
- useState as useState2
128
- } from "react";
129
- import { Dialog } from "radix-ui";
130
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
131
- var LightboxContext = createContext2(null);
132
- function useLightbox() {
133
- return useContext2(LightboxContext) ?? NOOP;
134
- }
135
- function useLightboxOptional() {
136
- return useContext2(LightboxContext);
137
- }
138
- var NOOP = { open: () => {
139
- } };
140
- function createLightboxSourceOwner() {
141
- let releaseCurrent = null;
142
- return {
143
- replace(release) {
144
- const previous = releaseCurrent;
145
- releaseCurrent = release ?? null;
146
- previous?.();
147
- },
148
- release() {
149
- const release = releaseCurrent;
150
- releaseCurrent = null;
151
- release?.();
152
- }
153
- };
154
- }
155
- function LightboxProvider({ children }) {
156
- const ancestor = useContext2(LightboxContext);
157
- if (ancestor) {
158
- return /* @__PURE__ */ jsx2(Fragment, { children });
159
- }
160
- return /* @__PURE__ */ jsx2(LightboxRoot, { children });
161
- }
162
- function LightboxRoot({ children }) {
163
- const [state, setState] = useState2(null);
164
- const [tokenSource, setTokenSource] = useState2(null);
165
- const sourceOwner = useMemo2(() => createLightboxSourceOwner(), []);
166
- const portalStyle = usePortalTokenStyle(tokenSource);
167
- const releaseCurrentSource = useCallback(() => {
168
- sourceOwner.release();
169
- }, [sourceOwner]);
170
- useEffect(() => releaseCurrentSource, [releaseCurrentSource]);
171
- const open = useCallback(
172
- (src, caption, source, label = "Screenshot", downloadFilename, controlLabels, releaseSource) => {
173
- sourceOwner.replace(releaseSource);
174
- setTokenSource(source ?? null);
175
- setState({
176
- src,
177
- label,
178
- ...caption ? { caption } : {},
179
- ...downloadFilename ? { downloadFilename } : {},
180
- ...controlLabels ? { controlLabels } : {}
181
- });
182
- },
183
- [sourceOwner]
184
- );
185
- const close = useCallback(() => {
186
- releaseCurrentSource();
187
- setState(null);
188
- setTokenSource(null);
189
- }, [releaseCurrentSource]);
190
- const controller = useMemo2(() => ({ open }), [open]);
191
- return /* @__PURE__ */ jsxs2(LightboxContext.Provider, { value: controller, children: [
192
- children,
193
- /* @__PURE__ */ jsx2(Dialog.Root, { open: state !== null, onOpenChange: (next) => !next && close(), children: /* @__PURE__ */ jsx2(AnimatePresence, { children: state !== null ? /* @__PURE__ */ jsxs2(Dialog.Portal, { forceMount: true, children: [
194
- /* @__PURE__ */ jsx2(Dialog.Overlay, { asChild: true, forceMount: true, children: /* @__PURE__ */ jsx2(
195
- motion.div,
196
- {
197
- initial: { opacity: 0 },
198
- animate: { opacity: 1 },
199
- exit: { opacity: 0 },
200
- transition: { duration: 0.15 },
201
- className: "og-root fixed inset-0 z-50 bg-black/90 backdrop-blur-md",
202
- style: portalStyle
203
- }
204
- ) }),
205
- /* @__PURE__ */ jsx2(
206
- Dialog.Content,
207
- {
208
- asChild: true,
209
- forceMount: true,
210
- "aria-label": state.label,
211
- onOpenAutoFocus: (event) => event.preventDefault(),
212
- children: /* @__PURE__ */ jsxs2(
213
- motion.div,
214
- {
215
- initial: { opacity: 0, scale: 0.98 },
216
- animate: { opacity: 1, scale: 1 },
217
- exit: { opacity: 0, scale: 0.98 },
218
- transition: { duration: 0.18, ease: [0.16, 1, 0.3, 1] },
219
- onClick: (event) => {
220
- if (event.target === event.currentTarget) {
221
- close();
222
- }
223
- },
224
- className: "og-root fixed inset-0 z-50 flex items-center justify-center p-6 sm:p-12",
225
- style: portalStyle,
226
- children: [
227
- /* @__PURE__ */ jsx2(Dialog.Title, { className: "sr-only", children: state.label }),
228
- /* @__PURE__ */ jsx2(Dialog.Description, { className: "sr-only", children: state.caption ?? `Expanded ${state.label.toLowerCase()}` }),
229
- /* @__PURE__ */ jsxs2("figure", { className: "m-0 flex max-h-full max-w-5xl flex-col items-center gap-3", children: [
230
- /* @__PURE__ */ jsxs2("div", { className: "relative flex min-h-0 w-fit max-w-full", children: [
231
- /* @__PURE__ */ jsx2(
232
- "img",
233
- {
234
- src: state.src,
235
- alt: state.caption ?? state.label,
236
- className: "min-h-0 max-h-[82vh] w-auto max-w-full rounded-og-md border border-white/10 object-contain shadow-og-lg"
237
- }
238
- ),
239
- /* @__PURE__ */ jsx2(
240
- ScreenshotLightboxControls,
241
- {
242
- src: state.src,
243
- downloadFilename: state.downloadFilename,
244
- controlLabels: state.controlLabels
245
- }
246
- )
247
- ] }),
248
- state.caption ? /* @__PURE__ */ jsx2("figcaption", { className: "max-w-2xl text-center font-og-mono text-og-xs text-white/55", children: state.caption }) : /* @__PURE__ */ jsx2("figcaption", { className: "font-og-mono text-og-xs uppercase tracking-[0.1em] text-white/35", children: "Esc or click outside to close" })
249
- ] })
250
- ]
251
- }
252
- )
253
- }
254
- )
255
- ] }) : null }) })
256
- ] });
257
- }
258
- function ScreenshotLightboxControls({
259
- src,
260
- downloadFilename,
261
- controlLabels
262
- }) {
263
- return /* @__PURE__ */ jsxs2("div", { className: "absolute -right-3 -top-3 flex items-center gap-2", children: [
264
- downloadFilename ? /* @__PURE__ */ jsx2(
265
- "a",
266
- {
267
- href: src,
268
- download: downloadFilename,
269
- className: cn(
270
- "inline-flex size-9 items-center justify-center rounded-full",
271
- "border border-white/15 bg-black/60 text-white/70 backdrop-blur",
272
- "transition-colors hover:border-white/30 hover:text-white"
273
- ),
274
- "aria-label": controlLabels?.download ?? `Download ${downloadFilename}`,
275
- children: /* @__PURE__ */ jsx2(DownloadIcon, { className: "size-4" })
276
- }
277
- ) : null,
278
- /* @__PURE__ */ jsx2(
279
- Dialog.Close,
280
- {
281
- className: cn(
282
- "inline-flex size-9 items-center justify-center rounded-full",
283
- "border border-white/15 bg-black/60 text-white/70 backdrop-blur",
284
- "transition-colors hover:border-white/30 hover:text-white"
285
- ),
286
- "aria-label": controlLabels?.close ?? "Close",
287
- children: /* @__PURE__ */ jsx2(XIcon, { className: "size-4" })
288
- }
289
- )
290
- ] });
291
- }
292
-
293
- export {
294
- TooltipProvider,
295
- Tooltip,
296
- TooltipTrigger,
297
- TooltipContent,
298
- useLightbox,
299
- useLightboxOptional,
300
- LightboxProvider
301
- };
302
- //# sourceMappingURL=chunk-7EAFGICR.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/tooltip.tsx","../src/timeline/screenshot-lightbox.tsx"],"sourcesContent":["/**\n * Token-styled Radix tooltip — SDK counterpart to apps/web `components/ui/tooltip`.\n * Portals copy `--og-*` tokens so embeds keep the host theme outside `.og-root`.\n *\n * Background/foreground are applied as inline styles (not only Tailwind\n * utilities): `.og-root` sets `color: var(--og-color-fg)` after utilities in the\n * consumer CSS cascade, which would otherwise paint light-on-light inverted\n * tips and look like a blank white bubble.\n */\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\";\nimport {\n createContext,\n useContext,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type ComponentProps,\n type CSSProperties,\n} from \"react\";\n\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\n\ntype TooltipSourceContextValue = {\n source: HTMLElement | null;\n ref: (node: HTMLElement | null) => void;\n};\n\nconst TooltipSourceContext = createContext<TooltipSourceContextValue | null>(null);\n\nfunction TooltipProvider({\n delayDuration = 300,\n ...props\n}: ComponentProps<typeof TooltipPrimitive.Provider>) {\n return <TooltipPrimitive.Provider delayDuration={delayDuration} {...props} />;\n}\n\nfunction Tooltip({ children, ...props }: ComponentProps<typeof TooltipPrimitive.Root>) {\n const { onOpenChange, ...rootProps } = props;\n const sourceRef = useRef<HTMLElement | null>(null);\n const sourcePublishedRef = useRef(false);\n const [source, setSource] = useState<HTMLElement | null>(null);\n const ref = useRef((node: HTMLElement | null) => {\n sourceRef.current = node;\n if (sourcePublishedRef.current) setSource(node);\n }).current;\n useLayoutEffect(() => {\n if (rootProps.open === true || rootProps.defaultOpen === true) {\n sourcePublishedRef.current = true;\n setSource(sourceRef.current);\n }\n }, [rootProps.defaultOpen, rootProps.open]);\n const sourceContext = useMemo(() => ({ source, ref }), [ref, source]);\n return (\n <TooltipSourceContext.Provider value={sourceContext}>\n <TooltipPrimitive.Root\n data-slot=\"tooltip\"\n onOpenChange={(open) => {\n if (open) {\n sourcePublishedRef.current = true;\n setSource(sourceRef.current);\n }\n onOpenChange?.(open);\n }}\n {...rootProps}\n >\n {children}\n </TooltipPrimitive.Root>\n </TooltipSourceContext.Provider>\n );\n}\n\nfunction TooltipTrigger({\n asChild = true,\n ...props\n}: ComponentProps<typeof TooltipPrimitive.Trigger>) {\n const sourceRef = useContext(TooltipSourceContext);\n return (\n <TooltipPrimitive.Trigger\n data-slot=\"tooltip-trigger\"\n asChild={asChild}\n ref={(node) => {\n sourceRef?.ref(node);\n }}\n {...props}\n />\n );\n}\n\nconst tooltipSurfaceStyle: CSSProperties = {\n backgroundColor: \"var(--og-color-fg)\",\n color: \"var(--og-color-bg)\",\n};\n\nfunction TooltipContent({\n className,\n sideOffset = 6,\n children,\n style,\n ...props\n}: ComponentProps<typeof TooltipPrimitive.Content>) {\n const source = useContext(TooltipSourceContext);\n const portalStyle = usePortalTokenStyle(source?.source ?? null);\n\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n data-slot=\"tooltip-content\"\n sideOffset={sideOffset}\n className={cn(\n \"og-root z-50 w-fit max-w-xs origin-[var(--radix-tooltip-content-transform-origin)]\",\n \"rounded-og-md px-2.5 py-1.5 text-og-xs leading-4 text-balance shadow-og-md\",\n \"animate-og-enter data-[state=closed]:opacity-0 data-[state=closed]:transition-opacity\",\n className,\n )}\n style={{ ...portalStyle, ...tooltipSurfaceStyle, ...style }}\n {...props}\n >\n {children}\n <TooltipPrimitive.Arrow\n className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]\"\n style={{\n backgroundColor: \"var(--og-color-fg)\",\n fill: \"var(--og-color-fg)\",\n }}\n />\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n );\n}\n\nexport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };\n","import { DownloadIcon, XIcon } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\nimport { Dialog } from \"radix-ui\";\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\n\n/* ----------------------------------------------------------------------------\n Screenshot lightbox\n\n A single, app-level lightbox the computer_call / view_image renderers open by\n `src`. Built on Radix Dialog so it is focus-trapped, ESC-closable, and\n scroll-locked — fixing the v1 mockup's broken expand (an absolutely-positioned\n <img> that overflowed its row). The image is centered, constrained to the\n viewport (`max-w/max-h` + `object-contain`), and sits on a dimmed backdrop.\n\n Consumers render `<LightboxProvider>` once near the timeline; renderers call\n `useLightbox().open(src)`.\n -------------------------------------------------------------------------- */\n\ntype LightboxController = {\n open: (\n src: string,\n caption?: string,\n source?: HTMLElement | null,\n label?: string,\n downloadFilename?: string,\n controlLabels?: LightboxControlLabels,\n releaseSource?: () => void,\n ) => void;\n};\n\nexport type LightboxControlLabels = {\n download?: string | undefined;\n close?: string | undefined;\n};\n\nconst LightboxContext = createContext<LightboxController | null>(null);\n\n/** Open the app-level screenshot lightbox. No-op outside a `LightboxProvider`. */\nexport function useLightbox(): LightboxController {\n return useContext(LightboxContext) ?? NOOP;\n}\n\n/**\n * The lightbox controller when one is mounted, or `null` outside a\n * `LightboxProvider`. Lets a media primitive degrade to a non-interactive image\n * (rather than a dead \"Expand\" button that announces an action it cannot do).\n */\nexport function useLightboxOptional(): LightboxController | null {\n return useContext(LightboxContext);\n}\n\nconst NOOP: LightboxController = { open: () => {} };\n\n/** @internal Owns one optional retained lightbox source and releases it exactly once. */\nexport function createLightboxSourceOwner() {\n let releaseCurrent: (() => void) | null = null;\n return {\n replace(release?: (() => void) | undefined) {\n const previous = releaseCurrent;\n releaseCurrent = release ?? null;\n previous?.();\n },\n release() {\n const release = releaseCurrent;\n releaseCurrent = null;\n release?.();\n },\n };\n}\n\n/**\n * The app-level screenshot lightbox. Render once near the timeline; renderers\n * call `useLightbox().open(src)`.\n *\n * Idempotent by design: when an ancestor `LightboxProvider` already exists (e.g.\n * a `MessageTimeline` mounted inside an app that already wraps its shell), this\n * one becomes a pass-through and does NOT mount a second focus-trapping Dialog.\n * That keeps `MessageTimeline` self-sufficient (it owns its own provider) while\n * composing cleanly when nested.\n */\nexport function LightboxProvider({ children }: { children: ReactNode }) {\n const ancestor = useContext(LightboxContext);\n if (ancestor) {\n return <>{children}</>;\n }\n return <LightboxRoot>{children}</LightboxRoot>;\n}\n\nfunction LightboxRoot({ children }: { children: ReactNode }) {\n const [state, setState] = useState<{\n src: string;\n caption?: string;\n label: string;\n downloadFilename?: string;\n controlLabels?: LightboxControlLabels;\n } | null>(null);\n const [tokenSource, setTokenSource] = useState<HTMLElement | null>(null);\n const sourceOwner = useMemo(() => createLightboxSourceOwner(), []);\n const portalStyle = usePortalTokenStyle(tokenSource);\n\n const releaseCurrentSource = useCallback(() => {\n sourceOwner.release();\n }, [sourceOwner]);\n\n useEffect(() => releaseCurrentSource, [releaseCurrentSource]);\n\n const open = useCallback(\n (\n src: string,\n caption?: string,\n source?: HTMLElement | null,\n label = \"Screenshot\",\n downloadFilename?: string,\n controlLabels?: LightboxControlLabels,\n releaseSource?: () => void,\n ) => {\n sourceOwner.replace(releaseSource);\n setTokenSource(source ?? null);\n setState({\n src,\n label,\n ...(caption ? { caption } : {}),\n ...(downloadFilename ? { downloadFilename } : {}),\n ...(controlLabels ? { controlLabels } : {}),\n });\n },\n [sourceOwner],\n );\n\n const close = useCallback(() => {\n releaseCurrentSource();\n setState(null);\n setTokenSource(null);\n }, [releaseCurrentSource]);\n\n const controller = useMemo<LightboxController>(() => ({ open }), [open]);\n\n return (\n <LightboxContext.Provider value={controller}>\n {children}\n <Dialog.Root open={state !== null} onOpenChange={(next) => !next && close()}>\n <AnimatePresence>\n {state !== null ? (\n <Dialog.Portal forceMount>\n <Dialog.Overlay asChild forceMount>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.15 }}\n className=\"og-root fixed inset-0 z-50 bg-black/90 backdrop-blur-md\"\n style={portalStyle}\n />\n </Dialog.Overlay>\n <Dialog.Content\n asChild\n forceMount\n aria-label={state.label}\n onOpenAutoFocus={(event) => event.preventDefault()}\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.98 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.98 }}\n transition={{ duration: 0.18, ease: [0.16, 1, 0.3, 1] }}\n // Content is a full-inset centering wrapper layered above the\n // Overlay, so Radix's own click-outside (which fires on the\n // Overlay) can never see a backdrop click — every click lands on\n // Content. We make Content's own backdrop dismiss: a click whose\n // target is the wrapper itself (not the figure) closes it, so the\n // figcaption's \"click outside to close\" affordance is real.\n onClick={(event) => {\n if (event.target === event.currentTarget) {\n close();\n }\n }}\n className=\"og-root fixed inset-0 z-50 flex items-center justify-center p-6 sm:p-12\"\n style={portalStyle}\n >\n <Dialog.Title className=\"sr-only\">{state.label}</Dialog.Title>\n <Dialog.Description className=\"sr-only\">\n {state.caption ?? `Expanded ${state.label.toLowerCase()}`}\n </Dialog.Description>\n {/* The figure is one self-contained object: image, caption, and\n its own close control. The close button anchors to a wrapper\n sized to the IMAGE (w-fit), so it hugs the real top-right\n corner regardless of aspect ratio — never floating into the\n empty space of a wide figure column. */}\n <figure className=\"m-0 flex max-h-full max-w-5xl flex-col items-center gap-3\">\n <div className=\"relative flex min-h-0 w-fit max-w-full\">\n {/* A plain <img>: this SDK is framework-agnostic, with no host Image component. */}\n <img\n src={state.src}\n alt={state.caption ?? state.label}\n className=\"min-h-0 max-h-[82vh] w-auto max-w-full rounded-og-md border border-white/10 object-contain shadow-og-lg\"\n />\n <ScreenshotLightboxControls\n src={state.src}\n downloadFilename={state.downloadFilename}\n controlLabels={state.controlLabels}\n />\n </div>\n {state.caption ? (\n <figcaption className=\"max-w-2xl text-center font-og-mono text-og-xs text-white/55\">\n {state.caption}\n </figcaption>\n ) : (\n <figcaption className=\"font-og-mono text-og-xs uppercase tracking-[0.1em] text-white/35\">\n Esc or click outside to close\n </figcaption>\n )}\n </figure>\n </motion.div>\n </Dialog.Content>\n </Dialog.Portal>\n ) : null}\n </AnimatePresence>\n </Dialog.Root>\n </LightboxContext.Provider>\n );\n}\n\n/** @internal Exact controls rendered by an open screenshot lightbox. */\nexport function ScreenshotLightboxControls({\n src,\n downloadFilename,\n controlLabels,\n}: {\n src: string;\n downloadFilename?: string | undefined;\n controlLabels?: LightboxControlLabels | undefined;\n}) {\n return (\n <div className=\"absolute -right-3 -top-3 flex items-center gap-2\">\n {downloadFilename ? (\n <a\n href={src}\n download={downloadFilename}\n className={cn(\n \"inline-flex size-9 items-center justify-center rounded-full\",\n \"border border-white/15 bg-black/60 text-white/70 backdrop-blur\",\n \"transition-colors hover:border-white/30 hover:text-white\",\n )}\n aria-label={controlLabels?.download ?? `Download ${downloadFilename}`}\n >\n <DownloadIcon className=\"size-4\" />\n </a>\n ) : null}\n <Dialog.Close\n className={cn(\n \"inline-flex size-9 items-center justify-center rounded-full\",\n \"border border-white/15 bg-black/60 text-white/70 backdrop-blur\",\n \"transition-colors hover:border-white/30 hover:text-white\",\n )}\n aria-label={controlLabels?.close ?? \"Close\"}\n >\n <XIcon className=\"size-4\" />\n </Dialog.Close>\n </div>\n );\n}\n"],"mappings":"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","names":["createContext","useContext","useMemo","useState","jsx","jsxs","createContext","useContext","useState","useMemo"]}