@ohhwells/bridge 0.1.139-next.382 → 0.1.139-next.383

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.cjs CHANGED
@@ -17960,7 +17960,7 @@ function AiSectionOverlay({
17960
17960
 
17961
17961
  // src/ui/ai-section/AiMediaStripOverlay.tsx
17962
17962
  var import_react10 = require("react");
17963
- var import_lucide_react8 = require("lucide-react");
17963
+ var import_lucide_react9 = require("lucide-react");
17964
17964
 
17965
17965
  // src/lib/ai-media-strip-tree.ts
17966
17966
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -18074,14 +18074,6 @@ function setMediaStripItemAsset(tree, index, assetId) {
18074
18074
  return true;
18075
18075
  });
18076
18076
  }
18077
- function setMediaStripItemAlt(tree, index, alt) {
18078
- return withMediaStrip(tree, (block) => {
18079
- const items = block.items;
18080
- if (index < 0 || index >= items.length) return false;
18081
- items[index] = { ...items[index], alt };
18082
- return true;
18083
- });
18084
- }
18085
18077
  function setMediaStripItemLink(tree, index, link) {
18086
18078
  return withMediaStrip(tree, (block) => {
18087
18079
  const items = block.items;
@@ -18099,11 +18091,117 @@ function setMediaStripItemLink(tree, index, link) {
18099
18091
  });
18100
18092
  }
18101
18093
 
18102
- // src/ui/ai-section/AiMediaStripOverlay.tsx
18094
+ // src/ui/dialog.tsx
18095
+ var React9 = __toESM(require("react"), 1);
18096
+ var import_radix_ui6 = require("radix-ui");
18097
+ var import_lucide_react8 = require("lucide-react");
18103
18098
  var import_jsx_runtime19 = require("react/jsx-runtime");
18104
- var PRIMARY3 = "#0885FE";
18105
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18106
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
18099
+ function Dialog2({
18100
+ ...props
18101
+ }) {
18102
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
18103
+ }
18104
+ function DialogPortal({
18105
+ ...props
18106
+ }) {
18107
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
18108
+ }
18109
+ function DialogOverlay({
18110
+ className,
18111
+ ...props
18112
+ }) {
18113
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18114
+ import_radix_ui6.Dialog.Overlay,
18115
+ {
18116
+ "data-slot": "dialog-overlay",
18117
+ "data-ohw-link-modal-root": "",
18118
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
18119
+ ...props
18120
+ }
18121
+ );
18122
+ }
18123
+ var DialogContent = React9.forwardRef(
18124
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
18125
+ const positionMode = container ? "absolute" : "fixed";
18126
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogPortal, { container: container ?? void 0, children: [
18127
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
18128
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18129
+ import_radix_ui6.Dialog.Content,
18130
+ {
18131
+ ref,
18132
+ "data-slot": "dialog-content",
18133
+ className: cn(
18134
+ positionMode,
18135
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
18136
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
18137
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
18138
+ className
18139
+ ),
18140
+ onMouseDown: (e) => e.stopPropagation(),
18141
+ ...props,
18142
+ children: [
18143
+ children,
18144
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18145
+ import_radix_ui6.Dialog.Close,
18146
+ {
18147
+ type: "button",
18148
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
18149
+ "aria-label": "Close",
18150
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
18151
+ }
18152
+ ) : null
18153
+ ]
18154
+ }
18155
+ )
18156
+ ] });
18157
+ }
18158
+ );
18159
+ DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
18160
+ function DialogHeader({
18161
+ className,
18162
+ ...props
18163
+ }) {
18164
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
18165
+ }
18166
+ function DialogFooter({
18167
+ className,
18168
+ ...props
18169
+ }) {
18170
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18171
+ "div",
18172
+ {
18173
+ className: cn("flex items-center justify-end gap-2", className),
18174
+ ...props
18175
+ }
18176
+ );
18177
+ }
18178
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18179
+ import_radix_ui6.Dialog.Title,
18180
+ {
18181
+ ref,
18182
+ className: cn(
18183
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
18184
+ className
18185
+ ),
18186
+ ...props
18187
+ }
18188
+ ));
18189
+ DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
18190
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18191
+ import_radix_ui6.Dialog.Description,
18192
+ {
18193
+ ref,
18194
+ className: cn("text-sm text-muted-foreground", className),
18195
+ ...props
18196
+ }
18197
+ ));
18198
+ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
18199
+ var DialogClose = import_radix_ui6.Dialog.Close;
18200
+
18201
+ // src/ui/ai-section/AiMediaStripOverlay.tsx
18202
+ var import_jsx_runtime20 = require("react/jsx-runtime");
18203
+ var PRIMARY3 = "var(--ohw-primary, #0885FE)";
18204
+ var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
18107
18205
  function findMediaStripItemElement(sectionId, index) {
18108
18206
  const sectionEl = findByInstanceId(sectionId);
18109
18207
  if (!sectionEl) return null;
@@ -18113,14 +18211,35 @@ function findMediaStripItemElement(sectionId, index) {
18113
18211
  }
18114
18212
  return null;
18115
18213
  }
18116
- function readRect2(ref) {
18117
- const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18214
+ function itemRefFromTarget(target) {
18215
+ if (!(target instanceof Element)) return null;
18216
+ if (target.closest(CHROME_SELECTOR)) return null;
18217
+ const itemEl = target.closest("[data-ohw-media-strip-item]");
18218
+ if (!itemEl) return null;
18219
+ const sectionEl = itemEl.closest("[data-ohw-section]");
18220
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
18221
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
18222
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
18223
+ if (!sectionId || Number.isNaN(index)) return null;
18224
+ return { sectionId, index };
18225
+ }
18226
+ function sameRef(a, b) {
18227
+ return a?.sectionId === b?.sectionId && a?.index === b?.index;
18228
+ }
18229
+ function rectOf(el) {
18118
18230
  if (!el) return null;
18119
18231
  const r2 = el.getBoundingClientRect();
18120
18232
  if (r2.width <= 0 || r2.height <= 0) return null;
18121
18233
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
18122
18234
  }
18123
- function useLiveItemRect(ref) {
18235
+ function readRect2(ref) {
18236
+ return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
18237
+ }
18238
+ function readStripRect(ref) {
18239
+ const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18240
+ return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
18241
+ }
18242
+ function useLiveItemRect(ref, strip = false) {
18124
18243
  const [rect, setRect] = (0, import_react10.useState)(null);
18125
18244
  (0, import_react10.useEffect)(() => {
18126
18245
  if (!ref) {
@@ -18128,7 +18247,7 @@ function useLiveItemRect(ref) {
18128
18247
  return;
18129
18248
  }
18130
18249
  const update = () => {
18131
- const next = readRect2(ref);
18250
+ const next = strip ? readStripRect(ref) : readRect2(ref);
18132
18251
  setRect(
18133
18252
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
18134
18253
  );
@@ -18143,9 +18262,49 @@ function useLiveItemRect(ref) {
18143
18262
  window.removeEventListener("resize", update);
18144
18263
  clearInterval(interval);
18145
18264
  };
18146
- }, [ref?.sectionId, ref?.index]);
18265
+ }, [ref?.sectionId, ref?.index, strip]);
18147
18266
  return rect;
18148
18267
  }
18268
+ var TOOLBAR_STRIP_GAP = 52;
18269
+ var TOOLBAR_STRIP_GAP_MOBILE = 42;
18270
+ var TOOLBAR_EDGE_MARGIN2 = 4;
18271
+ function StripToolbarSlot({ stripRect, children }) {
18272
+ const slotRef = (0, import_react10.useRef)(null);
18273
+ const [shift, setShift] = (0, import_react10.useState)(0);
18274
+ const centerX = stripRect.left + stripRect.width / 2;
18275
+ (0, import_react10.useLayoutEffect)(() => {
18276
+ const slot = slotRef.current;
18277
+ if (!slot) return;
18278
+ const clamp = () => {
18279
+ const w = slot.offsetWidth;
18280
+ if (w <= 0) return;
18281
+ const half = w / 2;
18282
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
18283
+ setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
18284
+ };
18285
+ clamp();
18286
+ const ro = new ResizeObserver(clamp);
18287
+ ro.observe(slot);
18288
+ return () => ro.disconnect();
18289
+ }, [centerX, children]);
18290
+ const mobile = window.innerWidth <= 480;
18291
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18292
+ "div",
18293
+ {
18294
+ ref: slotRef,
18295
+ "data-ohw-bridge": "",
18296
+ "data-ohw-item-interaction": "",
18297
+ className: "pointer-events-auto fixed",
18298
+ style: {
18299
+ top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
18300
+ left: centerX + shift,
18301
+ transform: "translate(-50%, -100%)",
18302
+ zIndex: 2147483645
18303
+ },
18304
+ children
18305
+ }
18306
+ );
18307
+ }
18149
18308
  function requestValidateTree(postToParent2, tree) {
18150
18309
  return new Promise((resolve) => {
18151
18310
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -18182,116 +18341,72 @@ function requestAssetPick(postToParent2) {
18182
18341
  postToParent2({ type: "ow:asset-pick", id });
18183
18342
  });
18184
18343
  }
18185
- function Pill({
18344
+ function ToolbarAction({
18186
18345
  label,
18187
18346
  onClick,
18188
- disabled,
18189
- active
18347
+ disabled = false,
18348
+ active = false,
18349
+ children
18190
18350
  }) {
18191
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18192
- "button",
18193
- {
18194
- type: "button",
18195
- onClick,
18196
- disabled,
18197
- style: {
18198
- height: 28,
18199
- padding: "0 10px",
18200
- display: "inline-flex",
18201
- alignItems: "center",
18202
- gap: 4,
18203
- borderRadius: 999,
18204
- border: "none",
18205
- cursor: disabled ? "default" : "pointer",
18206
- background: active ? OHW_BLUE_DARK : "#ffffff",
18207
- color: active ? "#ffffff" : OHW_BLUE_DARK,
18208
- opacity: disabled ? 0.4 : 1,
18209
- fontSize: 12,
18210
- lineHeight: "16px",
18211
- fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18212
- whiteSpace: "nowrap",
18213
- flex: "0 0 auto"
18214
- },
18215
- children: label
18216
- }
18217
- );
18351
+ const button = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
18352
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18353
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: button }) : button }),
18354
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
18355
+ ] });
18218
18356
  }
