@aperture-browser/session-react 0.0.0-init.0 → 0.0.0-nightly.101.c63f90ec6164

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/index.js CHANGED
@@ -1,24 +1,28 @@
1
- import { a as makeApertureRuntime, c as collaborationPaintLifetimeMs, d as RuntimeProviderMissingError, f as useBaseUrl, g as useRuntime, h as useRunApi, i as ApertureProvider, l as useLiveSession, m as useFork, n as parseShareToken, o as useBrowserControl, p as useEffectCallback, r as useSharedSession, s as SessionViewport, t as LiveSessionError, u as RuntimeProvider } from "./headless-CKFacFX3.js";
1
+ import { a as makeApertureRuntime, c as collaborationPaintLifetimeMs, d as RuntimeProviderMissingError, f as useBaseUrl, g as useRuntime, h as useRunApi, i as ApertureProvider, l as useLiveSession, m as useFork, n as shareSessionAccess, o as useBrowserControl, p as useEffectCallback, r as useSession, s as SessionViewport, t as LiveSessionError, u as RuntimeProvider } from "./headless-0KVuQrnF.js";
2
2
  import { Fragment, createContext, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
3
3
  import * as Effect from "effect/Effect";
4
4
  import { VIEWPORT_DEVICE_SCALE_FACTORS, VIEWPORT_PRESETS, createViewportPreset, formatViewportScale } from "@aperture-browser/live-session";
5
+ import * as Option from "effect/Option";
6
+ import * as Schema from "effect/Schema";
5
7
  import * as Data from "effect/Data";
6
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
- import { ArrowLeft, ArrowRight, CheckIcon, ChevronRightIcon, Circle, CircleCheckIcon, Copy, Download, Gauge, Globe2, Info, InfoIcon, Link2Off, Loader2, Loader2Icon, Lock, LockOpen, Maximize2, Monitor, MoreVertical, MousePointer2, OctagonXIcon, PanelBottom, PanelRight, Pencil, Plus, Radio, RefreshCw, RotateCcw, Share2, Square, TriangleAlertIcon, Unplug, Wrench, X } from "lucide-react";
9
+ import { RecordingSettings } from "@aperture-browser/api-client";
10
+ import { ArrowLeft, ArrowRight, CheckIcon, ChevronRightIcon, Circle, CircleCheckIcon, Copy, Download, Eye, Gauge, Globe2, Hand, Info, InfoIcon, Link2Off, Loader2, Loader2Icon, Lock, LockOpen, Maximize2, Monitor, MonitorPause, MoreVertical, MousePointer2, OctagonXIcon, PanelBottom, PanelRight, Pencil, Plus, Radio, RefreshCw, RotateCcw, Share2, Square, TriangleAlertIcon, Unplug, Wrench, X } from "lucide-react";
8
11
  import { cva } from "class-variance-authority";
9
12
  import { clsx } from "clsx";
10
- import { twMerge } from "tailwind-merge";
13
+ import { extendTailwindMerge } from "tailwind-merge";
11
14
  import { useTheme } from "next-themes";
12
15
  import { Toaster, toast } from "sonner";
13
- import { Tooltip } from "@base-ui/react/tooltip";
14
16
  import * as ResizablePrimitive from "react-resizable-panels";
15
17
  import { usePanelRef } from "react-resizable-panels";
16
18
  import * as Schedule from "effect/Schedule";
17
19
  import { Button } from "@base-ui/react/button";
18
20
  import { ContextMenu } from "@base-ui/react/context-menu";
19
21
  import { Input } from "@base-ui/react/input";
22
+ import { Tooltip } from "@base-ui/react/tooltip";
20
23
  import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
21
24
  import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
25
+ import { PreviewCard } from "@base-ui/react/preview-card";
22
26
  import { ScrollArea } from "@base-ui/react/scroll-area";
23
27
  import "@base-ui/react/separator";
24
28
  import { Menu } from "@base-ui/react/menu";
@@ -27,6 +31,7 @@ import { Popover } from "@base-ui/react/popover";
27
31
  import { mergeProps } from "@base-ui/react/merge-props";
28
32
  import { useRender } from "@base-ui/react/use-render";
29
33
  //#region ../ui/src/utils.ts
34
+ const twMerge = extendTailwindMerge({ prefix: "aperture" });
30
35
  function cn(...inputs) {
31
36
  return twMerge(clsx(inputs));
32
37
  }
@@ -35,21 +40,21 @@ function cn(...inputs) {
35
40
  function Empty({ className, ...props }) {
36
41
  return /* @__PURE__ */ jsx("div", {
37
42
  "data-slot": "empty",
38
- className: cn("flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance", className),
43
+ className: cn("aperture:flex aperture:w-full aperture:min-w-0 aperture:flex-1 aperture:flex-col aperture:items-center aperture:justify-center aperture:gap-4 aperture:rounded-xl aperture:border-dashed aperture:p-6 aperture:text-center aperture:text-balance", className),
39
44
  ...props
40
45
  });
41
46
  }
42
47
  function EmptyHeader({ className, ...props }) {
43
48
  return /* @__PURE__ */ jsx("div", {
44
49
  "data-slot": "empty-header",
45
- className: cn("flex max-w-sm flex-col items-center gap-2", className),
50
+ className: cn("aperture:flex aperture:max-w-sm aperture:flex-col aperture:items-center aperture:gap-2", className),
46
51
  ...props
47
52
  });
48
53
  }
49
- const emptyMediaVariants = cva("mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0", {
54
+ const emptyMediaVariants = cva("aperture:mb-2 aperture:flex aperture:shrink-0 aperture:items-center aperture:justify-center aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0", {
50
55
  variants: { variant: {
51
- default: "bg-transparent",
52
- icon: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-4"
56
+ default: "aperture:bg-transparent",
57
+ icon: "aperture:flex aperture:size-8 aperture:shrink-0 aperture:items-center aperture:justify-center aperture:rounded-lg aperture:bg-muted aperture:text-foreground aperture:[&_svg:not([class*='size-'])]:size-4"
53
58
  } },
54
59
  defaultVariants: { variant: "default" }
55
60
  });
@@ -67,14 +72,14 @@ function EmptyMedia({ className, variant = "default", ...props }) {
67
72
  function EmptyTitle({ className, ...props }) {
68
73
  return /* @__PURE__ */ jsx("div", {
69
74
  "data-slot": "empty-title",
70
- className: cn("text-sm font-medium tracking-tight", className),
75
+ className: cn("aperture:text-sm aperture:font-medium aperture:tracking-tight", className),
71
76
  ...props
72
77
  });
73
78
  }
74
79
  function EmptyDescription({ className, ...props }) {
75
80
  return /* @__PURE__ */ jsx("div", {
76
81
  "data-slot": "empty-description",
77
- className: cn("text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", className),
82
+ className: cn("aperture:text-sm/relaxed aperture:text-muted-foreground aperture:[&>a]:underline aperture:[&>a]:underline-offset-4 aperture:[&>a:hover]:text-primary", className),
78
83
  ...props
79
84
  });
80
85
  }
@@ -84,13 +89,13 @@ const Toaster$1 = ({ ...props }) => {
84
89
  const { theme = "system" } = useTheme();
85
90
  return /* @__PURE__ */ jsx(Toaster, {
86
91
  theme,
87
- className: "toaster group",
92
+ className: "toaster aperture:group",
88
93
  icons: {
89
- success: /* @__PURE__ */ jsx(CircleCheckIcon, { className: "size-4" }),
90
- info: /* @__PURE__ */ jsx(InfoIcon, { className: "size-4" }),
91
- warning: /* @__PURE__ */ jsx(TriangleAlertIcon, { className: "size-4" }),
92
- error: /* @__PURE__ */ jsx(OctagonXIcon, { className: "size-4" }),
93
- loading: /* @__PURE__ */ jsx(Loader2Icon, { className: "size-4 animate-spin" })
94
+ success: /* @__PURE__ */ jsx(CircleCheckIcon, { className: "aperture:size-4" }),
95
+ info: /* @__PURE__ */ jsx(InfoIcon, { className: "aperture:size-4" }),
96
+ warning: /* @__PURE__ */ jsx(TriangleAlertIcon, { className: "aperture:size-4" }),
97
+ error: /* @__PURE__ */ jsx(OctagonXIcon, { className: "aperture:size-4" }),
98
+ loading: /* @__PURE__ */ jsx(Loader2Icon, { className: "aperture:size-4 aperture:animate-spin" })
94
99
  },
95
100
  style: {
96
101
  "--normal-bg": "var(--popover)",
@@ -103,57 +108,6 @@ const Toaster$1 = ({ ...props }) => {
103
108
  });
104
109
  };
105
110
  //#endregion
106
- //#region ../ui/src/portal.tsx
107
- const PortalContainerContext = createContext(null);
108
- function PortalContainerProvider({ container, children }) {
109
- return /* @__PURE__ */ jsx(PortalContainerContext.Provider, {
110
- value: container,
111
- children
112
- });
113
- }
114
- function usePortalContainer() {
115
- return useContext(PortalContainerContext) ?? void 0;
116
- }
117
- //#endregion
118
- //#region ../ui/src/components/tooltip.tsx
119
- function TooltipProvider({ delay = 0, ...props }) {
120
- return /* @__PURE__ */ jsx(Tooltip.Provider, {
121
- "data-slot": "tooltip-provider",
122
- delay,
123
- ...props
124
- });
125
- }
126
- function Tooltip$1({ ...props }) {
127
- return /* @__PURE__ */ jsx(Tooltip.Root, {
128
- "data-slot": "tooltip",
129
- ...props
130
- });
131
- }
132
- function TooltipTrigger({ ...props }) {
133
- return /* @__PURE__ */ jsx(Tooltip.Trigger, {
134
- "data-slot": "tooltip-trigger",
135
- ...props
136
- });
137
- }
138
- function TooltipContent({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
139
- return /* @__PURE__ */ jsx(Tooltip.Portal, {
140
- container: usePortalContainer(),
141
- children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
142
- align,
143
- alignOffset,
144
- side,
145
- sideOffset,
146
- className: "isolate z-50",
147
- children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
148
- "data-slot": "tooltip-content",
149
- className: cn("z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
150
- ...props,
151
- children: [children, /* @__PURE__ */ jsx(Tooltip.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" })]
152
- })
153
- })
154
- });
155
- }
156
- //#endregion
157
111
  //#region src/features.ts
158
112
  function resolveSessionFeatures(features) {
159
113
  return {
@@ -170,25 +124,25 @@ function resolveSessionFeatures(features) {
170
124
  }
171
125
  //#endregion
172
126
  //#region ../ui/src/components/button.tsx
173
- const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm leading-none font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
127
+ const buttonVariants = cva("aperture:group/button aperture:inline-flex aperture:shrink-0 aperture:items-center aperture:justify-center aperture:rounded-lg aperture:border aperture:border-transparent aperture:bg-clip-padding aperture:text-sm aperture:leading-none aperture:font-medium aperture:whitespace-nowrap aperture:transition-all aperture:outline-none aperture:select-none aperture:focus-visible:border-ring aperture:focus-visible:ring-3 aperture:focus-visible:ring-ring/50 aperture:active:not-aria-[haspopup]:translate-y-px aperture:disabled:pointer-events-none aperture:disabled:opacity-50 aperture:aria-invalid:border-destructive aperture:aria-invalid:ring-3 aperture:aria-invalid:ring-destructive/20 aperture:dark:aria-invalid:border-destructive/50 aperture:dark:aria-invalid:ring-destructive/40 aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0 aperture:[&_svg:not([class*='size-'])]:size-4", {
174
128
  variants: {
175
129
  variant: {
176
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
177
- outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
178
- secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
179
- ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
180
- destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
181
- link: "text-primary underline-offset-4 hover:underline"
130
+ default: "aperture:bg-primary aperture:text-primary-foreground aperture:hover:bg-primary/80",
131
+ outline: "aperture:border-border aperture:bg-background aperture:hover:bg-muted aperture:hover:text-foreground aperture:aria-expanded:bg-muted aperture:aria-expanded:text-foreground aperture:dark:border-input aperture:dark:bg-input/30 aperture:dark:hover:bg-input/50",
132
+ secondary: "aperture:bg-secondary aperture:text-secondary-foreground aperture:hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aperture:aria-expanded:bg-secondary aperture:aria-expanded:text-secondary-foreground",
133
+ ghost: "aperture:hover:bg-muted aperture:hover:text-foreground aperture:aria-expanded:bg-muted aperture:aria-expanded:text-foreground aperture:dark:hover:bg-muted/50",
134
+ destructive: "aperture:bg-destructive/10 aperture:text-destructive aperture:hover:bg-destructive/20 aperture:focus-visible:border-destructive/40 aperture:focus-visible:ring-destructive/20 aperture:dark:bg-destructive/20 aperture:dark:hover:bg-destructive/30 aperture:dark:focus-visible:ring-destructive/40",
135
+ link: "aperture:text-primary aperture:underline-offset-4 aperture:hover:underline"
182
136
  },
183
137
  size: {
184
- default: "h-8 gap-1.5 px-2.5 pt-px has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
185
- xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 pt-px text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
186
- sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 pt-px text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
187
- lg: "h-9 gap-1.5 px-2.5 pt-px has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
188
- icon: "size-8",
189
- "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
190
- "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
191
- "icon-lg": "size-9"
138
+ default: "aperture:h-8 aperture:gap-1.5 aperture:px-2.5 aperture:pt-px aperture:has-data-[icon=inline-end]:pr-2 aperture:has-data-[icon=inline-start]:pl-2",
139
+ xs: "aperture:h-6 aperture:gap-1 aperture:rounded-[min(--theme(--radius-md),10px)] aperture:px-2 aperture:pt-px aperture:text-xs aperture:in-data-[slot=button-group]:rounded-lg aperture:has-data-[icon=inline-end]:pr-1.5 aperture:has-data-[icon=inline-start]:pl-1.5 aperture:[&_svg:not([class*='size-'])]:size-3",
140
+ sm: "aperture:h-7 aperture:gap-1 aperture:rounded-[min(--theme(--radius-md),12px)] aperture:px-2.5 aperture:pt-px aperture:text-[0.8rem] aperture:in-data-[slot=button-group]:rounded-lg aperture:has-data-[icon=inline-end]:pr-1.5 aperture:has-data-[icon=inline-start]:pl-1.5 aperture:[&_svg:not([class*='size-'])]:size-3.5",
141
+ lg: "aperture:h-9 aperture:gap-1.5 aperture:px-2.5 aperture:pt-px aperture:has-data-[icon=inline-end]:pr-2 aperture:has-data-[icon=inline-start]:pl-2",
142
+ icon: "aperture:size-8",
143
+ "icon-xs": "aperture:size-6 aperture:rounded-[min(--theme(--radius-md),10px)] aperture:in-data-[slot=button-group]:rounded-lg aperture:[&_svg:not([class*='size-'])]:size-3",
144
+ "icon-sm": "aperture:size-7 aperture:rounded-[min(--theme(--radius-md),12px)] aperture:in-data-[slot=button-group]:rounded-lg",
145
+ "icon-lg": "aperture:size-9"
192
146
  }
193
147
  },
194
148
  defaultVariants: {
@@ -208,6 +162,18 @@ function Button$1({ className, variant = "default", size = "default", ...props }
208
162
  });
209
163
  }
210
164
  //#endregion
165
+ //#region ../ui/src/portal.tsx
166
+ const PortalContainerContext = createContext(void 0);
167
+ function PortalContainerProvider({ container, children }) {
168
+ return /* @__PURE__ */ jsx(PortalContainerContext.Provider, {
169
+ value: container,
170
+ children
171
+ });
172
+ }
173
+ function usePortalContainer() {
174
+ return useContext(PortalContainerContext);
175
+ }
176
+ //#endregion
211
177
  //#region ../ui/src/components/context-menu.tsx
212
178
  function ContextMenu$1({ ...props }) {
213
179
  return /* @__PURE__ */ jsx(ContextMenu.Root, {
@@ -218,7 +184,7 @@ function ContextMenu$1({ ...props }) {
218
184
  function ContextMenuTrigger({ className, ...props }) {
219
185
  return /* @__PURE__ */ jsx(ContextMenu.Trigger, {
220
186
  "data-slot": "context-menu-trigger",
221
- className: cn("select-none", className),
187
+ className: cn("aperture:select-none", className),
222
188
  ...props
223
189
  });
224
190
  }
@@ -226,14 +192,14 @@ function ContextMenuContent({ className, align = "start", alignOffset = 4, side
226
192
  return /* @__PURE__ */ jsx(ContextMenu.Portal, {
227
193
  container: usePortalContainer(),
228
194
  children: /* @__PURE__ */ jsx(ContextMenu.Positioner, {
229
- className: "isolate z-50 outline-none",
195
+ className: "aperture:isolate aperture:z-50 aperture:outline-none",
230
196
  align,
231
197
  alignOffset,
232
198
  side,
233
199
  sideOffset,
234
200
  children: /* @__PURE__ */ jsx(ContextMenu.Popup, {
235
201
  "data-slot": "context-menu-content",
236
- className: cn("z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
202
+ className: cn("aperture:z-50 aperture:max-h-(--available-height) aperture:min-w-36 aperture:origin-(--transform-origin) aperture:overflow-x-hidden aperture:overflow-y-auto aperture:rounded-lg aperture:bg-popover aperture:p-1 aperture:text-popover-foreground aperture:shadow-md aperture:ring-1 aperture:ring-foreground/10 aperture:duration-100 aperture:outline-none aperture:data-[side=bottom]:slide-in-from-top-2 aperture:data-[side=inline-end]:slide-in-from-left-2 aperture:data-[side=inline-start]:slide-in-from-right-2 aperture:data-[side=left]:slide-in-from-right-2 aperture:data-[side=right]:slide-in-from-left-2 aperture:data-[side=top]:slide-in-from-bottom-2 aperture:data-open:animate-in aperture:data-open:fade-in-0 aperture:data-open:zoom-in-95 aperture:data-closed:animate-out aperture:data-closed:fade-out-0 aperture:data-closed:zoom-out-95", className),
237
203
  ...props
238
204
  })
239
205
  })
@@ -249,7 +215,7 @@ function ContextMenuLabel({ className, inset, ...props }) {
249
215
  return /* @__PURE__ */ jsx(ContextMenu.GroupLabel, {
250
216
  "data-slot": "context-menu-label",
251
217
  "data-inset": inset,
252
- className: cn("px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7", className),
218
+ className: cn("aperture:px-1.5 aperture:py-1 aperture:text-xs aperture:font-medium aperture:text-muted-foreground aperture:data-inset:pl-7", className),
253
219
  ...props
254
220
  });
255
221
  }
@@ -258,7 +224,7 @@ function ContextMenuItem({ className, inset, variant = "default", ...props }) {
258
224
  "data-slot": "context-menu-item",
259
225
  "data-inset": inset,
260
226
  "data-variant": variant,
261
- className: cn("group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive", className),
227
+ className: cn("aperture:group/context-menu-item aperture:relative aperture:flex aperture:cursor-default aperture:items-center aperture:gap-1.5 aperture:rounded-md aperture:px-1.5 aperture:py-1 aperture:text-sm aperture:outline-hidden aperture:select-none aperture:focus:bg-accent aperture:focus:text-accent-foreground aperture:data-inset:pl-7 aperture:data-[variant=destructive]:text-destructive aperture:data-[variant=destructive]:focus:bg-destructive/10 aperture:data-[variant=destructive]:focus:text-destructive aperture:dark:data-[variant=destructive]:focus:bg-destructive/20 aperture:data-disabled:pointer-events-none aperture:data-disabled:opacity-50 aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0 aperture:[&_svg:not([class*='size-'])]:size-4 aperture:focus:*:[svg]:text-accent-foreground aperture:data-[variant=destructive]:*:[svg]:text-destructive", className),
262
228
  ...props
263
229
  });
264
230
  }
@@ -272,10 +238,10 @@ function ContextMenuRadioItem({ className, children, inset, ...props }) {
272
238
  return /* @__PURE__ */ jsxs(ContextMenu.RadioItem, {
273
239
  "data-slot": "context-menu-radio-item",
274
240
  "data-inset": inset,
275
- className: cn("relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
241
+ className: cn("aperture:relative aperture:flex aperture:cursor-default aperture:items-center aperture:gap-1.5 aperture:rounded-md aperture:py-1 aperture:pr-8 aperture:pl-1.5 aperture:text-sm aperture:outline-hidden aperture:select-none aperture:focus:bg-accent aperture:focus:text-accent-foreground aperture:data-inset:pl-7 aperture:data-disabled:pointer-events-none aperture:data-disabled:opacity-50 aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0 aperture:[&_svg:not([class*='size-'])]:size-4", className),
276
242
  ...props,
277
243
  children: [/* @__PURE__ */ jsx("span", {
278
- className: "pointer-events-none absolute right-2",
244
+ className: "aperture:pointer-events-none aperture:absolute aperture:right-2",
279
245
  children: /* @__PURE__ */ jsx(ContextMenu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, {}) })
280
246
  }), children]
281
247
  });
@@ -283,7 +249,7 @@ function ContextMenuRadioItem({ className, children, inset, ...props }) {
283
249
  function ContextMenuSeparator({ className, ...props }) {
284
250
  return /* @__PURE__ */ jsx(ContextMenu.Separator, {
285
251
  "data-slot": "context-menu-separator",
286
- className: cn("-mx-1 my-1 h-px bg-border", className),
252
+ className: cn("aperture:-mx-1 aperture:my-1 aperture:h-px aperture:bg-border", className),
287
253
  ...props
288
254
  });
289
255
  }
@@ -293,7 +259,7 @@ function Input$1({ className, type, ...props }) {
293
259
  return /* @__PURE__ */ jsx(Input, {
294
260
  type,
295
261
  "data-slot": "input",
296
- className: cn("h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className),
262
+ className: cn("aperture:h-8 aperture:w-full aperture:min-w-0 aperture:rounded-lg aperture:border aperture:border-input aperture:bg-transparent aperture:px-2.5 aperture:py-1 aperture:text-base aperture:transition-colors aperture:outline-none aperture:file:inline-flex aperture:file:h-6 aperture:file:border-0 aperture:file:bg-transparent aperture:file:text-sm aperture:file:font-medium aperture:file:text-foreground aperture:placeholder:text-muted-foreground aperture:focus-visible:border-ring aperture:focus-visible:ring-3 aperture:focus-visible:ring-ring/50 aperture:disabled:pointer-events-none aperture:disabled:cursor-not-allowed aperture:disabled:bg-input/50 aperture:disabled:opacity-50 aperture:aria-invalid:border-destructive aperture:aria-invalid:ring-3 aperture:aria-invalid:ring-destructive/20 aperture:md:text-sm aperture:dark:bg-input/30 aperture:dark:disabled:bg-input/80 aperture:dark:aria-invalid:border-destructive/50 aperture:dark:aria-invalid:ring-destructive/40", className),
297
263
  ...props
298
264
  });
299
265
  }
@@ -303,52 +269,125 @@ function InputGroup({ className, ...props }) {
303
269
  return /* @__PURE__ */ jsx("div", {
304
270
  "data-slot": "input-group",
305
271
  role: "group",
306
- className: cn("group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", className),
272
+ className: cn("aperture:group/input-group aperture:relative aperture:flex aperture:h-8 aperture:w-full aperture:min-w-0 aperture:items-center aperture:rounded-lg aperture:border aperture:border-input aperture:transition-colors aperture:outline-none aperture:in-data-[slot=combobox-content]:focus-within:border-inherit aperture:in-data-[slot=combobox-content]:focus-within:ring-0 aperture:has-disabled:bg-input/50 aperture:has-disabled:opacity-50 aperture:has-[[data-slot=input-group-control]:focus-visible]:border-ring aperture:has-[[data-slot=input-group-control]:focus-visible]:ring-3 aperture:has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 aperture:has-[[data-slot][aria-invalid=true]]:border-destructive aperture:has-[[data-slot][aria-invalid=true]]:ring-3 aperture:has-[[data-slot][aria-invalid=true]]:ring-destructive/20 aperture:has-[>[data-align=block-end]]:h-auto aperture:has-[>[data-align=block-end]]:flex-col aperture:has-[>[data-align=block-start]]:h-auto aperture:has-[>[data-align=block-start]]:flex-col aperture:has-[>textarea]:h-auto aperture:dark:bg-input/30 aperture:dark:has-disabled:bg-input/80 aperture:dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 aperture:has-[>[data-align=block-end]]:[&>input]:pt-3 aperture:has-[>[data-align=block-start]]:[&>input]:pb-3 aperture:has-[>[data-align=inline-end]]:[&>input]:pr-1.5 aperture:has-[>[data-align=inline-start]]:[&>input]:pl-1.5", className),
307
273
  ...props
308
274
  });
309
275
  }
310
- cva("flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4", {
276
+ cva("aperture:flex aperture:h-auto aperture:cursor-text aperture:items-center aperture:justify-center aperture:gap-2 aperture:py-1.5 aperture:text-sm aperture:font-medium aperture:text-muted-foreground aperture:select-none aperture:group-data-[disabled=true]/input-group:opacity-50 aperture:[&>kbd]:rounded-[calc(var(--radius)-5px)] aperture:[&>svg:not([class*='size-'])]:size-4", {
311
277
  variants: { align: {
312
- "inline-start": "order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]",
313
- "inline-end": "order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]",
314
- "block-start": "order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2",
315
- "block-end": "order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2"
278
+ "inline-start": "aperture:order-first aperture:pl-2 aperture:has-[>button]:ml-[-0.3rem] aperture:has-[>kbd]:ml-[-0.15rem]",
279
+ "inline-end": "aperture:order-last aperture:pr-2 aperture:has-[>button]:mr-[-0.3rem] aperture:has-[>kbd]:mr-[-0.15rem]",
280
+ "block-start": "aperture:order-first aperture:w-full aperture:justify-start aperture:px-2.5 aperture:pt-2 aperture:group-has-[>input]/input-group:pt-2 aperture:[[class~='aperture:border-b']]:pb-2",
281
+ "block-end": "aperture:order-last aperture:w-full aperture:justify-start aperture:px-2.5 aperture:pb-2 aperture:group-has-[>input]/input-group:pb-2 aperture:[[class~='aperture:border-t']]:pt-2"
316
282
  } },
317
283
  defaultVariants: { align: "inline-start" }
318
284
  });
319
- cva("flex items-center gap-2 text-sm shadow-none", {
285
+ cva("aperture:flex aperture:items-center aperture:gap-2 aperture:text-sm aperture:shadow-none", {
320
286
  variants: { size: {
321
- xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
287
+ xs: "aperture:h-6 aperture:gap-1 aperture:rounded-[calc(var(--radius)-3px)] aperture:px-1.5 aperture:[&>svg:not([class*='size-'])]:size-3.5",
322
288
  sm: "",
323
- "icon-xs": "size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0",
324
- "icon-sm": "size-8 p-0 has-[>svg]:p-0"
289
+ "icon-xs": "aperture:size-6 aperture:rounded-[calc(var(--radius)-3px)] aperture:p-0 aperture:has-[>svg]:p-0",
290
+ "icon-sm": "aperture:size-8 aperture:p-0 aperture:has-[>svg]:p-0"
325
291
  } },
326
292
  defaultVariants: { size: "xs" }
327
293
  });
328
294
  function InputGroupInput({ className, ...props }) {
329
295
  return /* @__PURE__ */ jsx(Input$1, {
330
296
  "data-slot": "input-group-control",
331
- className: cn("flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent", className),
297
+ className: cn("aperture:flex-1 aperture:rounded-none aperture:border-0 aperture:bg-transparent aperture:shadow-none aperture:ring-0 aperture:focus-visible:ring-0 aperture:disabled:bg-transparent aperture:aria-invalid:ring-0 aperture:dark:bg-transparent aperture:dark:disabled:bg-transparent", className),
332
298
  ...props
333
299
  });
334
300
  }
335
301
  //#endregion
302
+ //#region ../ui/src/components/tooltip.tsx
303
+ function TooltipProvider({ delay = 0, ...props }) {
304
+ return /* @__PURE__ */ jsx(Tooltip.Provider, {
305
+ "data-slot": "tooltip-provider",
306
+ delay,
307
+ ...props
308
+ });
309
+ }
310
+ function Tooltip$1({ ...props }) {
311
+ return /* @__PURE__ */ jsx(Tooltip.Root, {
312
+ "data-slot": "tooltip",
313
+ ...props
314
+ });
315
+ }
316
+ function TooltipTrigger({ ...props }) {
317
+ return /* @__PURE__ */ jsx(Tooltip.Trigger, {
318
+ "data-slot": "tooltip-trigger",
319
+ ...props
320
+ });
321
+ }
322
+ function TooltipContent({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
323
+ return /* @__PURE__ */ jsx(Tooltip.Portal, {
324
+ container: usePortalContainer(),
325
+ children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
326
+ align,
327
+ alignOffset,
328
+ side,
329
+ sideOffset,
330
+ className: "aperture:isolate aperture:z-50",
331
+ children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
332
+ "data-slot": "tooltip-content",
333
+ className: cn("aperture:z-50 aperture:inline-flex aperture:w-fit aperture:max-w-xs aperture:origin-(--transform-origin) aperture:items-center aperture:gap-1.5 aperture:rounded-md aperture:bg-foreground aperture:px-3 aperture:py-1.5 aperture:text-xs aperture:text-background aperture:has-data-[slot=kbd]:pr-1.5 aperture:data-[side=bottom]:slide-in-from-top-2 aperture:data-[side=inline-end]:slide-in-from-left-2 aperture:data-[side=inline-start]:slide-in-from-right-2 aperture:data-[side=left]:slide-in-from-right-2 aperture:data-[side=right]:slide-in-from-left-2 aperture:data-[side=top]:slide-in-from-bottom-2 aperture:**:data-[slot=kbd]:relative aperture:**:data-[slot=kbd]:isolate aperture:**:data-[slot=kbd]:z-50 aperture:**:data-[slot=kbd]:rounded-sm aperture:data-[state=delayed-open]:animate-in aperture:data-[state=delayed-open]:fade-in-0 aperture:data-[state=delayed-open]:zoom-in-95 aperture:data-open:animate-in aperture:data-open:fade-in-0 aperture:data-open:zoom-in-95 aperture:data-closed:animate-out aperture:data-closed:fade-out-0 aperture:data-closed:zoom-out-95", className),
334
+ ...props,
335
+ children: [children, /* @__PURE__ */ jsx(Tooltip.Arrow, { className: "aperture:z-50 aperture:size-2.5 aperture:translate-y-[calc(-50%-2px)] aperture:rotate-45 aperture:rounded-[2px] aperture:bg-foreground aperture:fill-foreground aperture:data-[side=bottom]:top-1 aperture:data-[side=inline-end]:top-1/2! aperture:data-[side=inline-end]:-left-1 aperture:data-[side=inline-end]:-translate-y-1/2 aperture:data-[side=inline-start]:top-1/2! aperture:data-[side=inline-start]:-right-1 aperture:data-[side=inline-start]:-translate-y-1/2 aperture:data-[side=left]:top-1/2! aperture:data-[side=left]:-right-1 aperture:data-[side=left]:-translate-y-1/2 aperture:data-[side=right]:top-1/2! aperture:data-[side=right]:-left-1 aperture:data-[side=right]:-translate-y-1/2 aperture:data-[side=top]:-bottom-2.5" })]
336
+ })
337
+ })
338
+ });
339
+ }
340
+ //#endregion
341
+ //#region ../ui/src/components/hover-card.tsx
342
+ function HoverCard(props) {
343
+ return /* @__PURE__ */ jsx(PreviewCard.Root, {
344
+ "data-slot": "hover-card",
345
+ ...props
346
+ });
347
+ }
348
+ function HoverCardTrigger(props) {
349
+ return /* @__PURE__ */ jsx(PreviewCard.Trigger, {
350
+ "data-slot": "hover-card-trigger",
351
+ ...props
352
+ });
353
+ }
354
+ function HoverCardContent({ className, side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, collisionPadding, ...props }) {
355
+ return /* @__PURE__ */ jsx(PreviewCard.Portal, {
356
+ container: usePortalContainer(),
357
+ children: /* @__PURE__ */ jsx(PreviewCard.Positioner, {
358
+ align,
359
+ alignOffset,
360
+ side,
361
+ sideOffset,
362
+ collisionPadding,
363
+ className: "aperture:isolate aperture:z-50",
364
+ children: /* @__PURE__ */ jsx(PreviewCard.Popup, {
365
+ "data-slot": "hover-card-content",
366
+ className: cn("aperture:z-50 aperture:w-64 aperture:rounded-lg aperture:bg-popover aperture:p-2.5 aperture:text-sm aperture:text-popover-foreground aperture:shadow-md aperture:ring-1 aperture:ring-foreground/10 aperture:outline-hidden", className),
367
+ ...props
368
+ })
369
+ })
370
+ });
371
+ }
372
+ const createHoverCardHandle = PreviewCard.createHandle;
373
+ //#endregion
336
374
  //#region ../ui/src/components/scroll-area.tsx
337
- function ScrollArea$1({ className, viewportClassName, scrollbars = "vertical", children, ...props }) {
375
+ function ScrollArea$1({ className, viewportClassName, viewportRef, scrollbars = "vertical", children, ...props }) {
338
376
  const vertical = scrollbars === "vertical" || scrollbars === "both";
339
377
  const horizontal = scrollbars === "horizontal" || scrollbars === "both";
340
378
  return /* @__PURE__ */ jsxs(ScrollArea.Root, {
341
379
  "data-slot": "scroll-area",
342
- className: cn("relative", className),
380
+ className: cn("aperture:relative", className),
343
381
  ...props,
344
382
  children: [
345
383
  /* @__PURE__ */ jsx(ScrollArea.Viewport, {
384
+ ref: viewportRef,
346
385
  "data-slot": "scroll-area-viewport",
347
386
  style: {
348
387
  overflowX: horizontal ? "scroll" : "hidden",
349
388
  overflowY: vertical ? "scroll" : "hidden"
350
389
  },
351
- className: cn("size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1", viewportClassName),
390
+ className: cn("aperture:size-full aperture:rounded-[inherit] aperture:transition-[color,box-shadow] aperture:outline-none aperture:focus-visible:ring-[3px] aperture:focus-visible:ring-ring/50 aperture:focus-visible:outline-1", viewportClassName),
352
391
  children
353
392
  }),
354
393
  vertical ? /* @__PURE__ */ jsx(ScrollBar, {}) : null,
@@ -362,11 +401,11 @@ function ScrollBar({ className, orientation = "vertical", ...props }) {
362
401
  "data-slot": "scroll-area-scrollbar",
363
402
  "data-orientation": orientation,
364
403
  orientation,
365
- className: cn("flex touch-none p-px transition-colors select-none hover:bg-muted/50 data-[orientation=horizontal]:h-2.5 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:border-t data-[orientation=horizontal]:border-t-transparent data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2.5 data-[orientation=vertical]:border-l data-[orientation=vertical]:border-l-transparent", className),
404
+ className: cn("aperture:flex aperture:touch-none aperture:p-px aperture:transition-colors aperture:select-none aperture:hover:bg-muted/50 aperture:data-[orientation=horizontal]:h-2.5 aperture:data-[orientation=horizontal]:flex-col aperture:data-[orientation=horizontal]:border-t aperture:data-[orientation=horizontal]:border-t-transparent aperture:data-[orientation=vertical]:h-full aperture:data-[orientation=vertical]:w-2.5 aperture:data-[orientation=vertical]:border-l aperture:data-[orientation=vertical]:border-l-transparent", className),
366
405
  ...props,
367
406
  children: /* @__PURE__ */ jsx(ScrollArea.Thumb, {
368
407
  "data-slot": "scroll-area-thumb",
369
- className: "relative flex-1 rounded-full bg-foreground/35 transition-colors hover:bg-foreground/55"
408
+ className: "aperture:relative aperture:flex-1 aperture:rounded-full aperture:bg-foreground/35 aperture:transition-colors aperture:hover:bg-foreground/55"
370
409
  })
371
410
  });
372
411
  }
@@ -402,19 +441,20 @@ const copyTextWithToast = (value, onCopied = () => {}) => copyText(value).pipe(E
402
441
  //#endregion
403
442
  //#region src/components/browser-tab-strip.tsx
404
443
  const BROWSER_TAB_DRAG_KIND = "browser-tab";
405
- function BrowserTabStrip({ targets, activeTargetId, recordingTargetIds, devToolsTargetIds, disabled, mutationDisabled, onActivate, onCreate, onDuplicate, onClose, onReload, onReorder }) {
444
+ function BrowserTabStrip({ targets, activeTargetId, recordingTargetIds, devToolsTargetIds, disabled, mutationDisabled, onActivate, onCreate, onDuplicate, onClose, onReload, onReorder, loadThumbnail = null }) {
445
+ const [previewHandle] = useState(() => createHoverCardHandle());
406
446
  if (targets.length === 0) return /* @__PURE__ */ jsxs("div", {
407
- className: "flex h-8 min-w-0 flex-1 items-center gap-2 px-2 text-xs text-muted-foreground",
447
+ className: "aperture:flex aperture:h-8 aperture:min-w-0 aperture:flex-1 aperture:items-center aperture:gap-2 aperture:px-2 aperture:text-xs aperture:text-muted-foreground",
408
448
  children: [/* @__PURE__ */ jsx("span", { children: "No tabs" }), /* @__PURE__ */ jsx(NewTabButton, {
409
449
  disabled: disabled || mutationDisabled,
410
450
  onCreate
411
451
  })]
412
452
  });
413
- return /* @__PURE__ */ jsx(ScrollArea$1, {
453
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ScrollArea$1, {
414
454
  scrollbars: "horizontal",
415
- className: "h-8 min-w-0 flex-1",
455
+ className: "aperture:h-8 aperture:min-w-0 aperture:flex-1",
416
456
  children: /* @__PURE__ */ jsxs("div", {
417
- className: "flex min-w-max items-end gap-0.5 px-1 pt-1",
457
+ className: "aperture:flex aperture:min-w-max aperture:items-end aperture:gap-0.5 aperture:px-1 aperture:pt-1",
418
458
  children: [targets.map((target, index) => {
419
459
  return /* @__PURE__ */ jsx(BrowserTab, {
420
460
  target,
@@ -428,6 +468,7 @@ function BrowserTabStrip({ targets, activeTargetId, recordingTargetIds, devTools
428
468
  onClose,
429
469
  onReload,
430
470
  onReorder,
471
+ previewHandle: loadThumbnail ? previewHandle : null,
431
472
  closeOtherTargetIds: targets.filter((current) => current.id !== target.id).map((current) => current.id),
432
473
  closeRightTargetIds: targets.slice(index + 1).map((current) => current.id)
433
474
  }, target.id);
@@ -436,9 +477,18 @@ function BrowserTabStrip({ targets, activeTargetId, recordingTargetIds, devTools
436
477
  onCreate
437
478
  })]
438
479
  })
439
- });
480
+ }), loadThumbnail ? /* @__PURE__ */ jsx(HoverCard, {
481
+ handle: previewHandle,
482
+ children: ({ payload }) => {
483
+ const target = targets.find((current) => current.id === payload);
484
+ return target ? /* @__PURE__ */ jsx(TabPreview, {
485
+ target,
486
+ loadThumbnail
487
+ }, target.id) : null;
488
+ }
489
+ }) : null] });
440
490
  }
441
- function BrowserTab({ target, active, recording, devToolsOpen, disabled, mutationDisabled, onActivate, onDuplicate, onClose, onReload, onReorder, closeOtherTargetIds, closeRightTargetIds }) {
491
+ function BrowserTab({ target, active, recording, devToolsOpen, disabled, mutationDisabled, onActivate, onDuplicate, onClose, onReload, onReorder, previewHandle, closeOtherTargetIds, closeRightTargetIds }) {
442
492
  const tabRef = useRef(null);
443
493
  const [dragging, setDragging] = useState(false);
444
494
  const [dropPlacement, setDropPlacement] = useState(null);
@@ -478,49 +528,55 @@ function BrowserTab({ target, active, recording, devToolsOpen, disabled, mutatio
478
528
  }
479
529
  }));
480
530
  }, [onReorder, target.id]);
481
- return /* @__PURE__ */ jsxs(ContextMenu$1, { children: [/* @__PURE__ */ jsxs(ContextMenuTrigger, {
482
- render: /* @__PURE__ */ jsx("div", {
483
- ref: tabRef,
484
- "data-browser-tab": true,
485
- className: cn("group relative flex h-7 w-52 max-w-[38vw] min-w-28 cursor-grab select-none items-center gap-1.5 rounded-t-lg border border-b-0 px-2 text-left text-xs transition-[background-color,border-color,color,opacity] active:cursor-grabbing", active ? "border-border bg-background text-foreground" : "border-transparent bg-muted/55 text-muted-foreground hover:bg-muted", dragging && "opacity-60"),
486
- title: target.url || "about:blank",
487
- onMouseDown: (event) => {
488
- if (event.button === 1) event.preventDefault();
489
- },
490
- onAuxClick: (event) => {
491
- if (event.button === 1 && !disabled && !mutationDisabled) {
492
- event.preventDefault();
493
- onClose(target.id);
494
- }
531
+ const tab = /* @__PURE__ */ jsx("div", {
532
+ ref: tabRef,
533
+ "data-browser-tab": true,
534
+ className: cn("aperture:group aperture:relative aperture:flex aperture:h-7 aperture:w-52 aperture:max-w-[38vw] aperture:min-w-28 aperture:cursor-grab aperture:select-none aperture:items-center aperture:gap-1.5 aperture:rounded-t-lg aperture:border aperture:border-b-0 aperture:px-2 aperture:text-left aperture:text-xs aperture:transition-[background-color,border-color,color,opacity] aperture:active:cursor-grabbing", active ? "aperture:border-border aperture:bg-background aperture:text-foreground" : "aperture:border-transparent aperture:bg-muted/55 aperture:text-muted-foreground aperture:hover:bg-muted", dragging && "aperture:opacity-60"),
535
+ onMouseDown: (event) => {
536
+ if (event.button === 1) event.preventDefault();
537
+ },
538
+ onAuxClick: (event) => {
539
+ if (event.button === 1 && !disabled && !mutationDisabled) {
540
+ event.preventDefault();
541
+ onClose(target.id);
495
542
  }
496
- }),
543
+ }
544
+ });
545
+ return /* @__PURE__ */ jsxs(ContextMenu$1, { children: [/* @__PURE__ */ jsxs(ContextMenuTrigger, {
546
+ render: previewHandle ? /* @__PURE__ */ jsx(HoverCardTrigger, {
547
+ handle: previewHandle,
548
+ payload: target.id,
549
+ delay: 0,
550
+ closeDelay: 0,
551
+ render: tab
552
+ }) : tab,
497
553
  children: [
498
- /* @__PURE__ */ jsx("span", { className: cn("pointer-events-none absolute inset-y-1 left-0 w-0.5 rounded-full bg-primary opacity-0", dropPlacement === "before" && "opacity-100") }),
499
- /* @__PURE__ */ jsx("span", { className: cn("pointer-events-none absolute inset-y-1 right-0 w-0.5 rounded-full bg-primary opacity-0", dropPlacement === "after" && "opacity-100") }),
554
+ /* @__PURE__ */ jsx("span", { className: cn("aperture:pointer-events-none aperture:absolute aperture:inset-y-1 aperture:left-0 aperture:w-0.5 aperture:rounded-full aperture:bg-primary aperture:opacity-0", dropPlacement === "before" && "aperture:opacity-100") }),
555
+ /* @__PURE__ */ jsx("span", { className: cn("aperture:pointer-events-none aperture:absolute aperture:inset-y-1 aperture:right-0 aperture:w-0.5 aperture:rounded-full aperture:bg-primary aperture:opacity-0", dropPlacement === "after" && "aperture:opacity-100") }),
500
556
  /* @__PURE__ */ jsxs("button", {
501
557
  type: "button",
502
558
  "aria-current": active ? "page" : void 0,
503
559
  disabled,
504
- className: "flex min-w-0 flex-1 items-center gap-1.5 text-left",
560
+ className: "aperture:flex aperture:min-w-0 aperture:flex-1 aperture:items-center aperture:gap-1.5 aperture:text-left",
505
561
  onClick: () => onActivate(target.id),
506
562
  children: [
507
563
  /* @__PURE__ */ jsx(TabFavicon, { url: target.url }),
508
564
  devToolsOpen ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Wrench, {
509
565
  "aria-hidden": true,
510
- className: "size-3.5 shrink-0 text-muted-foreground"
566
+ className: "aperture:size-3.5 aperture:shrink-0 aperture:text-muted-foreground"
511
567
  }), /* @__PURE__ */ jsx("span", {
512
- className: "sr-only",
568
+ className: "aperture:sr-only",
513
569
  children: "DevTools open"
514
570
  })] }) : null,
515
571
  recording ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
516
572
  "aria-hidden": true,
517
- className: "size-2 shrink-0 rounded-full bg-destructive"
573
+ className: "aperture:size-2 aperture:shrink-0 aperture:rounded-full aperture:bg-destructive"
518
574
  }), /* @__PURE__ */ jsx("span", {
519
- className: "sr-only",
575
+ className: "aperture:sr-only",
520
576
  children: "Recording"
521
577
  })] }) : null,
522
578
  /* @__PURE__ */ jsx("span", {
523
- className: "min-w-0 truncate font-mono",
579
+ className: "aperture:min-w-0 aperture:truncate aperture:font-mono",
524
580
  children: label
525
581
  })
526
582
  ]
@@ -530,7 +586,7 @@ function BrowserTab({ target, active, recording, devToolsOpen, disabled, mutatio
530
586
  type: "button",
531
587
  variant: "ghost",
532
588
  size: "icon-xs",
533
- className: "shrink-0 opacity-55 hover:opacity-100 group-hover:opacity-100",
589
+ className: "aperture:shrink-0 aperture:opacity-55 aperture:hover:opacity-100 aperture:group-hover:opacity-100",
534
590
  "aria-label": "Close tab",
535
591
  disabled: disabled || mutationDisabled,
536
592
  onClick: () => onClose(target.id)
@@ -542,7 +598,7 @@ function BrowserTab({ target, active, recording, devToolsOpen, disabled, mutatio
542
598
  })] })
543
599
  ]
544
600
  }), /* @__PURE__ */ jsxs(ContextMenuContent, {
545
- className: "min-w-48",
601
+ className: "aperture:min-w-48",
546
602
  children: [
547
603
  /* @__PURE__ */ jsxs(ContextMenuGroup, { children: [
548
604
  /* @__PURE__ */ jsx(ContextMenuItem, {
@@ -591,7 +647,7 @@ function NewTabButton({ disabled, onCreate }) {
591
647
  type: "button",
592
648
  variant: "ghost",
593
649
  size: "icon-xs",
594
- className: "mb-px shrink-0",
650
+ className: "aperture:mb-px aperture:shrink-0",
595
651
  "aria-label": "New tab",
596
652
  disabled,
597
653
  onClick: onCreate
@@ -608,11 +664,11 @@ function TabFavicon({ url }) {
608
664
  useEffect(() => {
609
665
  setFailed(false);
610
666
  }, [faviconUrl]);
611
- if (!faviconUrl || failed) return /* @__PURE__ */ jsx(Globe2, { className: "size-4 shrink-0 text-muted-foreground" });
667
+ if (!faviconUrl || failed) return /* @__PURE__ */ jsx(Globe2, { className: "aperture:size-4 aperture:shrink-0 aperture:text-muted-foreground" });
612
668
  return /* @__PURE__ */ jsx("img", {
613
669
  src: faviconUrl,
614
670
  alt: "",
615
- className: "size-4 shrink-0",
671
+ className: "aperture:size-4 aperture:shrink-0",
616
672
  onError: () => setFailed(true)
617
673
  });
618
674
  }
@@ -641,12 +697,53 @@ function dropPlacementFromClientX(element, clientX) {
641
697
  const rect = element.getBoundingClientRect();
642
698
  return clientX < rect.left + rect.width / 2 ? "before" : "after";
643
699
  }
700
+ function TabPreview({ target, loadThumbnail }) {
701
+ return /* @__PURE__ */ jsxs(HoverCardContent, {
702
+ "data-browser-tab-preview": true,
703
+ align: "start",
704
+ sideOffset: 8,
705
+ collisionPadding: 16,
706
+ className: "aperture:pointer-events-none aperture:flex aperture:w-80 aperture:max-w-[calc(100vw-2rem)] aperture:flex-col aperture:gap-2 aperture:p-2",
707
+ children: [
708
+ /* @__PURE__ */ jsx(TabThumbnail, {
709
+ targetId: target.id,
710
+ loadThumbnail
711
+ }),
712
+ /* @__PURE__ */ jsx("span", {
713
+ className: "aperture:truncate aperture:text-xs aperture:font-medium",
714
+ children: target.title || simplifyUrl(target.url)
715
+ }),
716
+ /* @__PURE__ */ jsx("span", {
717
+ className: "aperture:truncate aperture:font-mono aperture:text-xs aperture:text-muted-foreground",
718
+ children: target.url || "about:blank"
719
+ })
720
+ ]
721
+ });
722
+ }
723
+ function TabThumbnail({ targetId, loadThumbnail }) {
724
+ const [src, setSrc] = useState(null);
725
+ useFork(() => Effect.gen(function* () {
726
+ const blob = yield* loadThumbnail(targetId);
727
+ const objectUrl = URL.createObjectURL(blob);
728
+ yield* Effect.addFinalizer(() => Effect.sync(() => URL.revokeObjectURL(objectUrl)));
729
+ setSrc(objectUrl);
730
+ return yield* Effect.never;
731
+ }).pipe(Effect.catch(() => Effect.void), Effect.scoped), [loadThumbnail, targetId]);
732
+ return /* @__PURE__ */ jsx("div", {
733
+ className: "aperture:aspect-video aperture:w-full aperture:overflow-hidden aperture:rounded-md aperture:bg-muted",
734
+ children: src ? /* @__PURE__ */ jsx("img", {
735
+ src,
736
+ alt: "",
737
+ className: "aperture:size-full aperture:object-cover aperture:object-top"
738
+ }) : null
739
+ });
740
+ }
644
741
  //#endregion
645
742
  //#region ../ui/src/components/label.tsx
646
743
  function Label({ className, ...props }) {
647
744
  return /* @__PURE__ */ jsx("label", {
648
745
  "data-slot": "label",
649
- className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className),
746
+ className: cn("aperture:flex aperture:items-center aperture:gap-2 aperture:text-sm aperture:leading-none aperture:font-medium aperture:select-none aperture:group-data-[disabled=true]:pointer-events-none aperture:group-data-[disabled=true]:opacity-50 aperture:peer-disabled:cursor-not-allowed aperture:peer-disabled:opacity-50", className),
650
747
  ...props
651
748
  });
652
749
  }
@@ -655,15 +752,15 @@ function Label({ className, ...props }) {
655
752
  function FieldGroup({ className, ...props }) {
656
753
  return /* @__PURE__ */ jsx("div", {
657
754
  "data-slot": "field-group",
658
- className: cn("group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4", className),
755
+ className: cn("aperture:group/field-group aperture:@container/field-group aperture:flex aperture:w-full aperture:flex-col aperture:gap-5 aperture:data-[slot=checkbox-group]:gap-3 aperture:*:data-[slot=field-group]:gap-4", className),
659
756
  ...props
660
757
  });
661
758
  }
662
- const fieldVariants = cva("group/field flex w-full gap-2 data-[invalid=true]:text-destructive", {
759
+ const fieldVariants = cva("aperture:group/field aperture:flex aperture:w-full aperture:gap-2 aperture:data-[invalid=true]:text-destructive", {
663
760
  variants: { orientation: {
664
- vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
665
- horizontal: "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
666
- responsive: "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
761
+ vertical: "aperture:flex-col aperture:*:w-full aperture:[&>.sr-only]:w-auto",
762
+ horizontal: "aperture:flex-row aperture:items-center aperture:has-[>[data-slot=field-content]]:items-start aperture:*:data-[slot=field-label]:flex-auto aperture:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
763
+ responsive: "aperture:flex-col aperture:*:w-full aperture:@md/field-group:flex-row aperture:@md/field-group:items-center aperture:@md/field-group:*:w-auto aperture:@md/field-group:has-[>[data-slot=field-content]]:items-start aperture:@md/field-group:*:data-[slot=field-label]:flex-auto aperture:[&>.sr-only]:w-auto aperture:@md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
667
764
  } },
668
765
  defaultVariants: { orientation: "vertical" }
669
766
  });
@@ -679,7 +776,7 @@ function Field({ className, orientation = "vertical", ...props }) {
679
776
  function FieldLabel({ className, ...props }) {
680
777
  return /* @__PURE__ */ jsx(Label, {
681
778
  "data-slot": "field-label",
682
- className: cn("group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10", "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col", className),
779
+ className: cn("aperture:group/field-label aperture:peer/field-label aperture:flex aperture:w-fit aperture:gap-2 aperture:leading-snug aperture:group-data-[disabled=true]/field:opacity-50 aperture:has-data-checked:border-primary/30 aperture:has-data-checked:bg-primary/5 aperture:has-[>[data-slot=field]]:rounded-lg aperture:has-[>[data-slot=field]]:border aperture:*:data-[slot=field]:p-2.5 aperture:dark:has-data-checked:border-primary/20 aperture:dark:has-data-checked:bg-primary/10", "aperture:has-[>[data-slot=field]]:w-full aperture:has-[>[data-slot=field]]:flex-col", className),
683
780
  ...props
684
781
  });
685
782
  }
@@ -701,14 +798,14 @@ function DropdownMenuContent({ align = "start", alignOffset = 0, side = "bottom"
701
798
  return /* @__PURE__ */ jsx(Menu.Portal, {
702
799
  container: usePortalContainer(),
703
800
  children: /* @__PURE__ */ jsx(Menu.Positioner, {
704
- className: "isolate z-50 outline-none",
801
+ className: "aperture:isolate aperture:z-50 aperture:outline-none",
705
802
  align,
706
803
  alignOffset,
707
804
  side,
708
805
  sideOffset,
709
806
  children: /* @__PURE__ */ jsx(Menu.Popup, {
710
807
  "data-slot": "dropdown-menu-content",
711
- className: cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className),
808
+ className: cn("aperture:z-50 aperture:max-h-(--available-height) aperture:w-(--anchor-width) aperture:min-w-32 aperture:origin-(--transform-origin) aperture:overflow-x-hidden aperture:overflow-y-auto aperture:rounded-lg aperture:bg-popover aperture:p-1 aperture:text-popover-foreground aperture:shadow-md aperture:ring-1 aperture:ring-foreground/10 aperture:duration-100 aperture:outline-none aperture:data-[side=bottom]:slide-in-from-top-2 aperture:data-[side=inline-end]:slide-in-from-left-2 aperture:data-[side=inline-start]:slide-in-from-right-2 aperture:data-[side=left]:slide-in-from-right-2 aperture:data-[side=right]:slide-in-from-left-2 aperture:data-[side=top]:slide-in-from-bottom-2 aperture:data-open:animate-in aperture:data-open:fade-in-0 aperture:data-open:zoom-in-95 aperture:data-closed:animate-out aperture:data-closed:overflow-hidden aperture:data-closed:fade-out-0 aperture:data-closed:zoom-out-95", className),
712
809
  ...props
713
810
  })
714
811
  })
@@ -724,7 +821,7 @@ function DropdownMenuLabel({ className, inset, ...props }) {
724
821
  return /* @__PURE__ */ jsx("div", {
725
822
  "data-slot": "dropdown-menu-label",
726
823
  "data-inset": inset,
727
- className: cn("px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7", className),
824
+ className: cn("aperture:px-1.5 aperture:py-1 aperture:text-xs aperture:font-medium aperture:text-muted-foreground aperture:data-inset:pl-7", className),
728
825
  ...props
729
826
  });
730
827
  }
@@ -733,7 +830,7 @@ function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
733
830
  "data-slot": "dropdown-menu-item",
734
831
  "data-inset": inset,
735
832
  "data-variant": variant,
736
- className: cn("group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive", className),
833
+ className: cn("aperture:group/dropdown-menu-item aperture:relative aperture:flex aperture:cursor-default aperture:items-center aperture:gap-1.5 aperture:rounded-md aperture:px-1.5 aperture:py-1 aperture:text-sm aperture:outline-hidden aperture:select-none aperture:focus:bg-accent aperture:focus:text-accent-foreground aperture:not-data-[variant=destructive]:focus:**:text-accent-foreground aperture:data-inset:pl-7 aperture:data-[variant=destructive]:text-destructive aperture:data-[variant=destructive]:focus:bg-destructive/10 aperture:data-[variant=destructive]:focus:text-destructive aperture:dark:data-[variant=destructive]:focus:bg-destructive/20 aperture:data-disabled:pointer-events-none aperture:data-disabled:opacity-50 aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0 aperture:[&_svg:not([class*='size-'])]:size-4 aperture:data-[variant=destructive]:*:[svg]:text-destructive", className),
737
834
  ...props
738
835
  });
739
836
  }
@@ -747,15 +844,15 @@ function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
747
844
  return /* @__PURE__ */ jsxs(Menu.SubmenuTrigger, {
748
845
  "data-slot": "dropdown-menu-sub-trigger",
749
846
  "data-inset": inset,
750
- className: cn("flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
847
+ className: cn("aperture:flex aperture:cursor-default aperture:items-center aperture:gap-1.5 aperture:rounded-md aperture:px-1.5 aperture:py-1 aperture:text-sm aperture:outline-hidden aperture:select-none aperture:focus:bg-accent aperture:focus:text-accent-foreground aperture:not-data-[variant=destructive]:focus:**:text-accent-foreground aperture:data-inset:pl-7 aperture:data-popup-open:bg-accent aperture:data-popup-open:text-accent-foreground aperture:data-open:bg-accent aperture:data-open:text-accent-foreground aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0 aperture:[&_svg:not([class*='size-'])]:size-4", className),
751
848
  ...props,
752
- children: [children, /* @__PURE__ */ jsx(ChevronRightIcon, { className: "ml-auto" })]
849
+ children: [children, /* @__PURE__ */ jsx(ChevronRightIcon, { className: "aperture:ml-auto" })]
753
850
  });
754
851
  }
755
852
  function DropdownMenuSubContent({ align = "start", alignOffset = -3, side = "right", sideOffset = 0, className, ...props }) {
756
853
  return /* @__PURE__ */ jsx(DropdownMenuContent, {
757
854
  "data-slot": "dropdown-menu-sub-content",
758
- className: cn("w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
855
+ className: cn("aperture:w-auto aperture:min-w-[96px] aperture:rounded-lg aperture:bg-popover aperture:p-1 aperture:text-popover-foreground aperture:shadow-lg aperture:ring-1 aperture:ring-foreground/10 aperture:duration-100 aperture:data-[side=bottom]:slide-in-from-top-2 aperture:data-[side=left]:slide-in-from-right-2 aperture:data-[side=right]:slide-in-from-left-2 aperture:data-[side=top]:slide-in-from-bottom-2 aperture:data-open:animate-in aperture:data-open:fade-in-0 aperture:data-open:zoom-in-95 aperture:data-closed:animate-out aperture:data-closed:fade-out-0 aperture:data-closed:zoom-out-95", className),
759
856
  align,
760
857
  alignOffset,
761
858
  side,
@@ -767,11 +864,11 @@ function DropdownMenuCheckboxItem({ className, children, checked, inset, ...prop
767
864
  return /* @__PURE__ */ jsxs(Menu.CheckboxItem, {
768
865
  "data-slot": "dropdown-menu-checkbox-item",
769
866
  "data-inset": inset,
770
- className: cn("relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
867
+ className: cn("aperture:relative aperture:flex aperture:cursor-default aperture:items-center aperture:gap-1.5 aperture:rounded-md aperture:py-1 aperture:pr-8 aperture:pl-1.5 aperture:text-sm aperture:outline-hidden aperture:select-none aperture:focus:bg-accent aperture:focus:text-accent-foreground aperture:focus:**:text-accent-foreground aperture:data-inset:pl-7 aperture:data-disabled:pointer-events-none aperture:data-disabled:opacity-50 aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0 aperture:[&_svg:not([class*='size-'])]:size-4", className),
771
868
  checked,
772
869
  ...props,
773
870
  children: [/* @__PURE__ */ jsx("span", {
774
- className: "pointer-events-none absolute right-2 flex items-center justify-center",
871
+ className: "aperture:pointer-events-none aperture:absolute aperture:right-2 aperture:flex aperture:items-center aperture:justify-center",
775
872
  "data-slot": "dropdown-menu-checkbox-item-indicator",
776
873
  children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, {}) })
777
874
  }), children]
@@ -787,10 +884,10 @@ function DropdownMenuRadioItem({ className, children, inset, ...props }) {
787
884
  return /* @__PURE__ */ jsxs(Menu.RadioItem, {
788
885
  "data-slot": "dropdown-menu-radio-item",
789
886
  "data-inset": inset,
790
- className: cn("relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
887
+ className: cn("aperture:relative aperture:flex aperture:cursor-default aperture:items-center aperture:gap-1.5 aperture:rounded-md aperture:py-1 aperture:pr-8 aperture:pl-1.5 aperture:text-sm aperture:outline-hidden aperture:select-none aperture:focus:bg-accent aperture:focus:text-accent-foreground aperture:focus:**:text-accent-foreground aperture:data-inset:pl-7 aperture:data-disabled:pointer-events-none aperture:data-disabled:opacity-50 aperture:[&_svg]:pointer-events-none aperture:[&_svg]:shrink-0 aperture:[&_svg:not([class*='size-'])]:size-4", className),
791
888
  ...props,
792
889
  children: [/* @__PURE__ */ jsx("span", {
793
- className: "pointer-events-none absolute right-2 flex items-center justify-center",
890
+ className: "aperture:pointer-events-none aperture:absolute aperture:right-2 aperture:flex aperture:items-center aperture:justify-center",
794
891
  "data-slot": "dropdown-menu-radio-item-indicator",
795
892
  children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, {}) })
796
893
  }), children]
@@ -799,11 +896,191 @@ function DropdownMenuRadioItem({ className, children, inset, ...props }) {
799
896
  function DropdownMenuSeparator({ className, ...props }) {
800
897
  return /* @__PURE__ */ jsx(Menu.Separator, {
801
898
  "data-slot": "dropdown-menu-separator",
802
- className: cn("-mx-1 my-1 h-px bg-border", className),
899
+ className: cn("aperture:-mx-1 aperture:my-1 aperture:h-px aperture:bg-border", className),
803
900
  ...props
804
901
  });
805
902
  }
806
903
  //#endregion
904
+ //#region src/hooks/use-recording-settings.ts
905
+ const STORAGE_KEY = "aperture.workbench.recordingSettings";
906
+ const storedSettings = Schema.fromJsonString(RecordingSettings);
907
+ const decodeSettings = Schema.decodeUnknownOption(storedSettings);
908
+ const encodeSettings = Schema.encodeSync(storedSettings);
909
+ function useRecordingSettings() {
910
+ const [settings, setSettings] = useState({ capture: "continuous" });
911
+ useEffect(() => {
912
+ try {
913
+ const stored = window.localStorage.getItem(STORAGE_KEY);
914
+ if (stored !== null) {
915
+ const decoded = decodeSettings(stored);
916
+ if (Option.isSome(decoded)) setSettings(decoded.value);
917
+ }
918
+ } catch {}
919
+ }, []);
920
+ function updateSettings(next) {
921
+ const encoded = encodeSettings(next);
922
+ setSettings(next);
923
+ try {
924
+ window.localStorage.setItem(STORAGE_KEY, encoded);
925
+ } catch {}
926
+ }
927
+ return {
928
+ settings,
929
+ updateSettings
930
+ };
931
+ }
932
+ //#endregion
933
+ //#region src/components/recording-settings-menu.tsx
934
+ function RecordingSettingsMenuItems({ settings, onChange }) {
935
+ const bursts = settings.capture === "bursts";
936
+ const idleLabel = settings.idle === "cut" ? "Cut" : settings.idle === "speed" ? "Speed up 8×" : "Keep";
937
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
938
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "New recording" }),
939
+ /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsx(DropdownMenuSubTrigger, { children: /* @__PURE__ */ jsxs("span", {
940
+ className: "aperture:flex aperture:min-w-0 aperture:flex-1 aperture:items-center aperture:justify-between aperture:gap-3",
941
+ children: [/* @__PURE__ */ jsx("span", {
942
+ className: "aperture:shrink-0",
943
+ children: "Capture"
944
+ }), /* @__PURE__ */ jsx("span", {
945
+ className: "aperture:truncate aperture:text-muted-foreground",
946
+ children: bursts ? "Bursts" : "Continuous"
947
+ })]
948
+ }) }), /* @__PURE__ */ jsx(DropdownMenuSubContent, {
949
+ className: "aperture:w-64",
950
+ children: /* @__PURE__ */ jsx(DropdownMenuGroup, { children: /* @__PURE__ */ jsxs(DropdownMenuRadioGroup, {
951
+ "aria-label": "Tab capture",
952
+ value: settings.capture ?? "continuous",
953
+ onValueChange: (value) => {
954
+ const capture = Schema.decodeUnknownSync(RecordingSettings.fields.capture)(value);
955
+ const { idle: _idle, ...rest } = settings;
956
+ onChange(capture === "bursts" ? {
957
+ ...rest,
958
+ capture
959
+ } : {
960
+ ...settings,
961
+ capture
962
+ });
963
+ },
964
+ children: [/* @__PURE__ */ jsx(DropdownMenuRadioItem, {
965
+ value: "continuous",
966
+ closeOnClick: false,
967
+ children: /* @__PURE__ */ jsxs("span", {
968
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
969
+ children: [/* @__PURE__ */ jsx("span", { children: "Continuous" }), /* @__PURE__ */ jsx("span", {
970
+ className: "aperture:text-xs aperture:text-muted-foreground",
971
+ children: "Keep this tab, including manual actions."
972
+ })]
973
+ })
974
+ }), /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
975
+ value: "bursts",
976
+ closeOnClick: false,
977
+ children: /* @__PURE__ */ jsxs("span", {
978
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
979
+ children: [/* @__PURE__ */ jsx("span", { children: "Automation bursts" }), /* @__PURE__ */ jsx("span", {
980
+ className: "aperture:text-xs aperture:text-muted-foreground",
981
+ children: "Keep moments around automated actions and follow their tabs. Clicking or typing in this viewer does not trigger bursts."
982
+ })]
983
+ })
984
+ })]
985
+ }) })
986
+ })] }),
987
+ /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsx(DropdownMenuSubTrigger, { children: /* @__PURE__ */ jsxs("span", {
988
+ className: "aperture:flex aperture:min-w-0 aperture:flex-1 aperture:items-center aperture:justify-between aperture:gap-3",
989
+ children: [/* @__PURE__ */ jsx("span", {
990
+ className: "aperture:shrink-0",
991
+ children: "Quiet stretches"
992
+ }), /* @__PURE__ */ jsx("span", {
993
+ className: "aperture:truncate aperture:text-muted-foreground",
994
+ children: bursts ? "Continuous only" : idleLabel
995
+ })]
996
+ }) }), /* @__PURE__ */ jsx(DropdownMenuSubContent, {
997
+ className: "aperture:w-64",
998
+ children: /* @__PURE__ */ jsx(DropdownMenuGroup, { children: /* @__PURE__ */ jsxs(DropdownMenuRadioGroup, {
999
+ "aria-label": "Quiet stretches",
1000
+ value: settings.idle ?? "keep",
1001
+ onValueChange: (value) => {
1002
+ if (value === "keep") {
1003
+ const { idle: _idle, ...rest } = settings;
1004
+ onChange(rest);
1005
+ } else {
1006
+ const idle = Schema.decodeUnknownSync(RecordingSettings.fields.idle)(value);
1007
+ onChange({
1008
+ ...settings,
1009
+ idle
1010
+ });
1011
+ }
1012
+ },
1013
+ children: [
1014
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
1015
+ value: "keep",
1016
+ disabled: bursts,
1017
+ closeOnClick: false,
1018
+ children: /* @__PURE__ */ jsxs("span", {
1019
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1020
+ children: [/* @__PURE__ */ jsx("span", { children: "Keep" }), /* @__PURE__ */ jsx("span", {
1021
+ className: "aperture:text-xs aperture:text-muted-foreground",
1022
+ children: "Leave pauses at normal speed."
1023
+ })]
1024
+ })
1025
+ }),
1026
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
1027
+ value: "cut",
1028
+ disabled: bursts,
1029
+ closeOnClick: false,
1030
+ children: /* @__PURE__ */ jsxs("span", {
1031
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1032
+ children: [/* @__PURE__ */ jsx("span", { children: "Cut" }), /* @__PURE__ */ jsx("span", {
1033
+ className: "aperture:text-xs aperture:text-muted-foreground",
1034
+ children: "Remove quiet pauses, keeping brief context around activity."
1035
+ })]
1036
+ })
1037
+ }),
1038
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
1039
+ value: "speed",
1040
+ disabled: bursts,
1041
+ closeOnClick: false,
1042
+ children: /* @__PURE__ */ jsxs("span", {
1043
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1044
+ children: [/* @__PURE__ */ jsx("span", { children: "Speed up" }), /* @__PURE__ */ jsx("span", {
1045
+ className: "aperture:text-xs aperture:text-muted-foreground",
1046
+ children: "Play those pauses at 8× speed."
1047
+ })]
1048
+ })
1049
+ })
1050
+ ]
1051
+ }) })
1052
+ })] }),
1053
+ /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1054
+ render: /* @__PURE__ */ jsx(DropdownMenuCheckboxItem, {
1055
+ checked: settings.presentation ?? false,
1056
+ closeOnClick: false,
1057
+ onCheckedChange: (presentation) => onChange({
1058
+ ...settings,
1059
+ presentation
1060
+ })
1061
+ }),
1062
+ children: "Presentation pace"
1063
+ }), /* @__PURE__ */ jsx(TooltipContent, {
1064
+ side: "left",
1065
+ children: "Only while recording: slower pointer movement and longer pauses before clicks than Watchable automation. Manual input keeps its normal pace."
1066
+ })] }),
1067
+ /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1068
+ render: /* @__PURE__ */ jsx(DropdownMenuCheckboxItem, {
1069
+ checked: settings.ripple ?? false,
1070
+ closeOnClick: false,
1071
+ onCheckedChange: (ripple) => onChange({
1072
+ ...settings,
1073
+ ripple
1074
+ })
1075
+ }),
1076
+ children: "Highlight clicks"
1077
+ }), /* @__PURE__ */ jsx(TooltipContent, {
1078
+ side: "left",
1079
+ children: "Add a ripple around recorded clicks in the edited copy. The raw video stays unchanged."
1080
+ })] })
1081
+ ] })] });
1082
+ }
1083
+ //#endregion
807
1084
  //#region src/components/browser-toolbar-menus.tsx
808
1085
  const STREAM_PRESETS = [
809
1086
  {
@@ -859,10 +1136,26 @@ const VIEWPORT_LIMITS = {
859
1136
  }
860
1137
  };
861
1138
  function BrowserMenus({ control, cdpUrl, shareUrls, busy, connected, localCursorEnabled, onLocalCursorChange, onReconnect, onSessionDetails, now }) {
1139
+ const { settings, updateSettings } = useRecordingSettings();
862
1140
  const runningRecordings = control.recordings.filter((recording) => recording.status === "starting" || recording.status === "running");
863
1141
  const recordingActive = runningRecordings.length > 0;
1142
+ const viewportConnected = connected && control.collaboration.role !== "viewer";
1143
+ function startRecording(mode) {
1144
+ const { capture: _capture, burst, ...continuous } = settings;
1145
+ if (mode === "tab" && settings.capture === "bursts") {
1146
+ const { idle: _idle, ...bursts } = continuous;
1147
+ control.startRecording(mode, {
1148
+ ...bursts,
1149
+ capture: "bursts",
1150
+ ...burst === void 0 ? {} : { burst }
1151
+ });
1152
+ } else control.startRecording(mode, {
1153
+ ...continuous,
1154
+ capture: "continuous"
1155
+ });
1156
+ }
864
1157
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
865
- className: "shrink-0 sm:hidden",
1158
+ className: "aperture:shrink-0 aperture:sm:hidden",
866
1159
  children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
867
1160
  render: /* @__PURE__ */ jsx(DropdownMenuTrigger, { render: /* @__PURE__ */ jsx(Button$1, {
868
1161
  type: "button",
@@ -876,7 +1169,7 @@ function BrowserMenus({ control, cdpUrl, shareUrls, busy, connected, localCursor
876
1169
  children: recordingActive ? "Browser menu, recording active" : "Browser menu"
877
1170
  })] }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
878
1171
  align: "end",
879
- className: "w-72",
1172
+ className: "aperture:w-72",
880
1173
  children: [
881
1174
  /* @__PURE__ */ jsx(RestMenuItems, {
882
1175
  control,
@@ -887,14 +1180,17 @@ function BrowserMenus({ control, cdpUrl, shareUrls, busy, connected, localCursor
887
1180
  /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
888
1181
  /* @__PURE__ */ jsx(RecordingMenuItems, {
889
1182
  control,
890
- connected: connected && control.collaboration.role === "owner",
1183
+ connected,
891
1184
  runningRecordings,
892
- now
1185
+ now,
1186
+ settings,
1187
+ onSettingsChange: updateSettings,
1188
+ onStartRecording: startRecording
893
1189
  }),
894
1190
  /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
895
1191
  /* @__PURE__ */ jsx(ViewportStreamMenuItems, {
896
1192
  control,
897
- connected: connected && control.collaboration.role !== "viewer",
1193
+ connected: viewportConnected,
898
1194
  localCursorEnabled,
899
1195
  onLocalCursorChange,
900
1196
  busy,
@@ -903,7 +1199,7 @@ function BrowserMenus({ control, cdpUrl, shareUrls, busy, connected, localCursor
903
1199
  ]
904
1200
  })] })
905
1201
  }), /* @__PURE__ */ jsxs("div", {
906
- className: "hidden shrink-0 items-center gap-0.5 sm:flex",
1202
+ className: "aperture:hidden aperture:shrink-0 aperture:items-center aperture:gap-0.5 aperture:sm:flex",
907
1203
  children: [
908
1204
  /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
909
1205
  render: /* @__PURE__ */ jsx(DropdownMenuTrigger, { render: /* @__PURE__ */ jsx(Button$1, {
@@ -918,14 +1214,21 @@ function BrowserMenus({ control, cdpUrl, shareUrls, busy, connected, localCursor
918
1214
  children: recordingActive ? "Recording in progress" : "Recording"
919
1215
  })] }), /* @__PURE__ */ jsx(DropdownMenuContent, {
920
1216
  align: "end",
921
- className: "w-72",
1217
+ className: "aperture:w-72",
922
1218
  children: /* @__PURE__ */ jsx(RecordingMenuItems, {
923
1219
  control,
924
- connected: connected && control.collaboration.role === "owner",
1220
+ connected,
925
1221
  runningRecordings,
926
- now
1222
+ now,
1223
+ settings,
1224
+ onSettingsChange: updateSettings,
1225
+ onStartRecording: startRecording
927
1226
  })
928
1227
  })] }),
1228
+ /* @__PURE__ */ jsx(ViewportPausedIndicator, {
1229
+ control,
1230
+ connected: viewportConnected
1231
+ }),
929
1232
  /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
930
1233
  render: /* @__PURE__ */ jsx(DropdownMenuTrigger, { render: /* @__PURE__ */ jsx(Button$1, {
931
1234
  type: "button",
@@ -939,10 +1242,10 @@ function BrowserMenus({ control, cdpUrl, shareUrls, busy, connected, localCursor
939
1242
  children: "Viewport and stream"
940
1243
  })] }), /* @__PURE__ */ jsx(DropdownMenuContent, {
941
1244
  align: "end",
942
- className: "w-56",
1245
+ className: "aperture:w-56",
943
1246
  children: /* @__PURE__ */ jsx(ViewportStreamMenuItems, {
944
1247
  control,
945
- connected: connected && control.collaboration.role !== "viewer",
1248
+ connected: viewportConnected,
946
1249
  localCursorEnabled,
947
1250
  onLocalCursorChange,
948
1251
  busy,
@@ -962,7 +1265,7 @@ function BrowserMenus({ control, cdpUrl, shareUrls, busy, connected, localCursor
962
1265
  children: "More browser actions"
963
1266
  })] }), /* @__PURE__ */ jsx(DropdownMenuContent, {
964
1267
  align: "end",
965
- className: "w-48",
1268
+ className: "aperture:w-48",
966
1269
  children: /* @__PURE__ */ jsx(RestMenuItems, {
967
1270
  control,
968
1271
  cdpUrl,
@@ -1044,63 +1347,74 @@ function InputControlMenuItem({ control }) {
1044
1347
  children: [localControlClaimed ? /* @__PURE__ */ jsx(Lock, {}) : /* @__PURE__ */ jsx(LockOpen, {}), label]
1045
1348
  });
1046
1349
  }
1047
- function RecordingMenuItems({ control, connected, runningRecordings, now }) {
1048
- const recordingAvailable = connected && control.collaboration.role === "owner";
1350
+ function RecordingMenuItems({ control, connected, runningRecordings, now, settings, onSettingsChange, onStartRecording }) {
1351
+ const recordingAvailable = connected && control.canRecord;
1049
1352
  const canStart = recordingAvailable && Boolean(control.activeTargetId) && !control.recordingBusy;
1050
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
1051
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Recording" }),
1052
- /* @__PURE__ */ jsxs(DropdownMenuItem, {
1053
- disabled: !canStart,
1054
- onClick: () => control.startRecording("tab"),
1055
- children: [/* @__PURE__ */ jsx(Circle, {}), /* @__PURE__ */ jsxs("span", {
1056
- className: "flex min-w-0 flex-col",
1057
- children: [/* @__PURE__ */ jsx("span", { children: "Record this tab" }), /* @__PURE__ */ jsx("span", {
1058
- className: "text-xs text-muted-foreground",
1059
- children: "Stay pinned to this target"
1353
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1354
+ /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
1355
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Recording" }),
1356
+ /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
1357
+ render: /* @__PURE__ */ jsx(DropdownMenuItem, {
1358
+ disabled: !canStart,
1359
+ onClick: () => onStartRecording("tab")
1360
+ }),
1361
+ children: [/* @__PURE__ */ jsx(Circle, {}), "Start recording"]
1362
+ }), /* @__PURE__ */ jsx(TooltipContent, {
1363
+ side: "left",
1364
+ children: "Uses the capture mode below. The full raw video downloads when you stop. When editing is enabled, an edited copy appears in session files."
1365
+ })] }),
1366
+ /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
1367
+ render: /* @__PURE__ */ jsx(DropdownMenuItem, {
1368
+ disabled: !canStart,
1369
+ onClick: () => onStartRecording("viewer")
1370
+ }),
1371
+ children: [/* @__PURE__ */ jsx(Monitor, {}), recordingAvailable ? "Record this viewer" : /* @__PURE__ */ jsxs("span", {
1372
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1373
+ children: [/* @__PURE__ */ jsx("span", { children: "Record this viewer" }), /* @__PURE__ */ jsx("span", {
1374
+ className: "aperture:text-xs aperture:text-muted-foreground",
1375
+ children: "Connect as an owner or editor"
1376
+ })]
1060
1377
  })]
1061
- })]
1378
+ }), /* @__PURE__ */ jsx(TooltipContent, {
1379
+ side: "left",
1380
+ children: recordingAvailable ? "Continuous capture following your tab switches. Quiet stretches, presentation pace and click highlights use the settings below." : "Connect as an owner or editor to record."
1381
+ })] })
1382
+ ] }),
1383
+ runningRecordings.map((recording) => {
1384
+ const target = control.targets.find((candidate) => candidate.id === recording.targetId);
1385
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
1386
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: /* @__PURE__ */ jsxs("span", {
1387
+ className: "aperture:flex aperture:min-w-0 aperture:items-center aperture:justify-between aperture:gap-2",
1388
+ children: [/* @__PURE__ */ jsxs("span", {
1389
+ className: "aperture:truncate",
1390
+ children: [
1391
+ recording.mode === "viewer" ? "Viewer" : "Tab",
1392
+ ":",
1393
+ " ",
1394
+ target?.title || recording.targetId.slice(0, 8)
1395
+ ]
1396
+ }), /* @__PURE__ */ jsx("span", {
1397
+ className: "aperture:shrink-0 aperture:tabular-nums",
1398
+ children: formatElapsed(recording.startedAt, now)
1399
+ })]
1400
+ }) }),
1401
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
1402
+ disabled: control.recordingBusy,
1403
+ onClick: () => control.stopRecording(recording.recordingId),
1404
+ children: [/* @__PURE__ */ jsx(Download, {}), "Stop and download"]
1405
+ }),
1406
+ /* @__PURE__ */ jsxs(DropdownMenuItem, {
1407
+ disabled: control.recordingBusy,
1408
+ onClick: () => control.cancelRecording(recording.recordingId),
1409
+ children: [/* @__PURE__ */ jsx(X, {}), "Cancel without download"]
1410
+ })
1411
+ ] })] }, recording.recordingId);
1062
1412
  }),
1063
- /* @__PURE__ */ jsxs(DropdownMenuItem, {
1064
- disabled: !canStart,
1065
- onClick: () => control.startRecording("viewer"),
1066
- children: [/* @__PURE__ */ jsx(Monitor, {}), /* @__PURE__ */ jsxs("span", {
1067
- className: "flex min-w-0 flex-col",
1068
- children: [/* @__PURE__ */ jsx("span", { children: "Record this viewer" }), /* @__PURE__ */ jsx("span", {
1069
- className: "text-xs text-muted-foreground",
1070
- children: recordingAvailable ? "Follow tab switches" : "Owner connection required"
1071
- })]
1072
- })]
1413
+ /* @__PURE__ */ jsx(RecordingSettingsMenuItems, {
1414
+ settings,
1415
+ onChange: onSettingsChange
1073
1416
  })
1074
- ] }), runningRecordings.map((recording) => {
1075
- const target = control.targets.find((candidate) => candidate.id === recording.targetId);
1076
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
1077
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: /* @__PURE__ */ jsxs("span", {
1078
- className: "flex min-w-0 items-center justify-between gap-2",
1079
- children: [/* @__PURE__ */ jsxs("span", {
1080
- className: "truncate",
1081
- children: [
1082
- recording.mode === "viewer" ? "Viewer" : "Tab",
1083
- ":",
1084
- " ",
1085
- target?.title || recording.targetId.slice(0, 8)
1086
- ]
1087
- }), /* @__PURE__ */ jsx("span", {
1088
- className: "shrink-0 tabular-nums",
1089
- children: formatElapsed(recording.startedAt, now)
1090
- })]
1091
- }) }),
1092
- /* @__PURE__ */ jsxs(DropdownMenuItem, {
1093
- disabled: control.recordingBusy,
1094
- onClick: () => control.stopRecording(recording.recordingId),
1095
- children: [/* @__PURE__ */ jsx(Download, {}), "Stop and download"]
1096
- }),
1097
- /* @__PURE__ */ jsxs(DropdownMenuItem, {
1098
- disabled: control.recordingBusy,
1099
- onClick: () => control.cancelRecording(recording.recordingId),
1100
- children: [/* @__PURE__ */ jsx(X, {}), "Cancel without download"]
1101
- })
1102
- ] })] }, recording.recordingId);
1103
- })] });
1417
+ ] });
1104
1418
  }
1105
1419
  function ViewportStreamMenuItems({ control, connected, localCursorEnabled, onLocalCursorChange, busy, onReconnect }) {
1106
1420
  return /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
@@ -1118,6 +1432,12 @@ function ViewportStreamMenuItems({ control, connected, localCursorEnabled, onLoc
1118
1432
  onCheckedChange: control.setRemoteCursorEnabled,
1119
1433
  children: [/* @__PURE__ */ jsx(MousePointer2, {}), "Remote cursor"]
1120
1434
  }),
1435
+ /* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
1436
+ disabled: !connected || !control.canRecord,
1437
+ checked: control.watchableAutomation,
1438
+ onCheckedChange: control.setWatchableAutomation,
1439
+ children: [/* @__PURE__ */ jsx(Eye, {}), "Watchable automation"]
1440
+ }),
1121
1441
  /* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
1122
1442
  checked: localCursorEnabled,
1123
1443
  onCheckedChange: onLocalCursorChange,
@@ -1135,7 +1455,7 @@ function StreamMenu({ control, qualityUpdatesEnabled }) {
1135
1455
  const source = control.mediaPath === "websocket-live" ? "jpeg" : qualityUpdatesEnabled ? settings?.profile : "webrtc";
1136
1456
  const disabled = control.mediaSwitching || control.phase !== "connected";
1137
1457
  return /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [/* @__PURE__ */ jsx(Gauge, {}), "Stream"] }), /* @__PURE__ */ jsx(DropdownMenuSubContent, {
1138
- className: "w-64",
1458
+ className: "aperture:w-64",
1139
1459
  children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [
1140
1460
  /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Source" }),
1141
1461
  /* @__PURE__ */ jsxs(DropdownMenuRadioGroup, {
@@ -1163,9 +1483,9 @@ function StreamMenu({ control, qualityUpdatesEnabled }) {
1163
1483
  value: "webrtc",
1164
1484
  disabled,
1165
1485
  children: /* @__PURE__ */ jsxs("span", {
1166
- className: "flex min-w-0 flex-col",
1486
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1167
1487
  children: [/* @__PURE__ */ jsx("span", { children: "WebRTC" }), /* @__PURE__ */ jsx("span", {
1168
- className: "text-xs text-muted-foreground",
1488
+ className: "aperture:text-xs aperture:text-muted-foreground",
1169
1489
  children: "Live video"
1170
1490
  })]
1171
1491
  })
@@ -1174,9 +1494,9 @@ function StreamMenu({ control, qualityUpdatesEnabled }) {
1174
1494
  value: profile.id,
1175
1495
  disabled: disabled || !settings,
1176
1496
  children: /* @__PURE__ */ jsxs("span", {
1177
- className: "flex min-w-0 flex-col",
1497
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1178
1498
  children: [/* @__PURE__ */ jsx("span", { children: profile.label }), /* @__PURE__ */ jsxs("span", {
1179
- className: "text-xs text-muted-foreground",
1499
+ className: "aperture:text-xs aperture:text-muted-foreground",
1180
1500
  children: [profile.codec, " · WebRTC"]
1181
1501
  })]
1182
1502
  })
@@ -1185,9 +1505,9 @@ function StreamMenu({ control, qualityUpdatesEnabled }) {
1185
1505
  value: "jpeg",
1186
1506
  disabled,
1187
1507
  children: /* @__PURE__ */ jsxs("span", {
1188
- className: "flex min-w-0 flex-col",
1508
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1189
1509
  children: [/* @__PURE__ */ jsx("span", { children: "JPEG" }), /* @__PURE__ */ jsx("span", {
1190
- className: "text-xs text-muted-foreground",
1510
+ className: "aperture:text-xs aperture:text-muted-foreground",
1191
1511
  children: "WebSocket raster stream"
1192
1512
  })]
1193
1513
  })
@@ -1210,9 +1530,9 @@ function StreamMenu({ control, qualityUpdatesEnabled }) {
1210
1530
  value: preset.id,
1211
1531
  disabled,
1212
1532
  children: /* @__PURE__ */ jsxs("span", {
1213
- className: "flex min-w-0 flex-col",
1533
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1214
1534
  children: [/* @__PURE__ */ jsx("span", { children: preset.label }), /* @__PURE__ */ jsx("span", {
1215
- className: "text-xs text-muted-foreground",
1535
+ className: "aperture:text-xs aperture:text-muted-foreground",
1216
1536
  children: preset.detail
1217
1537
  })]
1218
1538
  })
@@ -1228,9 +1548,43 @@ function StreamMenu({ control, qualityUpdatesEnabled }) {
1228
1548
  ] })
1229
1549
  })] });
1230
1550
  }
1551
+ function viewportSizeController(control) {
1552
+ const ownership = control.viewportOwnership;
1553
+ const ownerClientId = ownership?.ownerClientId ?? null;
1554
+ if (ownership === null || ownerClientId === control.collaboration.clientId) return null;
1555
+ const ownerName = control.collaboration.participants.find((participant) => participant.clientId === ownerClientId)?.name ?? null;
1556
+ if (ownerName !== null) return `Size controlled by ${ownerName}`;
1557
+ return control.viewportAutoSync ? "Paused: size was set explicitly" : null;
1558
+ }
1559
+ function ViewportPausedIndicator({ control, connected }) {
1560
+ const sizeController = viewportSizeController(control);
1561
+ if (!control.viewportAutoSync || control.viewportAutoSizeActive || sizeController === null) return null;
1562
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1563
+ render: /* @__PURE__ */ jsx(DropdownMenuTrigger, { render: /* @__PURE__ */ jsx(Button$1, {
1564
+ type: "button",
1565
+ variant: "ghost",
1566
+ size: "icon-sm",
1567
+ className: "aperture:text-muted-foreground",
1568
+ "aria-label": `Auto-size paused. ${sizeController}`
1569
+ }) }),
1570
+ children: /* @__PURE__ */ jsx(MonitorPause, {})
1571
+ }), /* @__PURE__ */ jsx(TooltipContent, {
1572
+ side: "bottom",
1573
+ children: sizeController
1574
+ })] }), /* @__PURE__ */ jsx(DropdownMenuContent, {
1575
+ align: "end",
1576
+ className: "aperture:w-56",
1577
+ children: /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [/* @__PURE__ */ jsx(DropdownMenuLabel, { children: sizeController }), /* @__PURE__ */ jsxs(DropdownMenuItem, {
1578
+ disabled: !connected,
1579
+ onClick: () => control.takeOverViewport(),
1580
+ children: [/* @__PURE__ */ jsx(Hand, {}), "Take over size"]
1581
+ })] })
1582
+ })] });
1583
+ }
1231
1584
  function ViewportMenu({ control, connected }) {
1585
+ const sizeController = viewportSizeController(control);
1232
1586
  return /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [/* @__PURE__ */ jsx(Monitor, {}), "Viewport"] }), /* @__PURE__ */ jsxs(DropdownMenuSubContent, {
1233
- className: "w-64",
1587
+ className: "aperture:w-64",
1234
1588
  children: [
1235
1589
  /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Viewport" }),
1236
1590
  /* @__PURE__ */ jsxs(DropdownMenuItem, {
@@ -1242,7 +1596,23 @@ function ViewportMenu({ control, connected }) {
1242
1596
  disabled: !connected || !control.browserViewportSize,
1243
1597
  checked: control.viewportAutoSync,
1244
1598
  onCheckedChange: control.setViewportAutoSync,
1245
- children: [/* @__PURE__ */ jsx(Monitor, {}), "Auto sync browser size"]
1599
+ children: [/* @__PURE__ */ jsx(Monitor, {}), /* @__PURE__ */ jsxs("span", {
1600
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col",
1601
+ children: [/* @__PURE__ */ jsx("span", { children: "Auto-size" }), sizeController !== null ? /* @__PURE__ */ jsx("span", {
1602
+ className: "aperture:truncate aperture:text-xs aperture:text-muted-foreground",
1603
+ children: sizeController
1604
+ }) : null]
1605
+ })]
1606
+ }),
1607
+ sizeController !== null ? /* @__PURE__ */ jsxs(DropdownMenuItem, {
1608
+ disabled: !connected || !control.browserViewportSize,
1609
+ onClick: () => control.takeOverViewport(),
1610
+ children: [/* @__PURE__ */ jsx(Hand, {}), "Take over size"]
1611
+ }) : null,
1612
+ /* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
1613
+ checked: control.viewportAutoSizeDefault,
1614
+ onCheckedChange: control.setViewportAutoSizeDefault,
1615
+ children: [/* @__PURE__ */ jsx(Monitor, {}), "Auto-size by default"]
1246
1616
  }),
1247
1617
  /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
1248
1618
  /* @__PURE__ */ jsx(DropdownMenuRadioGroup, {
@@ -1300,16 +1670,16 @@ function CustomViewportSettings({ viewport, connected, onApply }) {
1300
1670
  });
1301
1671
  const unchanged = nextViewport ? viewport.width === nextViewport.width && viewport.height === nextViewport.height && viewport.deviceScaleFactor === nextViewport.deviceScaleFactor : false;
1302
1672
  return /* @__PURE__ */ jsxs("div", {
1303
- className: "grid gap-2 px-2 py-1.5",
1673
+ className: "aperture:grid aperture:gap-2 aperture:px-2 aperture:py-1.5",
1304
1674
  onClick: (event) => event.stopPropagation(),
1305
1675
  onKeyDown: (event) => event.stopPropagation(),
1306
1676
  children: [
1307
1677
  /* @__PURE__ */ jsx(DropdownMenuLabel, {
1308
- className: "px-0",
1678
+ className: "aperture:px-0",
1309
1679
  children: "Custom"
1310
1680
  }),
1311
1681
  /* @__PURE__ */ jsxs("div", {
1312
- className: "grid grid-cols-3 gap-2",
1682
+ className: "aperture:grid aperture:grid-cols-3 aperture:gap-2",
1313
1683
  children: [
1314
1684
  /* @__PURE__ */ jsx(StreamNumberField, {
1315
1685
  label: "Width",
@@ -1331,7 +1701,7 @@ function CustomViewportSettings({ viewport, connected, onApply }) {
1331
1701
  /* @__PURE__ */ jsx(Button$1, {
1332
1702
  type: "button",
1333
1703
  size: "sm",
1334
- className: "h-7",
1704
+ className: "aperture:h-7",
1335
1705
  disabled: !connected || !nextViewport || unchanged,
1336
1706
  onClick: () => {
1337
1707
  if (nextViewport) onApply(nextViewport);
@@ -1354,16 +1724,16 @@ function CustomStreamSettings({ settings, disabled, onApply }) {
1354
1724
  });
1355
1725
  const unchanged = settings.fps === nextSettings?.fps && settings.bitrateKbps === nextSettings?.bitrateKbps;
1356
1726
  return /* @__PURE__ */ jsxs(FieldGroup, {
1357
- className: "gap-2 px-2 py-1.5",
1727
+ className: "aperture:gap-2 aperture:px-2 aperture:py-1.5",
1358
1728
  onClick: (event) => event.stopPropagation(),
1359
1729
  onKeyDown: (event) => event.stopPropagation(),
1360
1730
  children: [
1361
1731
  /* @__PURE__ */ jsx(DropdownMenuLabel, {
1362
- className: "px-0",
1732
+ className: "aperture:px-0",
1363
1733
  children: "Custom"
1364
1734
  }),
1365
1735
  /* @__PURE__ */ jsxs("div", {
1366
- className: "grid grid-cols-2 gap-2",
1736
+ className: "aperture:grid aperture:grid-cols-2 aperture:gap-2",
1367
1737
  children: [/* @__PURE__ */ jsx(StreamNumberField, {
1368
1738
  label: "FPS",
1369
1739
  value: fps,
@@ -1377,7 +1747,7 @@ function CustomStreamSettings({ settings, disabled, onApply }) {
1377
1747
  /* @__PURE__ */ jsx(Button$1, {
1378
1748
  type: "button",
1379
1749
  size: "sm",
1380
- className: "h-7 w-full",
1750
+ className: "aperture:h-7 aperture:w-full",
1381
1751
  disabled: disabled || !nextSettings || unchanged,
1382
1752
  onClick: () => {
1383
1753
  if (nextSettings) onApply({
@@ -1393,7 +1763,7 @@ function CustomStreamSettings({ settings, disabled, onApply }) {
1393
1763
  function StreamNumberField({ label, value, onChange }) {
1394
1764
  const id = useId();
1395
1765
  return /* @__PURE__ */ jsxs(Field, {
1396
- className: "gap-1",
1766
+ className: "aperture:gap-1",
1397
1767
  children: [/* @__PURE__ */ jsx(FieldLabel, {
1398
1768
  htmlFor: id,
1399
1769
  children: label
@@ -1404,14 +1774,14 @@ function StreamNumberField({ label, value, onChange }) {
1404
1774
  value,
1405
1775
  onChange: (event) => onChange(digitsOnly(event.currentTarget.value)),
1406
1776
  onFocus: (event) => event.currentTarget.select(),
1407
- className: "h-7"
1777
+ className: "aperture:h-7"
1408
1778
  })]
1409
1779
  });
1410
1780
  }
1411
1781
  function ViewportScaleField({ label, value, onChange }) {
1412
1782
  const id = useId();
1413
1783
  return /* @__PURE__ */ jsxs(Field, {
1414
- className: "gap-1",
1784
+ className: "aperture:gap-1",
1415
1785
  children: [/* @__PURE__ */ jsx(FieldLabel, {
1416
1786
  htmlFor: id,
1417
1787
  children: label
@@ -1422,7 +1792,7 @@ function ViewportScaleField({ label, value, onChange }) {
1422
1792
  value,
1423
1793
  onChange: (event) => onChange(decimalNumber(event.currentTarget.value)),
1424
1794
  onFocus: (event) => event.currentTarget.select(),
1425
- className: "h-7"
1795
+ className: "aperture:h-7"
1426
1796
  })]
1427
1797
  });
1428
1798
  }
@@ -1481,28 +1851,28 @@ function Avatar$1({ className, size = "default", ...props }) {
1481
1851
  return /* @__PURE__ */ jsx(Avatar.Root, {
1482
1852
  "data-slot": "avatar",
1483
1853
  "data-size": size,
1484
- className: cn("group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten", className),
1854
+ className: cn("aperture:group/avatar aperture:relative aperture:flex aperture:size-8 aperture:shrink-0 aperture:rounded-full aperture:select-none aperture:after:absolute aperture:after:inset-0 aperture:after:rounded-full aperture:after:border aperture:after:border-border aperture:after:mix-blend-darken aperture:data-[size=lg]:size-10 aperture:data-[size=sm]:size-6 aperture:dark:after:mix-blend-lighten", className),
1485
1855
  ...props
1486
1856
  });
1487
1857
  }
1488
1858
  function AvatarImage({ className, ...props }) {
1489
1859
  return /* @__PURE__ */ jsx(Avatar.Image, {
1490
1860
  "data-slot": "avatar-image",
1491
- className: cn("aspect-square size-full rounded-full object-cover", className),
1861
+ className: cn("aperture:aspect-square aperture:size-full aperture:rounded-full aperture:object-cover", className),
1492
1862
  ...props
1493
1863
  });
1494
1864
  }
1495
1865
  function AvatarFallback({ className, ...props }) {
1496
1866
  return /* @__PURE__ */ jsx(Avatar.Fallback, {
1497
1867
  "data-slot": "avatar-fallback",
1498
- className: cn("flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs", className),
1868
+ className: cn("aperture:flex aperture:size-full aperture:items-center aperture:justify-center aperture:rounded-full aperture:bg-muted aperture:text-sm aperture:text-muted-foreground aperture:group-data-[size=sm]/avatar:text-xs", className),
1499
1869
  ...props
1500
1870
  });
1501
1871
  }
1502
1872
  function AvatarBadge({ className, ...props }) {
1503
1873
  return /* @__PURE__ */ jsx("span", {
1504
1874
  "data-slot": "avatar-badge",
1505
- className: cn("absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none", "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden", "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", className),
1875
+ className: cn("aperture:absolute aperture:right-0 aperture:bottom-0 aperture:z-10 aperture:inline-flex aperture:items-center aperture:justify-center aperture:rounded-full aperture:bg-primary aperture:text-primary-foreground aperture:bg-blend-color aperture:ring-2 aperture:ring-background aperture:select-none", "aperture:group-data-[size=sm]/avatar:size-2 aperture:group-data-[size=sm]/avatar:[&>svg]:hidden", "aperture:group-data-[size=default]/avatar:size-2.5 aperture:group-data-[size=default]/avatar:[&>svg]:size-2", "aperture:group-data-[size=lg]/avatar:size-3 aperture:group-data-[size=lg]/avatar:[&>svg]:size-2", className),
1506
1876
  ...props
1507
1877
  });
1508
1878
  }
@@ -1528,10 +1898,10 @@ function PopoverContent({ className, align = "center", alignOffset = 0, side = "
1528
1898
  alignOffset,
1529
1899
  side,
1530
1900
  sideOffset,
1531
- className: "isolate z-50",
1901
+ className: "aperture:isolate aperture:z-50",
1532
1902
  children: /* @__PURE__ */ jsx(Popover.Popup, {
1533
1903
  "data-slot": "popover-content",
1534
- className: cn("z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
1904
+ className: cn("aperture:z-50 aperture:flex aperture:w-72 aperture:origin-(--transform-origin) aperture:flex-col aperture:gap-2.5 aperture:rounded-lg aperture:bg-popover aperture:p-2.5 aperture:text-sm aperture:text-popover-foreground aperture:shadow-md aperture:ring-1 aperture:ring-foreground/10 aperture:outline-hidden aperture:duration-100 aperture:data-[side=bottom]:slide-in-from-top-2 aperture:data-[side=inline-end]:slide-in-from-left-2 aperture:data-[side=inline-start]:slide-in-from-right-2 aperture:data-[side=left]:slide-in-from-right-2 aperture:data-[side=right]:slide-in-from-left-2 aperture:data-[side=top]:slide-in-from-bottom-2 aperture:data-open:animate-in aperture:data-open:fade-in-0 aperture:data-open:zoom-in-95 aperture:data-closed:animate-out aperture:data-closed:fade-out-0 aperture:data-closed:zoom-out-95", className),
1535
1905
  ...props
1536
1906
  })
1537
1907
  })
@@ -1540,7 +1910,7 @@ function PopoverContent({ className, align = "center", alignOffset = 0, side = "
1540
1910
  function PopoverTitle({ className, ...props }) {
1541
1911
  return /* @__PURE__ */ jsx(Popover.Title, {
1542
1912
  "data-slot": "popover-title",
1543
- className: cn("font-medium", className),
1913
+ className: cn("aperture:font-medium", className),
1544
1914
  ...props
1545
1915
  });
1546
1916
  }
@@ -1558,10 +1928,10 @@ function CollaborationPresence({ collaboration }) {
1558
1928
  const overflow = participants.length - visible.length;
1559
1929
  if (visible.length === 0) return null;
1560
1930
  return /* @__PURE__ */ jsxs("div", {
1561
- className: "group flex shrink-0 items-center px-2",
1931
+ className: "aperture:group aperture:flex aperture:shrink-0 aperture:items-center aperture:px-2",
1562
1932
  "aria-label": "Session participants",
1563
1933
  children: [overflow > 0 ? /* @__PURE__ */ jsx("div", {
1564
- className: "relative transition-[margin] duration-200",
1934
+ className: "aperture:relative aperture:transition-[margin] aperture:duration-200",
1565
1935
  style: { zIndex: visible.length + 1 },
1566
1936
  children: /* @__PURE__ */ jsx(OverflowParticipants, {
1567
1937
  overflow,
@@ -1569,7 +1939,7 @@ function CollaborationPresence({ collaboration }) {
1569
1939
  collaboration
1570
1940
  })
1571
1941
  }) : null, visible.map((participant, index) => /* @__PURE__ */ jsx("div", {
1572
- className: cn("relative transition-[margin] duration-200", (index > 0 || overflow > 0) && "-ml-4 group-hover:ml-0 group-focus-within:ml-0"),
1942
+ className: cn("aperture:relative aperture:transition-[margin] aperture:duration-200", (index > 0 || overflow > 0) && "aperture:-ml-4 aperture:group-hover:ml-0 aperture:group-focus-within:ml-0"),
1573
1943
  style: { zIndex: visible.length - index },
1574
1944
  children: /* @__PURE__ */ jsx(ParticipantButton, {
1575
1945
  participant,
@@ -1584,18 +1954,18 @@ function OverflowParticipants({ overflow, participants, collaboration }) {
1584
1954
  type: "button",
1585
1955
  variant: "ghost",
1586
1956
  size: "icon-sm",
1587
- className: "shrink-0 rounded-full border bg-background text-[0.65rem] text-muted-foreground",
1957
+ className: "aperture:shrink-0 aperture:rounded-full aperture:border aperture:bg-background aperture:text-[0.65rem] aperture:text-muted-foreground",
1588
1958
  "aria-label": `${overflow} more participants`
1589
1959
  }),
1590
1960
  children: ["+", overflow]
1591
1961
  }), /* @__PURE__ */ jsxs(PopoverContent, {
1592
1962
  align: "end",
1593
- className: "w-auto gap-2",
1963
+ className: "aperture:w-auto aperture:gap-2",
1594
1964
  children: [/* @__PURE__ */ jsx(PopoverTitle, {
1595
- className: "text-xs text-muted-foreground",
1965
+ className: "aperture:text-xs aperture:text-muted-foreground",
1596
1966
  children: "Everyone"
1597
1967
  }), /* @__PURE__ */ jsx("div", {
1598
- className: "flex flex-wrap items-center gap-1",
1968
+ className: "aperture:flex aperture:flex-wrap aperture:items-center aperture:gap-1",
1599
1969
  children: participants.map((participant) => /* @__PURE__ */ jsx(ParticipantButton, {
1600
1970
  participant,
1601
1971
  collaboration
@@ -1610,13 +1980,13 @@ function ParticipantButton({ participant, collaboration }) {
1610
1980
  const label = local ? `${participant.name} (you)` : !followable ? participant.name : following ? `${participant.name} · click to stop following` : `${participant.name} · click to follow`;
1611
1981
  return /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1612
1982
  render: local || !followable ? /* @__PURE__ */ jsx("span", {
1613
- className: "inline-flex size-7 shrink-0 items-center justify-center rounded-full",
1983
+ className: "aperture:inline-flex aperture:size-7 aperture:shrink-0 aperture:items-center aperture:justify-center aperture:rounded-full",
1614
1984
  "aria-label": label
1615
1985
  }) : /* @__PURE__ */ jsx(Button$1, {
1616
1986
  type: "button",
1617
1987
  variant: following ? "secondary" : "ghost",
1618
1988
  size: "icon-sm",
1619
- className: "shrink-0 rounded-full p-0",
1989
+ className: "aperture:shrink-0 aperture:rounded-full aperture:p-0",
1620
1990
  "aria-label": label,
1621
1991
  "aria-pressed": following,
1622
1992
  onClick: () => collaboration.follow(following ? null : participant.clientId)
@@ -1634,7 +2004,7 @@ function ParticipantAvatar({ participant, following }) {
1634
2004
  const controlClaimed = participant.holdingInput && participant.leaseMode === "explicit";
1635
2005
  return /* @__PURE__ */ jsxs(Avatar$1, {
1636
2006
  size: "sm",
1637
- className: cn("shrink-0 ring-2 ring-background", following && "ring-ring"),
2007
+ className: cn("aperture:shrink-0 aperture:ring-2 aperture:ring-background", following && "aperture:ring-ring"),
1638
2008
  children: [
1639
2009
  /* @__PURE__ */ jsx(AvatarImage, {
1640
2010
  src: gravatarURL(participant.avatarHash),
@@ -1642,7 +2012,7 @@ function ParticipantAvatar({ participant, following }) {
1642
2012
  }),
1643
2013
  /* @__PURE__ */ jsx(AvatarFallback, { children: initials(participant.name) }),
1644
2014
  participant.holdingInput ? /* @__PURE__ */ jsx(AvatarBadge, {
1645
- className: cn("bg-emerald-500", controlClaimed && "group-data-[size=sm]/avatar:size-3 group-data-[size=sm]/avatar:[&>svg]:!block group-data-[size=sm]/avatar:[&>svg]:size-2"),
2015
+ className: cn("aperture:bg-emerald-500", controlClaimed && "aperture:group-data-[size=sm]/avatar:size-3 aperture:group-data-[size=sm]/avatar:[&>svg]:!block aperture:group-data-[size=sm]/avatar:[&>svg]:size-2"),
1646
2016
  title: controlClaimed ? "Control claimed" : "Has input control",
1647
2017
  children: controlClaimed ? /* @__PURE__ */ jsx(Lock, {}) : /* @__PURE__ */ jsx(Radio, {})
1648
2018
  }) : null
@@ -1665,6 +2035,8 @@ function BrowserToolbar({ control, leading, features, collaborationRole, cdpUrl,
1665
2035
  const browserMutationEnabled = connected && collaborationRole !== "viewer";
1666
2036
  const drawingAvailable = connected && control.collaboration.phase === "connected" && Boolean(control.activeTargetId);
1667
2037
  const loading = control.activeTarget?.loading ?? false;
2038
+ const canGoBack = control.activeTarget?.canGoBack ?? true;
2039
+ const canGoForward = control.activeTarget?.canGoForward ?? true;
1668
2040
  const runningRecordings = control.recordings.filter((recording) => recording.status === "starting" || recording.status === "running");
1669
2041
  const hasRunningRecordings = runningRecordings.length > 0;
1670
2042
  const recordingTargetIds = new Set(runningRecordings.map((recording) => recording.targetId));
@@ -1677,10 +2049,10 @@ function BrowserToolbar({ control, leading, features, collaborationRole, cdpUrl,
1677
2049
  setUrlDraft(null);
1678
2050
  }
1679
2051
  return /* @__PURE__ */ jsxs("div", {
1680
- className: "flex min-w-0 flex-col bg-background",
2052
+ className: "aperture:flex aperture:min-w-0 aperture:flex-col aperture:bg-background",
1681
2053
  children: [leading || features.tabs || features.presence ? /* @__PURE__ */ jsxs("div", {
1682
2054
  "data-workbench-titlebar": true,
1683
- className: "flex min-w-0 shrink-0 items-stretch border-b bg-muted/35",
2055
+ className: "aperture:flex aperture:min-w-0 aperture:shrink-0 aperture:items-stretch aperture:border-b aperture:bg-muted/35",
1684
2056
  children: [
1685
2057
  leading,
1686
2058
  features.tabs ? /* @__PURE__ */ jsx(BrowserTabStrip, {
@@ -1695,25 +2067,26 @@ function BrowserToolbar({ control, leading, features, collaborationRole, cdpUrl,
1695
2067
  onDuplicate: control.duplicateTarget,
1696
2068
  onClose: control.closeTarget,
1697
2069
  onReload: control.reload,
1698
- onReorder: control.reorderTargets
1699
- }) : /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1" }),
2070
+ onReorder: control.reorderTargets,
2071
+ loadThumbnail: control.loadTargetThumbnail
2072
+ }) : /* @__PURE__ */ jsx("div", { className: "aperture:min-w-0 aperture:flex-1" }),
1700
2073
  features.presence ? /* @__PURE__ */ jsx(CollaborationPresence, { collaboration: control.collaboration }) : null
1701
2074
  ]
1702
2075
  }) : null, features.navigation || features.addressBar || features.drawing || features.devTools || features.menus ? /* @__PURE__ */ jsxs("div", {
1703
- className: "flex h-9 items-center gap-1 px-1.5",
2076
+ className: "aperture:flex aperture:h-9 aperture:items-center aperture:gap-1 aperture:px-1.5",
1704
2077
  children: [
1705
2078
  features.navigation ? /* @__PURE__ */ jsxs("div", {
1706
- className: "flex shrink-0 items-center gap-0.5",
2079
+ className: "aperture:flex aperture:shrink-0 aperture:items-center aperture:gap-0.5",
1707
2080
  children: [
1708
2081
  /* @__PURE__ */ jsx(ToolbarButton, {
1709
2082
  label: "Back",
1710
- disabled: !browserMutationEnabled,
2083
+ disabled: !browserMutationEnabled || !canGoBack,
1711
2084
  onClick: () => control.historyBack(),
1712
2085
  children: /* @__PURE__ */ jsx(ArrowLeft, {})
1713
2086
  }),
1714
2087
  /* @__PURE__ */ jsx(ToolbarButton, {
1715
2088
  label: "Forward",
1716
- disabled: !browserMutationEnabled,
2089
+ disabled: !browserMutationEnabled || !canGoForward,
1717
2090
  onClick: () => control.historyForward(),
1718
2091
  children: /* @__PURE__ */ jsx(ArrowRight, {})
1719
2092
  }),
@@ -1729,7 +2102,7 @@ function BrowserToolbar({ control, leading, features, collaborationRole, cdpUrl,
1729
2102
  ]
1730
2103
  }) : null,
1731
2104
  features.addressBar ? /* @__PURE__ */ jsx(InputGroup, {
1732
- className: "h-7 border-transparent bg-transparent transition-colors hover:border-input/50 hover:bg-muted/35 has-[[data-slot=input-group-control]:focus-visible]:border-input/70 has-[[data-slot=input-group-control]:focus-visible]:bg-background has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/20 dark:hover:bg-input/20",
2105
+ className: "aperture:h-7 aperture:border-transparent aperture:bg-transparent aperture:transition-colors aperture:hover:border-input/50 aperture:hover:bg-muted/35 aperture:has-[[data-slot=input-group-control]:focus-visible]:border-input/70 aperture:has-[[data-slot=input-group-control]:focus-visible]:bg-background aperture:has-[[data-slot=input-group-control]:focus-visible]:ring-2 aperture:has-[[data-slot=input-group-control]:focus-visible]:ring-ring/20 aperture:dark:hover:bg-input/20",
1733
2106
  children: /* @__PURE__ */ jsx(InputGroupInput, {
1734
2107
  value: urlDraft ?? displayUrl,
1735
2108
  onChange: (event) => setUrlDraft(event.target.value),
@@ -1741,17 +2114,17 @@ function BrowserToolbar({ control, leading, features, collaborationRole, cdpUrl,
1741
2114
  }
1742
2115
  },
1743
2116
  placeholder: "URL",
1744
- className: "h-7 px-2 font-mono text-xs text-muted-foreground transition-colors focus-visible:text-foreground",
2117
+ className: "aperture:h-7 aperture:px-2 aperture:font-mono aperture:text-xs aperture:text-muted-foreground aperture:transition-colors aperture:focus-visible:text-foreground",
1745
2118
  disabled: !browserMutationEnabled
1746
2119
  })
1747
- }) : /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1" }),
1748
- busy ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 shrink-0 animate-spin text-muted-foreground" }) : null,
2120
+ }) : /* @__PURE__ */ jsx("div", { className: "aperture:min-w-0 aperture:flex-1" }),
2121
+ busy ? /* @__PURE__ */ jsx(Loader2, { className: "aperture:size-4 aperture:shrink-0 aperture:animate-spin aperture:text-muted-foreground" }) : null,
1749
2122
  features.drawing ? /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1750
2123
  render: /* @__PURE__ */ jsx(Button$1, {
1751
2124
  type: "button",
1752
2125
  variant: paintingEnabled ? "secondary" : "ghost",
1753
2126
  size: "icon-sm",
1754
- className: "shrink-0",
2127
+ className: "aperture:shrink-0",
1755
2128
  disabled: !paintingEnabled && !drawingAvailable,
1756
2129
  "aria-label": paintingEnabled ? "Stop drawing" : "Draw on this tab",
1757
2130
  "aria-pressed": paintingEnabled,
@@ -1852,14 +2225,14 @@ function isLikelyHost(value) {
1852
2225
  }
1853
2226
  //#endregion
1854
2227
  //#region ../ui/src/components/badge.tsx
1855
- const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
2228
+ const badgeVariants = cva("aperture:group/badge aperture:inline-flex aperture:h-5 aperture:w-fit aperture:shrink-0 aperture:items-center aperture:justify-center aperture:gap-1 aperture:overflow-hidden aperture:rounded-4xl aperture:border aperture:border-transparent aperture:px-2 aperture:py-0.5 aperture:text-xs aperture:font-medium aperture:whitespace-nowrap aperture:transition-all aperture:focus-visible:border-ring aperture:focus-visible:ring-[3px] aperture:focus-visible:ring-ring/50 aperture:has-data-[icon=inline-end]:pr-1.5 aperture:has-data-[icon=inline-start]:pl-1.5 aperture:aria-invalid:border-destructive aperture:aria-invalid:ring-destructive/20 aperture:dark:aria-invalid:ring-destructive/40 aperture:[&>svg]:pointer-events-none aperture:[&>svg]:size-3!", {
1856
2229
  variants: { variant: {
1857
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
1858
- secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
1859
- destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
1860
- outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
1861
- ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
1862
- link: "text-primary underline-offset-4 hover:underline"
2230
+ default: "aperture:bg-primary aperture:text-primary-foreground aperture:[a]:hover:bg-primary/80",
2231
+ secondary: "aperture:bg-secondary aperture:text-secondary-foreground aperture:[a]:hover:bg-secondary/80",
2232
+ destructive: "aperture:bg-destructive/10 aperture:text-destructive aperture:focus-visible:ring-destructive/20 aperture:dark:bg-destructive/20 aperture:dark:focus-visible:ring-destructive/40 aperture:[a]:hover:bg-destructive/20",
2233
+ outline: "aperture:border-border aperture:text-foreground aperture:[a]:hover:bg-muted aperture:[a]:hover:text-muted-foreground",
2234
+ ghost: "aperture:hover:bg-muted aperture:hover:text-muted-foreground aperture:dark:hover:bg-muted/50",
2235
+ link: "aperture:text-primary aperture:underline-offset-4 aperture:hover:underline"
1863
2236
  } },
1864
2237
  defaultVariants: { variant: "default" }
1865
2238
  });
@@ -1885,7 +2258,7 @@ function showNotice(notice) {
1885
2258
  function BrowserViewport({ statusBadge = true, className, ...props }) {
1886
2259
  return /* @__PURE__ */ jsx(SessionViewport, {
1887
2260
  ...props,
1888
- className: className ?? "bg-background",
2261
+ className: className ?? "aperture:bg-background",
1889
2262
  onNotice: showNotice,
1890
2263
  renderOverlay: (overlay) => /* @__PURE__ */ jsx(ViewportOverlay, {
1891
2264
  overlay,
@@ -1896,26 +2269,26 @@ function BrowserViewport({ statusBadge = true, className, ...props }) {
1896
2269
  function ViewportOverlay({ overlay, statusBadge }) {
1897
2270
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1898
2271
  overlay.placeholder ? /* @__PURE__ */ jsx("div", {
1899
- className: "pointer-events-none absolute inset-0 flex items-center justify-center",
2272
+ className: "aperture:pointer-events-none aperture:absolute aperture:inset-0 aperture:flex aperture:items-center aperture:justify-center",
1900
2273
  children: /* @__PURE__ */ jsx(ViewportPlaceholder, { placeholder: overlay.placeholder })
1901
2274
  }) : null,
1902
2275
  statusBadge ? /* @__PURE__ */ jsx("div", {
1903
- className: "pointer-events-none absolute right-2 bottom-2 flex items-center gap-1.5",
2276
+ className: "aperture:pointer-events-none aperture:absolute aperture:right-2 aperture:bottom-2 aperture:flex aperture:items-center aperture:gap-1.5",
1904
2277
  children: /* @__PURE__ */ jsx(StatusBadge, { status: overlay.status })
1905
2278
  }) : null,
1906
2279
  overlay.followedCursor ? /* @__PURE__ */ jsxs("div", {
1907
- className: "pointer-events-none absolute z-30 flex translate-x-[-2px] translate-y-[-2px] items-start text-primary drop-shadow-sm",
2280
+ className: "aperture:pointer-events-none aperture:absolute aperture:z-30 aperture:flex aperture:translate-x-[-2px] aperture:translate-y-[-2px] aperture:items-start aperture:text-primary aperture:drop-shadow-sm",
1908
2281
  style: {
1909
2282
  left: overlay.followedCursor.x,
1910
2283
  top: overlay.followedCursor.y
1911
2284
  },
1912
- children: [/* @__PURE__ */ jsx(MousePointer2, { className: "size-5 fill-primary stroke-background stroke-[1.5]" }), /* @__PURE__ */ jsx("span", {
1913
- className: "mt-4 -ml-1 rounded bg-primary px-1.5 py-0.5 text-[10px] leading-none font-medium whitespace-nowrap text-primary-foreground",
2285
+ children: [/* @__PURE__ */ jsx(MousePointer2, { className: "aperture:size-5 aperture:fill-primary aperture:stroke-background aperture:stroke-[1.5]" }), /* @__PURE__ */ jsx("span", {
2286
+ className: "aperture:mt-4 aperture:-ml-1 aperture:rounded aperture:bg-primary aperture:px-1.5 aperture:py-0.5 aperture:text-[10px] aperture:leading-none aperture:font-medium aperture:whitespace-nowrap aperture:text-primary-foreground",
1914
2287
  children: overlay.followedCursor.name
1915
2288
  })]
1916
2289
  }) : null,
1917
2290
  overlay.cursorHint ? /* @__PURE__ */ jsx("div", {
1918
- className: "pointer-events-none absolute z-20 max-w-64 translate-x-3 translate-y-3 rounded-md border bg-popover px-2 py-1 text-xs text-popover-foreground shadow-md",
2291
+ className: "aperture:pointer-events-none aperture:absolute aperture:z-20 aperture:max-w-64 aperture:translate-x-3 aperture:translate-y-3 aperture:rounded-md aperture:border aperture:bg-popover aperture:px-2 aperture:py-1 aperture:text-xs aperture:text-popover-foreground aperture:shadow-md",
1919
2292
  style: {
1920
2293
  left: overlay.cursorHint.x,
1921
2294
  top: overlay.cursorHint.y
@@ -1923,7 +2296,7 @@ function ViewportOverlay({ overlay, statusBadge }) {
1923
2296
  children: overlay.cursorHint.text
1924
2297
  }) : null,
1925
2298
  overlay.collaborationError ? /* @__PURE__ */ jsx("div", {
1926
- className: "pointer-events-none absolute bottom-10 left-2 max-w-[80%] rounded-md border border-amber-500/40 bg-background/90 px-2 py-1 text-xs text-amber-800 dark:text-amber-300",
2299
+ className: "aperture:pointer-events-none aperture:absolute aperture:bottom-10 aperture:left-2 aperture:max-w-[80%] aperture:rounded-md aperture:border aperture:border-amber-500/40 aperture:bg-background/90 aperture:px-2 aperture:py-1 aperture:text-xs aperture:text-amber-800 aperture:dark:text-amber-300",
1927
2300
  children: overlay.collaborationError
1928
2301
  }) : null
1929
2302
  ] });
@@ -1937,14 +2310,14 @@ const placeholderLabels = {
1937
2310
  };
1938
2311
  function ViewportPlaceholder({ placeholder }) {
1939
2312
  return /* @__PURE__ */ jsxs("div", {
1940
- className: "flex flex-col items-center gap-2 text-sm text-muted-foreground",
1941
- children: [placeholder === "disconnected" ? /* @__PURE__ */ jsx(Unplug, { className: "size-5" }) : /* @__PURE__ */ jsx(Loader2, { className: "size-5 animate-spin" }), placeholderLabels[placeholder]]
2313
+ className: "aperture:flex aperture:flex-col aperture:items-center aperture:gap-2 aperture:text-sm aperture:text-muted-foreground",
2314
+ children: [placeholder === "disconnected" ? /* @__PURE__ */ jsx(Unplug, { className: "aperture:size-5" }) : /* @__PURE__ */ jsx(Loader2, { className: "aperture:size-5 aperture:animate-spin" }), placeholderLabels[placeholder]]
1942
2315
  });
1943
2316
  }
1944
2317
  function StatusBadge({ status }) {
1945
2318
  if (status === "webrtc" || status === "websocket") return /* @__PURE__ */ jsx(Badge, {
1946
2319
  variant: "secondary",
1947
- className: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-300",
2320
+ className: "aperture:bg-emerald-500/15 aperture:text-emerald-700 aperture:dark:text-emerald-300",
1948
2321
  children: status
1949
2322
  });
1950
2323
  return /* @__PURE__ */ jsx(Badge, {
@@ -1965,7 +2338,7 @@ function BrowserDevToolsPane({ cdpUrl, targetId }) {
1965
2338
  return frame.toString();
1966
2339
  }, [cdpUrl, targetId]);
1967
2340
  if (!src) return /* @__PURE__ */ jsx(Empty, {
1968
- className: "h-full rounded-none border-none",
2341
+ className: "aperture:h-full aperture:rounded-none aperture:border-none",
1969
2342
  children: /* @__PURE__ */ jsxs(EmptyHeader, { children: [
1970
2343
  /* @__PURE__ */ jsx(EmptyMedia, {
1971
2344
  variant: "icon",
@@ -1976,7 +2349,7 @@ function BrowserDevToolsPane({ cdpUrl, targetId }) {
1976
2349
  ] })
1977
2350
  });
1978
2351
  return /* @__PURE__ */ jsx("iframe", {
1979
- className: "h-full w-full border-0 bg-background",
2352
+ className: "aperture:h-full aperture:w-full aperture:border-0 aperture:bg-background",
1980
2353
  src,
1981
2354
  title: "Browser DevTools",
1982
2355
  allow: "clipboard-read; clipboard-write",
@@ -1988,7 +2361,7 @@ function BrowserDevToolsPane({ cdpUrl, targetId }) {
1988
2361
  function ResizablePanelGroup({ className, ...props }) {
1989
2362
  return /* @__PURE__ */ jsx(ResizablePrimitive.Group, {
1990
2363
  "data-slot": "resizable-panel-group",
1991
- className: cn("flex h-full w-full aria-[orientation=vertical]:flex-col", className),
2364
+ className: cn("aperture:flex aperture:h-full aperture:w-full aperture:aria-[orientation=vertical]:flex-col", className),
1992
2365
  ...props
1993
2366
  });
1994
2367
  }
@@ -2001,9 +2374,9 @@ function ResizablePanel({ ...props }) {
2001
2374
  function ResizableHandle({ withHandle, className, ...props }) {
2002
2375
  return /* @__PURE__ */ jsx(ResizablePrimitive.Separator, {
2003
2376
  "data-slot": "resizable-handle",
2004
- className: cn("relative flex w-px items-center justify-center bg-border ring-offset-background after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-hidden aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90", className),
2377
+ className: cn("aperture:relative aperture:flex aperture:w-px aperture:items-center aperture:justify-center aperture:bg-border aperture:ring-offset-background aperture:after:absolute aperture:after:inset-y-0 aperture:after:left-1/2 aperture:after:w-1 aperture:after:-translate-x-1/2 aperture:focus-visible:ring-1 aperture:focus-visible:ring-ring aperture:focus-visible:outline-hidden aperture:aria-[orientation=horizontal]:h-px aperture:aria-[orientation=horizontal]:w-full aperture:aria-[orientation=horizontal]:after:left-0 aperture:aria-[orientation=horizontal]:after:h-1 aperture:aria-[orientation=horizontal]:after:w-full aperture:aria-[orientation=horizontal]:after:translate-x-0 aperture:aria-[orientation=horizontal]:after:-translate-y-1/2 aperture:[&[aria-orientation=horizontal]>div]:rotate-90", className),
2005
2378
  ...props,
2006
- children: withHandle && /* @__PURE__ */ jsx("div", { className: "z-10 flex h-6 w-1 shrink-0 rounded-lg bg-border" })
2379
+ children: withHandle && /* @__PURE__ */ jsx("div", { className: "aperture:z-10 aperture:flex aperture:h-6 aperture:w-1 aperture:shrink-0 aperture:rounded-lg aperture:bg-border" })
2007
2380
  });
2008
2381
  }
2009
2382
  //#endregion
@@ -2052,8 +2425,16 @@ function BrowserControlPane({ control, leading, features: featureOverrides, coll
2052
2425
  window.localStorage.setItem(LOCAL_CURSOR_STORAGE_KEY, String(enabled));
2053
2426
  } catch {}
2054
2427
  }
2428
+ const viewport = /* @__PURE__ */ jsx(BrowserViewport, {
2429
+ control,
2430
+ viewport: control.viewport,
2431
+ localCursorEnabled,
2432
+ paintingEnabled: features.drawing && paintingEnabled,
2433
+ onPaintingEnabledChange: setPaintingEnabled,
2434
+ statusBadge: features.statusBadge
2435
+ });
2055
2436
  return /* @__PURE__ */ jsxs("div", {
2056
- className: "flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
2437
+ className: "aperture:flex aperture:h-full aperture:min-h-0 aperture:min-w-0 aperture:flex-1 aperture:flex-col aperture:overflow-hidden",
2057
2438
  children: [/* @__PURE__ */ jsx(BrowserToolbar, {
2058
2439
  control,
2059
2440
  leading,
@@ -2071,22 +2452,15 @@ function BrowserControlPane({ control, leading, features: featureOverrides, coll
2071
2452
  onDevToolsOpenChange: handleDevToolsOpenChange,
2072
2453
  onDevToolsDockChange: setDevToolsDock,
2073
2454
  onSessionDetails
2074
- }), /* @__PURE__ */ jsxs(ResizablePanelGroup, {
2455
+ }), features.devTools ? /* @__PURE__ */ jsxs(ResizablePanelGroup, {
2075
2456
  orientation: devToolsDock === "bottom" ? "vertical" : "horizontal",
2076
- className: "min-h-0 min-w-0 flex-1 has-[[data-separator=active]]:[&_iframe]:pointer-events-none",
2457
+ className: "aperture:min-h-0 aperture:min-w-0 aperture:flex-1 aperture:has-[[data-separator=active]]:[&_iframe]:pointer-events-none",
2077
2458
  children: [
2078
2459
  /* @__PURE__ */ jsx(ResizablePanel, {
2079
- className: "flex min-h-0 min-w-0",
2460
+ className: "aperture:flex aperture:min-h-0 aperture:min-w-0",
2080
2461
  defaultSize: "60%",
2081
2462
  minSize: "20%",
2082
- children: /* @__PURE__ */ jsx(BrowserViewport, {
2083
- control,
2084
- viewport: control.viewport,
2085
- localCursorEnabled,
2086
- paintingEnabled: features.drawing && paintingEnabled,
2087
- onPaintingEnabledChange: setPaintingEnabled,
2088
- statusBadge: features.statusBadge
2089
- })
2463
+ children: viewport
2090
2464
  }),
2091
2465
  /* @__PURE__ */ jsx(ResizableHandle, {
2092
2466
  withHandle: true,
@@ -2095,12 +2469,12 @@ function BrowserControlPane({ control, leading, features: featureOverrides, coll
2095
2469
  }),
2096
2470
  /* @__PURE__ */ jsx(ResizablePanel, {
2097
2471
  panelRef: devToolsPanelRef,
2098
- className: "flex min-h-0 min-w-0",
2472
+ className: "aperture:flex aperture:min-h-0 aperture:min-w-0",
2099
2473
  collapsible: true,
2100
2474
  defaultSize: "40%",
2101
2475
  minSize: "20%",
2102
- children: Array.from(features.devTools ? devToolsTargetIds : [], (targetId) => /* @__PURE__ */ jsx("div", {
2103
- className: "h-full min-h-0 w-full min-w-0",
2476
+ children: Array.from(devToolsTargetIds, (targetId) => /* @__PURE__ */ jsx("div", {
2477
+ className: "aperture:h-full aperture:min-h-0 aperture:w-full aperture:min-w-0",
2104
2478
  hidden: targetId !== activeTargetId,
2105
2479
  children: /* @__PURE__ */ jsx(BrowserDevToolsPane, {
2106
2480
  cdpUrl,
@@ -2109,28 +2483,19 @@ function BrowserControlPane({ control, leading, features: featureOverrides, coll
2109
2483
  }, targetId))
2110
2484
  })
2111
2485
  ]
2112
- })]
2486
+ }) : viewport]
2113
2487
  });
2114
2488
  }
2115
2489
  //#endregion
2116
- //#region src/aperture-session.tsx
2117
- function ApertureSession({ baseUrl, theme = "system", className, ...props }) {
2490
+ //#region src/ui-root.tsx
2491
+ function ApertureUIRoot({ theme = "system", className, children }) {
2118
2492
  const [root, setRoot] = useState(null);
2119
- const dark = useDarkTheme(theme);
2120
2493
  return /* @__PURE__ */ jsx("div", {
2121
2494
  ref: setRoot,
2122
- className: cn("aperture-root relative h-full w-full", dark && "dark", className),
2123
- children: root ? /* @__PURE__ */ jsx(ApertureProvider, {
2124
- baseUrl,
2125
- children: /* @__PURE__ */ jsx(PortalContainerProvider, {
2126
- container: root,
2127
- children: /* @__PURE__ */ jsxs(TooltipProvider, { children: [/* @__PURE__ */ jsx(SharedSession, { ...props }), props.features?.toaster !== false ? /* @__PURE__ */ jsx(Toaster$1, {
2128
- theme: dark ? "dark" : "light",
2129
- richColors: true,
2130
- closeButton: true,
2131
- position: "bottom-center"
2132
- }) : null] })
2133
- })
2495
+ className: cn("aperture-root aperture:relative aperture:h-full aperture:w-full", useDarkTheme(theme) && "aperture-dark", className),
2496
+ children: root ? /* @__PURE__ */ jsx(PortalContainerProvider, {
2497
+ container: root,
2498
+ children: /* @__PURE__ */ jsx(TooltipProvider, { children })
2134
2499
  }) : null
2135
2500
  });
2136
2501
  }
@@ -2146,36 +2511,58 @@ function useDarkTheme(theme) {
2146
2511
  }, [theme]);
2147
2512
  return theme === "dark" || theme === "system" && systemDark;
2148
2513
  }
2149
- function SharedSession({ token, features, leading }) {
2150
- const { status, share, control } = useSharedSession({
2151
- token,
2514
+ //#endregion
2515
+ //#region src/aperture-session.tsx
2516
+ function ApertureSession({ theme = "system", className, ...props }) {
2517
+ return /* @__PURE__ */ jsx(ApertureUIRoot, {
2518
+ theme,
2519
+ className,
2520
+ children: /* @__PURE__ */ jsxs(ApertureProvider, {
2521
+ baseUrl: props.access?.baseUrl,
2522
+ children: [/* @__PURE__ */ jsx(Session, { ...props }), props.features?.toaster !== false ? /* @__PURE__ */ jsx(Toaster$1, {
2523
+ theme,
2524
+ richColors: true,
2525
+ closeButton: true,
2526
+ position: "bottom-center"
2527
+ }) : null]
2528
+ })
2529
+ });
2530
+ }
2531
+ function Session({ access, features, leading }) {
2532
+ const { status, control } = useSession({
2533
+ access,
2152
2534
  onNotice: showNotice
2153
2535
  });
2154
2536
  switch (status) {
2155
2537
  case "invalid": return /* @__PURE__ */ jsx(SessionState, {
2156
2538
  icon: /* @__PURE__ */ jsx(Link2Off, {}),
2157
- title: "Invalid share link",
2158
- description: "This link does not contain a valid session capability."
2539
+ title: "Invalid session access",
2540
+ description: "Provide a valid session connection."
2159
2541
  });
2160
2542
  case "loading": return /* @__PURE__ */ jsx(SessionState, {
2161
- icon: /* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }),
2162
- title: "Opening shared session"
2543
+ icon: /* @__PURE__ */ jsx(Loader2, { className: "aperture:animate-spin" }),
2544
+ title: "Opening session"
2545
+ });
2546
+ case "denied": return /* @__PURE__ */ jsx(SessionState, {
2547
+ icon: /* @__PURE__ */ jsx(Link2Off, {}),
2548
+ title: "Session access denied",
2549
+ description: "Sign in to the app or ask for access to this session."
2163
2550
  });
2164
2551
  case "expired": return /* @__PURE__ */ jsx(SessionState, {
2165
2552
  icon: /* @__PURE__ */ jsx(Link2Off, {}),
2166
- title: "Share link expired",
2167
- description: "This session capability has expired. Ask the session owner for a new link."
2553
+ title: "Session access expired",
2554
+ description: "Ask the session owner to renew access."
2168
2555
  });
2169
2556
  case "unavailable": return /* @__PURE__ */ jsx(SessionState, {
2170
2557
  icon: /* @__PURE__ */ jsx(Link2Off, {}),
2171
- title: "Shared session unavailable",
2172
- description: "This link is invalid, revoked, or the shared session is no longer available."
2558
+ title: "Session unavailable",
2559
+ description: "This session is unavailable or access has been revoked."
2173
2560
  });
2174
2561
  case "ready": return /* @__PURE__ */ jsx("div", {
2175
- className: "flex h-full min-h-0 flex-1 flex-col overflow-hidden bg-background",
2562
+ className: "aperture:flex aperture:h-full aperture:min-h-0 aperture:flex-1 aperture:flex-col aperture:overflow-hidden aperture:bg-background",
2176
2563
  children: /* @__PURE__ */ jsx(BrowserControlPane, {
2177
2564
  control,
2178
- collaborationRole: share?.role ?? "viewer",
2565
+ collaborationRole: control.collaboration.role,
2179
2566
  cdpUrl: null,
2180
2567
  shareUrls: null,
2181
2568
  leading,
@@ -2195,7 +2582,7 @@ function devToolsUrl(origin, cdpUrl, sessionToken) {
2195
2582
  }
2196
2583
  function SessionState({ icon, title, description }) {
2197
2584
  return /* @__PURE__ */ jsx(Empty, {
2198
- className: "h-full border-none",
2585
+ className: "aperture:h-full aperture:border-none",
2199
2586
  children: /* @__PURE__ */ jsxs(EmptyHeader, { children: [
2200
2587
  /* @__PURE__ */ jsx(EmptyMedia, {
2201
2588
  variant: "icon",
@@ -2207,6 +2594,6 @@ function SessionState({ icon, title, description }) {
2207
2594
  });
2208
2595
  }
2209
2596
  //#endregion
2210
- export { ApertureProvider, ApertureSession, BrowserControlPane, BrowserViewport, ClipboardError, LiveSessionError, RuntimeProvider, RuntimeProviderMissingError, SessionViewport, SharedSession, collaborationPaintLifetimeMs, copyText, copyTextWithToast, devToolsUrl, makeApertureRuntime, parseShareToken, resolveSessionFeatures, showNotice, useBaseUrl, useBrowserControl, useEffectCallback, useFork, useLiveSession, useRunApi, useRuntime, useSharedSession };
2597
+ export { ApertureProvider, ApertureSession, ApertureUIRoot, BrowserControlPane, BrowserViewport, ClipboardError, LiveSessionError, RuntimeProvider, RuntimeProviderMissingError, Session, SessionViewport, collaborationPaintLifetimeMs, copyText, copyTextWithToast, devToolsUrl, makeApertureRuntime, resolveSessionFeatures, shareSessionAccess, showNotice, useBaseUrl, useBrowserControl, useEffectCallback, useFork, useLiveSession, useRunApi, useRuntime, useSession };
2211
2598
 
2212
2599
  //# sourceMappingURL=index.js.map