18219
- function AltInput({ value, onCommit }) {
18220
- const [draft, setDraft] = (0, import_react10.useState)(value);
18221
- (0, import_react10.useEffect)(() => setDraft(value), [value]);
18222
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18223
- "div",
18224
- {
18225
- style: {
18226
- display: "flex",
18227
- alignItems: "center",
18228
- gap: 6,
18229
- height: 28,
18230
- padding: "0 12px",
18231
- borderRadius: 999,
18232
- background: "#ffffff",
18233
- width: "100%",
18234
- maxWidth: 260,
18235
- boxSizing: "border-box"
18236
- },
18237
- children: [
18238
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18239
- "span",
18240
- {
18241
- style: {
18242
- flex: "0 0 auto",
18243
- color: OHW_BLUE_DARK,
18244
- fontSize: 10,
18245
- fontWeight: 700,
18246
- textTransform: "uppercase",
18247
- letterSpacing: 0.4
18248
- },
18249
- children: "Alt"
18250
- }
18251
- ),
18252
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18253
- "input",
18357
+ function ToolbarSeparator() {
18358
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
18359
+ }
18360
+ function OptionMenu({
18361
+ label,
18362
+ value,
18363
+ options,
18364
+ onChange
18365
+ }) {
18366
+ const current = options.find((o) => o.value === value) ?? options[0];
18367
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18368
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
18369
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenu, { modal: false, children: [
18370
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18371
+ "button",
18372
+ {
18373
+ type: "button",
18374
+ "aria-label": label,
18375
+ className: "inline-flex h-8 min-w-[76px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
18376
+ children: [
18377
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18378
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18379
+ ]
18380
+ }
18381
+ ) }),
18382
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
18383
+ const selected = option.value === value;
18384
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18385
+ DropdownMenuItem,
18254
18386
  {
18255
- type: "text",
18256
- value: draft,
18257
- onChange: (e) => setDraft(e.target.value),
18258
- onBlur: () => {
18259
- if (draft !== value) onCommit(draft);
18260
- },
18261
- onKeyDown: (e) => {
18262
- if (e.key === "Enter") e.currentTarget.blur();
18263
- if (e.key === "Escape") {
18264
- setDraft(value);
18265
- e.currentTarget.blur();
18266
- }
18267
- },
18268
- placeholder: "Describe this image",
18269
- "aria-label": "Image alt text",
18270
- style: {
18271
- flex: 1,
18272
- minWidth: 0,
18273
- border: "none",
18274
- outline: "none",
18275
- background: "transparent",
18276
- fontSize: 12,
18277
- lineHeight: "16px",
18278
- fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18279
- color: "#1f2937"
18280
- }
18281
- }
18282
- )
18283
- ]
18284
- }
18285
- );
18387
+ role: "menuitemradio",
18388
+ "aria-checked": selected,
18389
+ onSelect: () => onChange(option.value),
18390
+ className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
18391
+ children: [
18392
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: option.label }),
18393
+ selected && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
18394
+ ]
18395
+ },
18396
+ option.value
18397
+ );
18398
+ }) })
18399
+ ] })
18400
+ ] });
18286
18401
  }
18287
18402
  var VARIANTS = [
18288
18403
  { value: "logos", label: "Logos" },
18289
18404
  { value: "reel", label: "Reel" }
18290
18405
  ];
18291
18406
  var ALIGNS = [
18292
- { value: "left", label: "Left" },
18293
- { value: "center", label: "Center" },
18294
- { value: "right", label: "Right" }
18407
+ { value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
18408
+ { value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
18409
+ { value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
18295
18410
  ];
18296
18411
  var HOVERS = [
18297
18412
  { value: "none", label: "None" },
@@ -18299,104 +18414,104 @@ var HOVERS = [
18299
18414
  { value: "zoom", label: "Zoom" },
18300
18415
  { value: "fade", label: "Fade" }
18301
18416
  ];
18302
- var TREATMENTS = [
18303
- { value: "color", label: "Color" },
18304
- { value: "grayscale", label: "Grayscale" }
18305
- ];
18306
18417
  var HTTP_URL_RE = /^https?:\/\/\S+$/i;
18307
- function LinkInput({ value, onCommit }) {
18308
- const [draft, setDraft] = (0, import_react10.useState)(value);
18418
+ function LinkModal({
18419
+ href,
18420
+ linkTarget,
18421
+ onClose,
18422
+ onSave
18423
+ }) {
18424
+ const [draft, setDraft] = (0, import_react10.useState)(href);
18425
+ const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
18309
18426
  const [invalid, setInvalid] = (0, import_react10.useState)(false);
18310
- (0, import_react10.useEffect)(() => {
18311
- setDraft(value);
18312
- setInvalid(false);
18313
- }, [value]);
18314
- const commit = () => {
18427
+ const save = () => {
18315
18428
  const next = draft.trim();
18316
- if (next === value) return setInvalid(false);
18317
- if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
18318
- setInvalid(false);
18319
- onCommit(next);
18320
- };
18321
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18322
- "div",
18323
- {
18324
- style: {
18325
- display: "flex",
18326
- alignItems: "center",
18327
- gap: 6,
18328
- height: 28,
18329
- padding: "0 12px",
18330
- borderRadius: 999,
18331
- background: "#ffffff",
18332
- width: "100%",
18333
- maxWidth: 260,
18334
- boxSizing: "border-box",
18335
- boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
18336
- },
18337
- children: [
18338
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18339
- "span",
18340
- {
18341
- style: {
18342
- flex: "0 0 auto",
18343
- color: OHW_BLUE_DARK,
18344
- fontSize: 10,
18345
- fontWeight: 700,
18346
- textTransform: "uppercase",
18347
- letterSpacing: 0.4
18348
- },
18349
- children: "Link"
18350
- }
18351
- ),
18352
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18353
- "input",
18354
- {
18355
- type: "text",
18356
- inputMode: "url",
18357
- value: draft,
18358
- onChange: (e) => setDraft(e.target.value),
18359
- onBlur: commit,
18360
- onKeyDown: (e) => {
18361
- if (e.key === "Enter") e.currentTarget.blur();
18362
- if (e.key === "Escape") {
18363
- setDraft(value);
18364
- setInvalid(false);
18365
- e.currentTarget.blur();
18366
- }
18367
- },
18368
- placeholder: "https://example.com",
18369
- "aria-label": "Image link URL",
18370
- "aria-invalid": invalid,
18371
- style: {
18372
- flex: 1,
18373
- minWidth: 0,
18374
- border: "none",
18375
- outline: "none",
18376
- background: "transparent",
18377
- fontSize: 12,
18378
- lineHeight: "16px",
18379
- fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18380
- color: "#1f2937"
18381
- }
18382
- }
18383
- )
18384
- ]
18429
+ if (next && !HTTP_URL_RE.test(next)) {
18430
+ setInvalid(true);
18431
+ return;
18385
18432
  }
18386
- );
18433
+ onSave(next, newTab ? "new-tab" : "same-tab");
18434
+ onClose();
18435
+ };
18436
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
18437
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
18438
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col gap-3", children: [
18439
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18440
+ "input",
18441
+ {
18442
+ autoFocus: true,
18443
+ type: "text",
18444
+ inputMode: "url",
18445
+ value: draft,
18446
+ onChange: (e) => {
18447
+ setDraft(e.target.value);
18448
+ setInvalid(false);
18449
+ },
18450
+ onKeyDown: (e) => {
18451
+ if (e.key === "Enter") save();
18452
+ },
18453
+ placeholder: "https://example.com",
18454
+ "aria-label": "Image link URL",
18455
+ "aria-invalid": invalid,
18456
+ className: cn(
18457
+ "h-9 w-full rounded-[6px] border border-border bg-background px-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/70 focus:border-bridge-primary",
18458
+ invalid && "border-destructive focus:border-destructive"
18459
+ )
18460
+ }
18461
+ ),
18462
+ invalid && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
18463
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
18464
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
18465
+ "Open in new tab"
18466
+ ] })
18467
+ ] }),
18468
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogFooter, { children: [
18469
+ href && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18470
+ "button",
18471
+ {
18472
+ type: "button",
18473
+ onClick: () => {
18474
+ onSave("", "new-tab");
18475
+ onClose();
18476
+ },
18477
+ className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
18478
+ children: "Remove link"
18479
+ }
18480
+ ),
18481
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18482
+ "button",
18483
+ {
18484
+ type: "button",
18485
+ onClick: onClose,
18486
+ className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
18487
+ children: "Cancel"
18488
+ }
18489
+ ),
18490
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18491
+ "button",
18492
+ {
18493
+ type: "button",
18494
+ onClick: save,
18495
+ className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
18496
+ children: "Save"
18497
+ }
18498
+ )
18499
+ ] })
18500
+ ] }) });
18387
18501
  }
18388
18502
  function MediaStripToolbar({
18389
18503
  entry,
18390
18504
  index,
18391
18505
  pending,
18392
18506
  error,
18507
+ itemEditOn,
18508
+ onToggleItemEdit,
18393
18509
  onSetVariant,
18394
18510
  onSetAlign,
18395
18511
  onSetHover,
18396
18512
  onSetTreatment,
18397
18513
  onResize,
18398
- onSetAlt,
18399
- onSetLink,
18514
+ onOpenLink,
18400
18515
  onAddItem,
18401
18516
  onRemoveItem,
18402
18517
  onChangeImage,
@@ -18405,81 +18520,102 @@ function MediaStripToolbar({
18405
18520
  }) {
18406
18521
  const items = mediaStripItems(entry.tree);
18407
18522
  const itemCount = items.length;
18408
- const currentAlt = items[index]?.alt ?? "";
18409
- const currentHref = items[index]?.href ?? "";
18410
- const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
18411
18523
  const slots = findMediaStripSlots(entry.tree);
18412
18524
  const variant = slots?.variant === "reel" ? "reel" : "logos";
18413
18525
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
18414
18526
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
18415
18527
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
18416
18528
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18417
- const isMobile = window.innerWidth <= 480;
18418
- const rowStyle = isMobile ? {
18419
- display: "flex",
18420
- gap: 6,
18421
- flexWrap: "nowrap",
18422
- justifyContent: "center",
18423
- overflowX: "auto",
18424
- WebkitOverflowScrolling: "touch",
18425
- width: "100%",
18426
- scrollbarWidth: "none"
18427
- } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
18428
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18529
+ const mobile = window.innerWidth <= 480;
18530
+ const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
18531
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18429
18532
  "div",
18430
18533
  {
18431
18534
  "data-ohw-bridge": "",
18432
18535
  "data-ohw-toolbar": "",
18433
18536
  onMouseDown: (e) => e.stopPropagation(),
18434
- style: {
18435
- display: "flex",
18436
- flexDirection: "column",
18437
- gap: 6,
18438
- alignItems: "center",
18439
- background: OHW_BLUE,
18440
- padding: 8,
18441
- borderRadius: 12,
18442
- boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
18443
- pointerEvents: pending ? "none" : "auto",
18444
- opacity: pending ? 0.6 : 1,
18445
- maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
18446
- },
18537
+ className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
18538
+ style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
18447
18539
  children: [
18448
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18449
- VARIANTS.map((v) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
18450
- ALIGNS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
18451
- ] }),
18452
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18453
- HOVERS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
18454
- TREATMENTS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
18455
- ] }),
18456
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
18457
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
18458
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
18459
- sizeValue,
18460
- "px"
18461
- ] }),
18462
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "+", onClick: () => onResize(8) })
18463
- ] }),
18464
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AltInput, { value: currentAlt, onCommit: onSetAlt }) }),
18465
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LinkInput, { value: currentHref, onCommit: (href) => onSetLink(href, currentTarget) }) }),
18466
- currentHref && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18467
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
18468
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
18469
- ] }),
18470
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18471
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "Change Image", onClick: onChangeImage }),
18472
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "+ Item", onClick: onAddItem }),
18473
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
18474
- ] }),
18475
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18476
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
18477
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
18478
- ] }),
18479
- error && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
18540
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18541
+ "div",
18542
+ {
18543
+ className: cn(
18544
+ "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
18545
+ "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
18546
+ mobile ? "flex-wrap justify-center" : "flex-nowrap"
18547
+ ),
18548
+ children: [
18549
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18550
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18551
+ CustomToolbarButton,
18552
+ {
18553
+ type: "button",
18554
+ "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
18555
+ "aria-pressed": itemEditOn,
18556
+ onClick: onToggleItemEdit,
18557
+ className: "group size-8",
18558
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18559
+ import_lucide_react9.Star,
18560
+ {
18561
+ size: 16,
18562
+ fill: "currentColor",
18563
+ strokeWidth: 0,
18564
+ className: cn(
18565
+ "transition-colors",
18566
+ itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
18567
+ ),
18568
+ style: { pointerEvents: "none" }
18569
+ }
18570
+ )
18571
+ }
18572
+ ) }),
18573
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
18574
+ ] }),
18575
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18576
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Image, { size: 16, style: { pointerEvents: "none" } }) }),
18577
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Link, { size: 16, style: { pointerEvents: "none" } }) }),
18578
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ArrowLeft, { size: 16, style: { pointerEvents: "none" } }) }),
18579
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18580
+ ToolbarAction,
18581
+ {
18582
+ label: gatedLabel("Move right"),
18583
+ onClick: onMoveRight,
18584
+ disabled: !itemEditOn || index >= itemCount - 1,
18585
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
18586
+ }
18587
+ ),
18588
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
18589
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18590
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
18591
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
18592
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18593
+ ToolbarAction,
18594
+ {
18595
+ label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
18596
+ active: treatment === "grayscale",
18597
+ onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
18598
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
18599
+ }
18600
+ ),
18601
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18602
+ ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
18603
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18604
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) }),
18605
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
18606
+ sizeValue,
18607
+ "px"
18608
+ ] }),
18609
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
18610
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18611
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
18612
+ ]
18613
+ }
18614
+ ),
18615
+ error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
18480
18616
  ]
18481
18617
  }
18482
- );
18618
+ ) });
18483
18619
  }
18484
18620
  function findMediaStripSlots(tree) {
18485
18621
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -18498,31 +18634,23 @@ function AiMediaStripOverlay({
18498
18634
  }) {
18499
18635
  const [hover, setHover] = (0, import_react10.useState)(null);
18500
18636
  const [active, setActive] = (0, import_react10.useState)(null);
18637
+ const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
18638
+ const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
18501
18639
  const [pending, setPending] = (0, import_react10.useState)(false);
18502
18640
  const [error, setError] = (0, import_react10.useState)(null);
18503
18641
  const activeRef = (0, import_react10.useRef)(null);
18504
18642
  activeRef.current = active;
18643
+ const linkOpenRef = (0, import_react10.useRef)(false);
18644
+ linkOpenRef.current = linkOpen;
18505
18645
  (0, import_react10.useEffect)(() => {
18506
18646
  const onMove = (e) => {
18647
+ const ref = itemRefFromTarget(e.target);
18507
18648
  const t = e.target;
18508
- if (!(t instanceof HTMLElement)) return;
18509
- if (t.closest("[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review]")) {
18510
- return;
18511
- }
18512
- const itemEl = t.closest("[data-ohw-media-strip-item]");
18513
- if (!itemEl) {
18514
- setHover(null);
18515
- return;
18516
- }
18517
- const sectionEl = itemEl.closest("[data-ohw-section]");
18518
- const unit = sectionEl ? movableUnit(sectionEl) : null;
18519
- const sectionId = unit ? instanceIdOf(unit) || null : null;
18520
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
18521
- if (!sectionId || Number.isNaN(index)) {
18522
- setHover(null);
18523
- return;
18524
- }
18525
- setHover({ sectionId, index });
18649
+ const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
18650
+ setHover((prev) => {
18651
+ const next = ref && !overEditable ? ref : null;
18652
+ return sameRef(prev, next) ? prev : next;
18653
+ });
18526
18654
  };
18527
18655
  const onLeave = () => setHover(null);
18528
18656
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -18534,23 +18662,25 @@ function AiMediaStripOverlay({
18534
18662
  }, []);
18535
18663
  (0, import_react10.useEffect)(() => {
18536
18664
  const onClick = (e) => {
18537
- const current = activeRef.current;
18538
- if (!current) return;
18539
18665
  const t = e.target;
18540
- if (!(t instanceof HTMLElement)) return;
18541
- if (t.closest("[data-ohw-item-interaction]")) return;
18542
- const itemEl = findMediaStripItemElement(current.sectionId, current.index);
18543
- if (itemEl && itemEl.contains(t)) return;
18544
- setActive(null);
18666
+ if (!(t instanceof Element)) return;
18667
+ if (t.closest(CHROME_SELECTOR)) return;
18668
+ const ref = itemRefFromTarget(t);
18669
+ if (sameRef(activeRef.current, ref)) return;
18670
+ if (ref) clearInteractionChromeRef.current();
18671
+ setItemEditOn(false);
18545
18672
  setError(null);
18673
+ setActive(ref);
18546
18674
  };
18547
18675
  document.addEventListener("click", onClick, true);
18548
18676
  return () => document.removeEventListener("click", onClick, true);
18549
- }, []);
18677
+ }, [clearInteractionChromeRef]);
18550
18678
  (0, import_react10.useEffect)(() => {
18551
18679
  const onBlur = () => {
18552
18680
  if (!activeRef.current) return;
18553
18681
  setActive(null);
18682
+ setItemEditOn(false);
18683
+ setLinkOpen(false);
18554
18684
  setError(null);
18555
18685
  };
18556
18686
  window.addEventListener("blur", onBlur);
@@ -18560,7 +18690,10 @@ function AiMediaStripOverlay({
18560
18690
  const onKeyDown = (e) => {
18561
18691
  if (e.key !== "Escape") return;
18562
18692
  if (!activeRef.current) return;
18693
+ if (linkOpenRef.current) return;
18694
+ if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
18563
18695
  setActive(null);
18696
+ setItemEditOn(false);
18564
18697
  setError(null);
18565
18698
  };
18566
18699
  document.addEventListener("keydown", onKeyDown);
@@ -18589,142 +18722,138 @@ function AiMediaStripOverlay({
18589
18722
  }
18590
18723
  applyTreeLocalRef.current?.({
18591
18724
  id: sectionId,
18592
- mode: "replace",
18593
- targetSectionId: sectionId,
18594
- tree: nextTree,
18595
- label: entry.label,
18596
- media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
18597
- ...entry.brand ? { brand: entry.brand } : {}
18598
- });
18599
- return true;
18600
- },
18601
- [findEntry, postToParent2, applyTreeLocalRef]
18602
- );
18603
- const activeEntry = active ? findEntry(active.sectionId) : null;
18604
- const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
18605
- const activeItemRect = useLiveItemRect(active);
18606
- const TOOLBAR_APPROX_H = 170;
18607
- const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
18608
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
18609
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18610
- "button",
18611
- {
18612
- type: "button",
18613
- "data-ohw-toolbar": "",
18614
- onClick: () => {
18615
- clearInteractionChromeRef.current();
18616
- setActive(hover);
18617
- },
18618
- "aria-label": "Configure media item",
18619
- style: {
18620
- position: "fixed",
18621
- top: hoverHandleRect.top + 8,
18622
- left: hoverHandleRect.left + hoverHandleRect.width - 36,
18623
- width: 28,
18624
- height: 28,
18625
- borderRadius: 8,
18626
- border: `1.5px dashed ${PRIMARY3}`,
18627
- background: "#ffffff",
18628
- color: PRIMARY3,
18629
- display: "flex",
18630
- alignItems: "center",
18631
- justifyContent: "center",
18632
- cursor: "pointer",
18633
- zIndex: 2147483643,
18634
- fontSize: 14,
18635
- fontWeight: 700
18636
- },
18637
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Star, { size: 14, style: { pointerEvents: "none" } })
18638
- }
18639
- ),
18640
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18641
- ItemInteractionLayer,
18642
- {
18643
- rect: activeItemRect,
18644
- state: toolbarState,
18645
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18646
- MediaStripToolbar,
18647
- {
18648
- entry: activeEntry,
18649
- index: active.index,
18650
- pending,
18651
- error,
18652
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
18653
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
18654
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
18655
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
18656
- onResize: (delta) => {
18657
- const slots = findMediaStripSlots(activeEntry.tree);
18658
- const variant = slots?.variant === "reel" ? "reel" : "logos";
18659
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18660
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
18661
- },
18662
- onSetAlt: (alt) => {
18663
- void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
18664
- },
18665
- onSetLink: (href, linkTarget) => {
18725
+ mode: "replace",
18726
+ targetSectionId: sectionId,
18727
+ tree: nextTree,
18728
+ label: entry.label,
18729
+ media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
18730
+ ...entry.brand ? { brand: entry.brand } : {},
18731
+ // The user is already looking at the strip they just edited — recentering the viewport
18732
+ // on every edit reads as the section jumping around.
18733
+ skipScrollIntoView: true
18734
+ });
18735
+ return true;
18736
+ },
18737
+ [findEntry, postToParent2, applyTreeLocalRef]
18738
+ );
18739
+ const activeEntry = active ? findEntry(active.sectionId) : null;
18740
+ const activeItemRect = useLiveItemRect(active);
18741
+ const activeStripRect = useLiveItemRect(active, true);
18742
+ const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
18743
+ const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
18744
+ const activeItem = active ? activeItems[active.index] : void 0;
18745
+ const activeHref = activeItem?.href ?? "";
18746
+ const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
18747
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
18748
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
18749
+ activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
18750
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
18751
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18752
+ MediaStripToolbar,
18753
+ {
18754
+ entry: activeEntry,
18755
+ index: active.index,
18756
+ pending,
18757
+ error,
18758
+ itemEditOn,
18759
+ onToggleItemEdit: () => setItemEditOn((on) => !on),
18760
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
18761
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
18762
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
18763
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
18764
+ onResize: (delta) => {
18765
+ const slots = findMediaStripSlots(activeEntry.tree);
18766
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
18767
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18768
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
18769
+ },
18770
+ onOpenLink: () => setLinkOpen(true),
18771
+ onAddItem: () => {
18772
+ void requestAssetPick(postToParent2).then((picked) => {
18773
+ if (!picked) return;
18774
+ const item = { assetId: picked.assetId, alt: "Logo" };
18666
18775
  void runMutation(
18667
18776
  active.sectionId,
18668
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
18669
- );
18670
- },
18671
- onAddItem: () => {
18672
- void requestAssetPick(postToParent2).then((picked) => {
18673
- if (!picked) return;
18674
- const item = { assetId: picked.assetId, alt: "Logo" };
18675
- void runMutation(
18676
- active.sectionId,
18677
- (tree) => addMediaStripItem(tree, item, active.index),
18678
- { [picked.assetId]: picked.url }
18679
- ).then((applied) => {
18680
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
18681
- });
18682
- });
18683
- },
18684
- onChangeImage: () => {
18685
- void requestAssetPick(postToParent2).then((picked) => {
18686
- if (!picked) return;
18687
- void runMutation(
18688
- active.sectionId,
18689
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
18690
- { [picked.assetId]: picked.url }
18691
- );
18777
+ (tree) => addMediaStripItem(tree, item, active.index),
18778
+ { [picked.assetId]: picked.url }
18779
+ ).then((applied) => {
18780
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
18692
18781
  });
18693
- },
18694
- onRemoveItem: () => {
18695
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
18696
- (applied) => {
18697
- if (applied) setActive(null);
18698
- }
18699
- );
18700
- },
18701
- onMoveLeft: () => {
18702
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
18703
- (applied) => {
18704
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
18705
- }
18706
- );
18707
- },
18708
- onMoveRight: () => {
18709
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
18710
- (applied) => {
18711
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
18712
- }
18782
+ });
18783
+ },
18784
+ onChangeImage: () => {
18785
+ void requestAssetPick(postToParent2).then((picked) => {
18786
+ if (!picked) return;
18787
+ void runMutation(
18788
+ active.sectionId,
18789
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
18790
+ { [picked.assetId]: picked.url }
18713
18791
  );
18714
- }
18792
+ });
18793
+ },
18794
+ onRemoveItem: () => {
18795
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
18796
+ (applied) => {
18797
+ if (applied) setActive(null);
18798
+ }
18799
+ );
18800
+ },
18801
+ onMoveLeft: () => {
18802
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
18803
+ (applied) => {
18804
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
18805
+ }
18806
+ );
18807
+ },
18808
+ onMoveRight: () => {
18809
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
18810
+ (applied) => {
18811
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
18812
+ }
18813
+ );
18715
18814
  }
18716
- )
18717
- }
18718
- )
18815
+ }
18816
+ ) }),
18817
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18818
+ "div",
18819
+ {
18820
+ "data-ohw-bridge": "",
18821
+ "data-ohw-item-interaction": "",
18822
+ "aria-hidden": true,
18823
+ className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
18824
+ style: {
18825
+ top: activeItemRect.top + activeItemRect.height + 10,
18826
+ left: activeItemRect.left - 5,
18827
+ background: PRIMARY3,
18828
+ zIndex: 2147483644
18829
+ },
18830
+ children: "Media item"
18831
+ }
18832
+ ),
18833
+ linkOpen && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18834
+ LinkModal,
18835
+ {
18836
+ href: activeHref,
18837
+ linkTarget: activeTarget,
18838
+ onClose: () => setLinkOpen(false),
18839
+ onSave: (href, linkTarget) => {
18840
+ void runMutation(
18841
+ active.sectionId,
18842
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
18843
+ );
18844
+ }
18845
+ }
18846
+ )
18847
+ ] })
18719
18848
  ] });
18720
18849
  }
18721
18850
 
18722
18851
  // src/ui/ai-section/AiPricingCardOverlay.tsx
18723
18852
  var import_react11 = require("react");
18724
- var import_lucide_react9 = require("lucide-react");
18725
- var import_jsx_runtime20 = require("react/jsx-runtime");
18853
+ var import_lucide_react10 = require("lucide-react");
18854
+ var import_jsx_runtime21 = require("react/jsx-runtime");
18726
18855
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
18727
- var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
18856
+ var CHROME_SELECTOR2 = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
18728
18857
  function findPricingCardElement(sectionId, index) {
18729
18858
  const sectionEl = findByInstanceId(sectionId);
18730
18859
  if (!sectionEl) return null;
@@ -18736,7 +18865,7 @@ function findPricingCardElement(sectionId, index) {
18736
18865
  }
18737
18866
  function cardRefFromTarget(target) {
18738
18867
  if (!(target instanceof Element)) return null;
18739
- if (target.closest(CHROME_SELECTOR)) return null;
18868
+ if (target.closest(CHROME_SELECTOR2)) return null;
18740
18869
  const cardEl = target.closest("[data-ohw-pricing-card]");
18741
18870
  if (!cardEl) return null;
18742
18871
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -18746,21 +18875,21 @@ function cardRefFromTarget(target) {
18746
18875
  if (!sectionId || Number.isNaN(index)) return null;
18747
18876
  return { sectionId, index };
18748
18877
  }
18749
- function sameRef(a, b) {
18878
+ function sameRef2(a, b) {
18750
18879
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
18751
18880
  }
18752
- function rectOf(el) {
18881
+ function rectOf2(el) {
18753
18882
  if (!el) return null;
18754
18883
  const r2 = el.getBoundingClientRect();
18755
18884
  if (r2.width <= 0 || r2.height <= 0) return null;
18756
18885
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
18757
18886
  }
18758
18887
  function readRect3(ref) {
18759
- return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
18888
+ return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
18760
18889
  }
18761
18890
  function readGridRect(ref) {
18762
18891
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
18763
- return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
18892
+ return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
18764
18893
  }
18765
18894
  function useLiveCardRect(ref, grid = false) {
18766
18895
  const [rect, setRect] = (0, import_react11.useState)(null);
@@ -18790,7 +18919,7 @@ function useLiveCardRect(ref, grid = false) {
18790
18919
  }
18791
18920
  var TOOLBAR_GRID_GAP = 52;
18792
18921
  var TOOLBAR_GRID_GAP_MOBILE = 42;
18793
- var TOOLBAR_EDGE_MARGIN2 = 4;
18922
+ var TOOLBAR_EDGE_MARGIN3 = 4;
18794
18923
  function GridToolbarSlot({ gridRect, children }) {
18795
18924
  const slotRef = (0, import_react11.useRef)(null);
18796
18925
  const [shift, setShift] = (0, import_react11.useState)(0);
@@ -18802,7 +18931,7 @@ function GridToolbarSlot({ gridRect, children }) {
18802
18931
  const w = slot.offsetWidth;
18803
18932
  if (w <= 0) return;
18804
18933
  const half = w / 2;
18805
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
18934
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
18806
18935
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
18807
18936
  };
18808
18937
  clamp();
@@ -18811,7 +18940,7 @@ function GridToolbarSlot({ gridRect, children }) {
18811
18940
  return () => ro.disconnect();
18812
18941
  }, [centerX, children]);
18813
18942
  const mobile = window.innerWidth <= 480;
18814
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18943
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18815
18944
  "div",
18816
18945
  {
18817
18946
  ref: slotRef,
@@ -18846,7 +18975,7 @@ function requestValidateTree2(postToParent2, tree) {
18846
18975
  });
18847
18976
  }
18848
18977
  function StyleSwatch({ style }) {
18849
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18978
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18850
18979
  "span",
18851
18980
  {
18852
18981
  "aria-hidden": true,
@@ -18860,35 +18989,35 @@ function StyleSwatch({ style }) {
18860
18989
  );
18861
18990
  }
18862
18991
  function ButtonGlyph() {
18863
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
18864
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
18865
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
18992
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
18993
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
18994
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
18866
18995
  ] });
18867
18996
  }
18868
18997
  function FeatureGlyph({ mode }) {
18869
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
18870
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
18871
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
18872
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
18873
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
18874
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
18875
- mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
18998
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
18999
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19000
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19001
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19002
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19003
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19004
+ mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
18876
19005
  ] });
18877
19006
  }
18878
- function ToolbarAction({
19007
+ function ToolbarAction2({
18879
19008
  label,
18880
19009
  onClick,
18881
19010
  disabled = false,
18882
19011
  children
18883
19012
  }) {
18884
- const button = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
18885
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18886
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: button }) : button }),
18887
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19013
+ const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19014
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19015
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19016
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
18888
19017
  ] });
18889
19018
  }
18890
- function ToolbarSeparator() {
18891
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19019
+ function ToolbarSeparator2() {
19020
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
18892
19021
  }
18893
19022
  function CardStyleMenu({
18894
19023
  value,
@@ -18896,7 +19025,7 @@ function CardStyleMenu({
18896
19025
  disabled = false
18897
19026
  }) {
18898
19027
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
18899
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19028
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18900
19029
  "button",
18901
19030
  {
18902
19031
  type: "button",
@@ -18907,28 +19036,28 @@ function CardStyleMenu({
18907
19036
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
18908
19037
  ),
18909
19038
  children: [
18910
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: current.value }),
18911
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18912
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19039
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: current.value }),
19040
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19041
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18913
19042
  ]
18914
19043
  }
18915
19044
  );
18916
19045
  if (disabled) {
18917
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18918
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
18919
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18920
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: trigger }) }),
18921
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19046
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19047
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19048
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19049
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: trigger }) }),
19050
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
18922
19051
  ] })
18923
19052
  ] });
18924
19053
  }
18925
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18926
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
18927
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenu, { modal: false, children: [
18928
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
18929
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19054
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19055
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19056
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19057
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19058
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
18930
19059
  const selected = style.value === value;
18931
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19060
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18932
19061
  DropdownMenuItem,
18933
19062
  {
18934
19063
  role: "menuitemradio",
@@ -18936,9 +19065,9 @@ function CardStyleMenu({
18936
19065
  onSelect: () => onChange(style.value),
18937
19066
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
18938
19067
  children: [
18939
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: style.value }),
18940
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: style.label }),
18941
- selected && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19068
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: style.value }),
19069
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: style.label }),
19070
+ selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
18942
19071
  ]
18943
19072
  },
18944
19073
  style.value
@@ -18968,7 +19097,7 @@ function PricingCardToolbar({
18968
19097
  const style = pricingCardStyle(entry.tree, index);
18969
19098
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
18970
19099
  const mobile = window.innerWidth <= 480;
18971
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19100
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18972
19101
  "div",
18973
19102
  {
18974
19103
  "data-ohw-bridge": "",
@@ -18980,7 +19109,7 @@ function PricingCardToolbar({
18980
19109
  ),
18981
19110
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
18982
19111
  children: [
18983
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19112
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18984
19113
  "div",
18985
19114
  {
18986
19115
  className: cn(
@@ -18989,8 +19118,8 @@ function PricingCardToolbar({
18989
19118
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
18990
19119
  ),
18991
19120
  children: [
18992
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18993
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19121
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19122
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18994
19123
  CustomToolbarButton,
18995
19124
  {
18996
19125
  type: "button",
@@ -18998,8 +19127,8 @@ function PricingCardToolbar({
18998
19127
  "aria-pressed": featuredTag,
18999
19128
  onClick: () => onSetFeaturedTag(!featuredTag),
19000
19129
  className: "group size-8",
19001
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19002
- import_lucide_react9.Star,
19130
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19131
+ import_lucide_react10.Star,
19003
19132
  {
19004
19133
  size: 16,
19005
19134
  fill: "currentColor",
@@ -19013,25 +19142,25 @@ function PricingCardToolbar({
19013
19142
  )
19014
19143
  }
19015
19144
  ) }),
19016
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19145
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19017
19146
  ] }),
19018
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
19019
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19020
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
19021
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}) }),
19022
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "relative inline-flex", children: [
19023
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}),
19024
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19147
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator2, {}),
19148
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19149
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator2, {}),
19150
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}) }),
19151
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "relative inline-flex", children: [
19152
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}),
19153
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19025
19154
  ] }) }),
19026
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(FeatureGlyph, { mode: "add" }) }),
19027
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(FeatureGlyph, { mode: "remove" }) }),
19028
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
19029
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19030
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19155
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "add" }) }),
19156
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "remove" }) }),
19157
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator2, {}),
19158
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19159
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19031
19160
  ]
19032
19161
  }
19033
19162
  ),
19034
- error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19163
+ error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19035
19164
  ]
19036
19165
  }
19037
19166
  ) });
@@ -19065,7 +19194,7 @@ function AiPricingCardOverlay({
19065
19194
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
19066
19195
  setHover((prev) => {
19067
19196
  const next = ref && !overEditable ? ref : null;
19068
- return sameRef(prev, next) ? prev : next;
19197
+ return sameRef2(prev, next) ? prev : next;
19069
19198
  });
19070
19199
  };
19071
19200
  const onLeave = () => setHover(null);
@@ -19080,10 +19209,10 @@ function AiPricingCardOverlay({
19080
19209
  const onClick = (e) => {
19081
19210
  const t = e.target;
19082
19211
  if (!(t instanceof Element)) return;
19083
- if (t.closest(CHROME_SELECTOR)) return;
19212
+ if (t.closest(CHROME_SELECTOR2)) return;
19084
19213
  const ref = cardRefFromTarget(t);
19085
- setActive((prev) => sameRef(prev, ref) ? prev : ref);
19086
- if (!ref || !sameRef(activeRef.current, ref)) setError(null);
19214
+ setActive((prev) => sameRef2(prev, ref) ? prev : ref);
19215
+ if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19087
19216
  };
19088
19217
  document.addEventListener("click", onClick, true);
19089
19218
  return () => document.removeEventListener("click", onClick, true);
@@ -19148,12 +19277,12 @@ function AiPricingCardOverlay({
19148
19277
  const activeEntry = active ? findEntry(active.sectionId) : null;
19149
19278
  const activeCardRect = useLiveCardRect(active);
19150
19279
  const activeGridRect = useLiveCardRect(active, true);
19151
- const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19152
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
19153
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19154
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
19155
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19156
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19280
+ const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
19281
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19282
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19283
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19284
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19285
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19157
19286
  PricingCardToolbar,
19158
19287
  {
19159
19288
  entry: activeEntry,
@@ -19181,7 +19310,7 @@ function AiPricingCardOverlay({
19181
19310
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
19182
19311
  }
19183
19312
  ) }),
19184
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19313
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19185
19314
  "div",
19186
19315
  {
19187
19316
  "data-ohw-bridge": "",
@@ -19203,7 +19332,7 @@ function AiPricingCardOverlay({
19203
19332
 
19204
19333
  // src/ui/ai-section/AiVideoOverlay.tsx
19205
19334
  var import_react12 = require("react");
19206
- var import_lucide_react10 = require("lucide-react");
19335
+ var import_lucide_react11 = require("lucide-react");
19207
19336
 
19208
19337
  // src/lib/ai-video-tree.ts
19209
19338
  function resolveVideoBlock(tree, path) {
@@ -19274,10 +19403,10 @@ function extractYouTubeId(input) {
19274
19403
  }
19275
19404
 
19276
19405
  // src/ui/ai-section/AiVideoOverlay.tsx
19277
- var import_jsx_runtime21 = require("react/jsx-runtime");
19406
+ var import_jsx_runtime22 = require("react/jsx-runtime");
19278
19407
  var PRIMARY5 = "#0885FE";
19279
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
19280
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19408
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
19409
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19281
19410
  function findVideoBlockElement(sectionId, path) {
19282
19411
  const sectionEl = findByInstanceId(sectionId);
19283
19412
  if (!sectionEl) return null;
@@ -19337,12 +19466,12 @@ function requestValidateTree3(postToParent2, tree) {
19337
19466
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
19338
19467
  });
19339
19468
  }
19340
- function Pill2({
19469
+ function Pill({
19341
19470
  label,
19342
19471
  onClick,
19343
19472
  disabled
19344
19473
  }) {
19345
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19474
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19346
19475
  "button",
19347
19476
  {
19348
19477
  type: "button",
@@ -19358,7 +19487,7 @@ function Pill2({
19358
19487
  border: "none",
19359
19488
  cursor: disabled ? "default" : "pointer",
19360
19489
  background: "#ffffff",
19361
- color: OHW_BLUE_DARK2,
19490
+ color: OHW_BLUE_DARK,
19362
19491
  opacity: disabled ? 0.4 : 1,
19363
19492
  fontSize: 12,
19364
19493
  lineHeight: "16px",
@@ -19401,8 +19530,8 @@ function VideoUrlInput({ value, onCommit }) {
19401
19530
  onCommit(id);
19402
19531
  }
19403
19532
  };
19404
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
19405
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19533
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
19534
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19406
19535
  "div",
19407
19536
  {
19408
19537
  style: {
@@ -19418,12 +19547,12 @@ function VideoUrlInput({ value, onCommit }) {
19418
19547
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
19419
19548
  },
19420
19549
  children: [
19421
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19550
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19422
19551
  "span",
19423
19552
  {
19424
19553
  style: {
19425
19554
  flex: "0 0 auto",
19426
- color: OHW_BLUE_DARK2,
19555
+ color: OHW_BLUE_DARK,
19427
19556
  fontSize: 10,
19428
19557
  lineHeight: "16px",
19429
19558
  fontWeight: 700,
@@ -19433,7 +19562,7 @@ function VideoUrlInput({ value, onCommit }) {
19433
19562
  children: "Video"
19434
19563
  }
19435
19564
  ),
19436
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19565
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19437
19566
  "input",
19438
19567
  {
19439
19568
  type: "text",
@@ -19470,7 +19599,7 @@ function VideoUrlInput({ value, onCommit }) {
19470
19599
  ]
19471
19600
  }
19472
19601
  ),
19473
- invalid && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
19602
+ invalid && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
19474
19603
  ] });
19475
19604
  }
19476
19605
  function VideoToolbar({
@@ -19480,7 +19609,7 @@ function VideoToolbar({
19480
19609
  onSetVideoId,
19481
19610
  onRemove
19482
19611
  }) {
19483
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19612
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19484
19613
  "div",
19485
19614
  {
19486
19615
  "data-ohw-bridge": "",
@@ -19492,7 +19621,7 @@ function VideoToolbar({
19492
19621
  gap: 6,
19493
19622
  alignItems: "center",
19494
19623
  width: 280,
19495
- background: OHW_BLUE2,
19624
+ background: OHW_BLUE,
19496
19625
  padding: 8,
19497
19626
  borderRadius: 12,
19498
19627
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -19500,9 +19629,9 @@ function VideoToolbar({
19500
19629
  opacity: pending ? 0.6 : 1
19501
19630
  },
19502
19631
  children: [
19503
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
19504
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
19505
- error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19632
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
19633
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
19634
+ error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19506
19635
  ]
19507
19636
  }
19508
19637
  );
@@ -19622,8 +19751,8 @@ function AiVideoOverlay({
19622
19751
  const activeBlockRect = useLiveBlockRect(active);
19623
19752
  const TOOLBAR_APPROX_H = 90;
19624
19753
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
19625
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19626
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19754
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19755
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19627
19756
  "button",
19628
19757
  {
19629
19758
  type: "button",
@@ -19651,15 +19780,15 @@ function AiVideoOverlay({
19651
19780
  fontSize: 14,
19652
19781
  fontWeight: 700
19653
19782
  },
19654
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Star, { size: 14, style: { pointerEvents: "none" } })
19783
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
19655
19784
  }
19656
19785
  ),
19657
- activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19786
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19658
19787
  ItemInteractionLayer,
19659
19788
  {
19660
19789
  rect: activeBlockRect,
19661
19790
  state: toolbarState,
19662
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19791
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19663
19792
  VideoToolbar,
19664
19793
  {
19665
19794
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -19683,7 +19812,7 @@ function AiVideoOverlay({
19683
19812
 
19684
19813
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
19685
19814
  var import_react13 = require("react");
19686
- var import_lucide_react11 = require("lucide-react");
19815
+ var import_lucide_react12 = require("lucide-react");
19687
19816
 
19688
19817
  // src/lib/ai-carousel-tree.ts
19689
19818
  function isCarouselBlock(node) {
@@ -19734,10 +19863,10 @@ function setCarouselAutoplay(tree, on) {
19734
19863
  }
19735
19864
 
19736
19865
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
19737
- var import_jsx_runtime22 = require("react/jsx-runtime");
19866
+ var import_jsx_runtime23 = require("react/jsx-runtime");
19738
19867
  var PRIMARY6 = "#0885FE";
19739
- var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
19740
- var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
19868
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
19869
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
19741
19870
  function findCarouselItemElement(sectionId, index) {
19742
19871
  const sectionEl = findByInstanceId(sectionId);
19743
19872
  if (!sectionEl) return null;
@@ -19816,8 +19945,8 @@ function requestAssetPick2(postToParent2) {
19816
19945
  postToParent2({ type: "ow:asset-pick", id });
19817
19946
  });
19818
19947
  }
19819
- function Pill3({ label, onClick, disabled }) {
19820
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19948
+ function Pill2({ label, onClick, disabled }) {
19949
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19821
19950
  "button",
19822
19951
  {
19823
19952
  type: "button",
@@ -19833,7 +19962,7 @@ function Pill3({ label, onClick, disabled }) {
19833
19962
  border: "none",
19834
19963
  cursor: disabled ? "default" : "pointer",
19835
19964
  background: "#ffffff",
19836
- color: OHW_BLUE_DARK3,
19965
+ color: OHW_BLUE_DARK2,
19837
19966
  opacity: disabled ? 0.4 : 1,
19838
19967
  fontSize: 12,
19839
19968
  lineHeight: "16px",
@@ -19851,7 +19980,7 @@ function TogglePill({
19851
19980
  onToggle,
19852
19981
  disabled
19853
19982
  }) {
19854
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19983
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
19855
19984
  "button",
19856
19985
  {
19857
19986
  type: "button",
@@ -19870,7 +19999,7 @@ function TogglePill({
19870
19999
  border: "none",
19871
20000
  cursor: disabled ? "default" : "pointer",
19872
20001
  background: "#ffffff",
19873
- color: OHW_BLUE_DARK3,
20002
+ color: OHW_BLUE_DARK2,
19874
20003
  opacity: disabled ? 0.4 : 1,
19875
20004
  fontSize: 12,
19876
20005
  lineHeight: "16px",
@@ -19880,7 +20009,7 @@ function TogglePill({
19880
20009
  },
19881
20010
  children: [
19882
20011
  label,
19883
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20012
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19884
20013
  "span",
19885
20014
  {
19886
20015
  "aria-hidden": true,
@@ -19893,10 +20022,10 @@ function TogglePill({
19893
20022
  // Off is a grey track, not a washed-out blue: the pill's own white background already
19894
20023
  // carries the brand colour in the text, and a pale blue track next to it read as
19895
20024
  // "disabled control" rather than "switch that is off".
19896
- background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
20025
+ background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
19897
20026
  transition: "background 120ms ease"
19898
20027
  },
19899
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20028
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19900
20029
  "span",
19901
20030
  {
19902
20031
  style: {
@@ -19925,7 +20054,7 @@ function CarouselSlideToolbar({
19925
20054
  onChangeImage,
19926
20055
  onToggleAutoplay
19927
20056
  }) {
19928
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20057
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
19929
20058
  "div",
19930
20059
  {
19931
20060
  "data-ohw-bridge": "",
@@ -19936,7 +20065,7 @@ function CarouselSlideToolbar({
19936
20065
  flexDirection: "column",
19937
20066
  gap: 6,
19938
20067
  alignItems: "center",
19939
- background: OHW_BLUE3,
20068
+ background: OHW_BLUE2,
19940
20069
  padding: 8,
19941
20070
  borderRadius: 12,
19942
20071
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -19948,11 +20077,11 @@ function CarouselSlideToolbar({
19948
20077
  pointerEvents: pending ? "none" : "auto"
19949
20078
  },
19950
20079
  children: [
19951
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
19952
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill3, { label: "Change Image", onClick: onChangeImage }),
19953
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20080
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20081
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
20082
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
19954
20083
  ] }),
19955
- error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20084
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19956
20085
  ]
19957
20086
  }
19958
20087
  );
@@ -20092,8 +20221,8 @@ function AiFadeCarouselOverlay({
20092
20221
  const activeItemRect = useLiveItemRect2(active);
20093
20222
  const TOOLBAR_APPROX_H = 90;
20094
20223
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20095
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
20096
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20224
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20225
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20097
20226
  "button",
20098
20227
  {
20099
20228
  type: "button",
@@ -20122,10 +20251,10 @@ function AiFadeCarouselOverlay({
20122
20251
  fontSize: 14,
20123
20252
  fontWeight: 700
20124
20253
  },
20125
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20254
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
20126
20255
  }
20127
20256
  ),
20128
- swapping && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20257
+ swapping && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20129
20258
  "div",
20130
20259
  {
20131
20260
  ref: skeletonRef,
@@ -20143,15 +20272,15 @@ function AiFadeCarouselOverlay({
20143
20272
  zIndex: 2147483646,
20144
20273
  pointerEvents: "none"
20145
20274
  },
20146
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("style", { children: MEDIA_SKELETON_CSS })
20275
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("style", { children: MEDIA_SKELETON_CSS })
20147
20276
  }
20148
20277
  ),
20149
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20278
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20150
20279
  ItemInteractionLayer,
20151
20280
  {
20152
20281
  rect: activeItemRect,
20153
20282
  state: toolbarState,
20154
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20283
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20155
20284
  CarouselSlideToolbar,
20156
20285
  {
20157
20286
  pending,
@@ -20504,113 +20633,6 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
20504
20633
  // src/ui/link-modal/LinkPopover.tsx
20505
20634
  var import_react17 = require("react");
20506
20635
 
20507
- // src/ui/dialog.tsx
20508
- var React9 = __toESM(require("react"), 1);
20509
- var import_radix_ui6 = require("radix-ui");
20510
- var import_lucide_react12 = require("lucide-react");
20511
- var import_jsx_runtime23 = require("react/jsx-runtime");
20512
- function Dialog2({
20513
- ...props
20514
- }) {
20515
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
20516
- }
20517
- function DialogPortal({
20518
- ...props
20519
- }) {
20520
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
20521
- }
20522
- function DialogOverlay({
20523
- className,
20524
- ...props
20525
- }) {
20526
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20527
- import_radix_ui6.Dialog.Overlay,
20528
- {
20529
- "data-slot": "dialog-overlay",
20530
- "data-ohw-link-modal-root": "",
20531
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
20532
- ...props
20533
- }
20534
- );
20535
- }
20536
- var DialogContent = React9.forwardRef(
20537
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
20538
- const positionMode = container ? "absolute" : "fixed";
20539
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DialogPortal, { container: container ?? void 0, children: [
20540
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
20541
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20542
- import_radix_ui6.Dialog.Content,
20543
- {
20544
- ref,
20545
- "data-slot": "dialog-content",
20546
- className: cn(
20547
- positionMode,
20548
- "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
20549
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
20550
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
20551
- className
20552
- ),
20553
- onMouseDown: (e) => e.stopPropagation(),
20554
- ...props,
20555
- children: [
20556
- children,
20557
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20558
- import_radix_ui6.Dialog.Close,
20559
- {
20560
- type: "button",
20561
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
20562
- "aria-label": "Close",
20563
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
20564
- }
20565
- ) : null
20566
- ]
20567
- }
20568
- )
20569
- ] });
20570
- }
20571
- );
20572
- DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
20573
- function DialogHeader({
20574
- className,
20575
- ...props
20576
- }) {
20577
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
20578
- }
20579
- function DialogFooter({
20580
- className,
20581
- ...props
20582
- }) {
20583
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20584
- "div",
20585
- {
20586
- className: cn("flex items-center justify-end gap-2", className),
20587
- ...props
20588
- }
20589
- );
20590
- }
20591
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20592
- import_radix_ui6.Dialog.Title,
20593
- {
20594
- ref,
20595
- className: cn(
20596
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
20597
- className
20598
- ),
20599
- ...props
20600
- }
20601
- ));
20602
- DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
20603
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20604
- import_radix_ui6.Dialog.Description,
20605
- {
20606
- ref,
20607
- className: cn("text-sm text-muted-foreground", className),
20608
- ...props
20609
- }
20610
- ));
20611
- DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
20612
- var DialogClose = import_radix_ui6.Dialog.Close;
20613
-
20614
20636
  // src/ui/link-modal/LinkEditorPanel.tsx
20615
20637
  var import_lucide_react16 = require("lucide-react");
20616
20638
 
@@ -24614,7 +24636,7 @@ function applyVisibleViewport(el, parentScroll) {
24614
24636
  el.style.top = `${top}px`;
24615
24637
  el.style.height = `${height}px`;
24616
24638
  }
24617
- var TOOLBAR_EDGE_MARGIN3 = 4;
24639
+ var TOOLBAR_EDGE_MARGIN4 = 4;
24618
24640
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
24619
24641
  const isAbove = transform.includes("translateY(-100%)");
24620
24642
  let visualTop = isAbove ? top - approxH : top;
@@ -24641,8 +24663,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
24641
24663
  function clampToolbarLeft(centerX, toolbarWidth) {
24642
24664
  const half = toolbarWidth / 2;
24643
24665
  return Math.max(
24644
- TOOLBAR_EDGE_MARGIN3 + half,
24645
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
24666
+ TOOLBAR_EDGE_MARGIN4 + half,
24667
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
24646
24668
  );
24647
24669
  }
24648
24670
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {