camox 0.56.0 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,7 +39,7 @@ function CanvasPageHeader({ page, pathname, selected, onSelect, onHoverChange, o
39
39
  }
40
40
  };
41
41
  const path = pathname ?? (page.kind === "derived" ? page.pattern : void 0);
42
- const pathClassName = "text-muted-foreground w-full min-w-0 truncate text-left text-xs font-normal";
42
+ const pathClassName = "text-muted-foreground max-w-full min-w-0 truncate text-left text-xs font-normal";
43
43
  const focusClassName = "focus-visible:ring-ring outline-none focus-visible:ring-2";
44
44
  const name = draft !== null ? /* @__PURE__ */ jsx("input", {
45
45
  "aria-label": "Page nickname",
@@ -4,6 +4,16 @@ import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle } from "../preview/components/previ
4
4
  //#region src/features/canvas/canvasFrameDocument.ts
5
5
  /** Freezes CSS animations, including ones that start while the frame is paused. */
6
6
  const PAUSED_ANIMATIONS_STYLE = `html[${PREVIEW_PAUSED_ATTRIBUTE}] *, html[${PREVIEW_PAUSED_ATTRIBUTE}] *::before, html[${PREVIEW_PAUSED_ATTRIBUTE}] *::after { animation-play-state: paused !important; }`;
7
+ /**
8
+ * A style of the studio's own for a frame's document, marked so that giving the frame a site
9
+ * document's stylesheets (`restyle`) leaves it.
10
+ */
11
+ function studioStyle(doc, css) {
12
+ const style = doc.createElement("style");
13
+ style.setAttribute("data-camox-studio", "");
14
+ style.textContent = css;
15
+ return style;
16
+ }
7
17
  /** Set by the camera on elements that hold Canvas frames: see `canvasBlockOverlap`. */
8
18
  const BLOCK_OVERLAP_ATTRIBUTE = "data-canvas-block-overlap";
9
19
  const BLOCK = "[data-camox-viewport-block]";
@@ -93,8 +103,7 @@ function createCanvasDocument(previewDocument, href) {
93
103
  doc.querySelector("[data-camox-preview-root]")?.replaceChildren();
94
104
  doc.documentElement.style.setProperty("overscroll-behavior", "none");
95
105
  doc.body.style.setProperty("overscroll-behavior", "none");
96
- const paused = doc.createElement("style");
97
- paused.textContent = PAUSED_ANIMATIONS_STYLE;
106
+ const paused = studioStyle(doc, PAUSED_ANIMATIONS_STYLE);
98
107
  doc.head.appendChild(paused);
99
108
  return `<!doctype html>${doc.documentElement.outerHTML}`;
100
109
  }
@@ -106,8 +115,7 @@ function createCanvasDocument(previewDocument, href) {
106
115
  */
107
116
  function adoptCanvasDocument(iframe, root, viewportHeight) {
108
117
  const doc = root.ownerDocument;
109
- const paused = doc.createElement("style");
110
- paused.textContent = PAUSED_ANIMATIONS_STYLE;
118
+ const paused = studioStyle(doc, PAUSED_ANIMATIONS_STYLE);
111
119
  doc.head.appendChild(paused);
112
120
  const restored = [doc.documentElement, doc.body].map((element) => ({
113
121
  element,
@@ -199,8 +207,7 @@ function observeCanvasDocument(iframe, root, viewportHeight, suspended = () => f
199
207
  doc.documentElement.style.setProperty("--camox-viewport-height", `${baseline}px`);
200
208
  doc.documentElement.style.setProperty("overflow", "hidden", "important");
201
209
  doc.body.style.setProperty("overflow", "hidden", "important");
202
- const overlapStyle = doc.createElement("style");
203
- overlapStyle.textContent = BLOCK_OVERLAP_STYLE;
210
+ const overlapStyle = studioStyle(doc, BLOCK_OVERLAP_STYLE);
204
211
  doc.head.appendChild(overlapStyle);
205
212
  const followOverlap = () => {
206
213
  const overlap = iframe.closest(`[${BLOCK_OVERLAP_ATTRIBUTE}]`);
@@ -5,7 +5,7 @@ import { PageViewportContext } from "../../runtime/pageViewport.js";
5
5
  import { PreviewDocumentContext } from "../../runtime/PreviewDocumentContext.js";
6
6
  import { ViewportContinuityContext, capturePreviewAnchor, restorePreviewAnchor } from "../viewportContinuity.js";
7
7
  import { PreviewActivationContext } from "./PreviewActivation.js";
8
- import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle } from "./previewStyles.js";
8
+ import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle, restyle } from "./previewStyles.js";
9
9
  import { c } from "react/compiler-runtime";
10
10
  import * as React from "react";
11
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -21,23 +21,6 @@ function useFrame() {
21
21
  if (!context) throw new Error("useFrame must be used within a Frame");
22
22
  return context;
23
23
  }
24
- /** The stylesheets of a document's head that are the site's: not the studio's. */
25
- const SITE_STYLESHEETS = "link[rel~=\"stylesheet\"], style";
26
- const siteStylesheetsOf = (head) => Array.from(head.querySelectorAll(SITE_STYLESHEETS)).filter((element) => element.parentElement === head && isSiteStyle(element));
27
- /**
28
- * Gives the frame's document the site stylesheets of `html`, a site document: the ones it no
29
- * longer has go, and its new ones are added, the rest of the frame left as it is.
30
- */
31
- function restyle(frame, html) {
32
- const doc = frame.document;
33
- const { DOMParser } = frame;
34
- const wanted = siteStylesheetsOf(new DOMParser().parseFromString(html, "text/html").head);
35
- const held = siteStylesheetsOf(doc.head);
36
- const wantedMarkup = new Set(wanted.map((element) => element.outerHTML));
37
- const heldMarkup = new Set(held.map((element) => element.outerHTML));
38
- for (const element of held) if (!wantedMarkup.has(element.outerHTML)) element.remove();
39
- for (const element of wanted) if (!heldMarkup.has(element.outerHTML)) doc.head.appendChild(doc.importNode(element, true));
40
- }
41
24
  function ClearServerMarkup(t0) {
42
25
  const $ = c(4);
43
26
  const { nodes } = t0;
@@ -64,16 +64,8 @@ const LEAVE_DURATION = 180;
64
64
  const SWAP_DURATION = 150;
65
65
  /** How long after each other cards laid at once land, front-most last. */
66
66
  const STAGGER = 25;
67
- const HEADER_TRANSITION = [...[
68
- "transform",
69
- "margin",
70
- "height",
71
- "padding"
72
- ].map((p) => `${p} ${DURATION}ms ${EASE}`), ...[
73
- "background-color",
74
- "box-shadow",
75
- "color"
76
- ].map((p) => `${p} ${DURATION}ms ease-out`)].join(", ");
67
+ /** How far any card shades at most, `stepSeries`' limit: a header's shade is a part of it. */
68
+ const MAX_SHADE = FIRST_STEP_SHADE / .15000000000000002;
77
69
  /** How far below the front a card is laid from, or pulled away to, in px. */
78
70
  const PULL = 24;
79
71
  const PLACED = {
@@ -312,6 +304,41 @@ function useStackCard(ref, depth, isView) {
312
304
  };
313
305
  }, []);
314
306
  }
307
+ /**
308
+ * Eases a header's card and content from where they were laid out before `shape` changed to
309
+ * where they are now, as its layout snaps: its card scales from its old size, and its content
310
+ * slides from its old height.
311
+ */
312
+ function useHeaderReshape(surfaceRef, contentRef, shape) {
313
+ const last = React.useRef(null);
314
+ const moving = React.useRef([]);
315
+ React.useLayoutEffect(() => {
316
+ const surface = surfaceRef.current;
317
+ const content = contentRef.current;
318
+ if (!surface || !content) return;
319
+ const box = {
320
+ width: surface.offsetWidth,
321
+ height: surface.offsetHeight,
322
+ center: content.offsetTop + content.offsetHeight / 2
323
+ };
324
+ const from = last.current;
325
+ last.current = box;
326
+ if (!from || !box.width || !box.height || !prefersMotion()) return;
327
+ let { width, height, center } = from;
328
+ if (moving.current.some((animation) => animation.playState === "running")) {
329
+ const shown = new DOMMatrixReadOnly(getComputedStyle(surface).transform);
330
+ width *= shown.a;
331
+ height *= shown.d;
332
+ center += new DOMMatrixReadOnly(getComputedStyle(content).transform).f;
333
+ }
334
+ for (const animation of moving.current) animation.cancel();
335
+ const timing = {
336
+ duration: DURATION,
337
+ easing: EASE
338
+ };
339
+ moving.current = [surface.animate([{ transform: `scale(${width / box.width}, ${height / box.height})` }, { transform: "none" }], timing), content.animate([{ transform: `translateY(${center - box.center}px)` }, { transform: "none" }], timing)];
340
+ }, [shape]);
341
+ }
315
342
  /** How many of the selection's views are laid over the page's, in this stack. */
316
343
  function useSelectionViewCount() {
317
344
  return React.useContext(SelectionViewsContext)?.count ?? 0;
@@ -372,7 +399,7 @@ function SidebarStackView(t0) {
372
399
  return t3;
373
400
  }
374
401
  function SidebarStackHeader(t0) {
375
- const $ = c(55);
402
+ const $ = c(69);
376
403
  const { depth, distance: t1, first: t2, onSelect, hoverTarget, tooltip, popover, children } = t0;
377
404
  const distance = t1 === void 0 ? 0 : t1;
378
405
  const first = t2 === void 0 ? false : t2;
@@ -450,82 +477,129 @@ function SidebarStackHeader(t0) {
450
477
  $[16] = t8;
451
478
  } else t8 = $[16];
452
479
  const button = t8;
453
- const t9 = !first && "-mt-1";
454
- let t10;
455
- if ($[17] !== t9) {
456
- t10 = cn("relative shrink-0 origin-top", t9);
457
- $[17] = t9;
458
- $[18] = t10;
459
- } else t10 = $[18];
460
- const t11 = !flat && !first && "rounded-t-sm";
480
+ const surfaceRef = React.useRef(null);
481
+ const contentRef = React.useRef(null);
482
+ let t9;
483
+ if ($[17] !== covered || $[18] !== distance) {
484
+ t9 = covered ? stackInset(distance) : 0;
485
+ $[17] = covered;
486
+ $[18] = distance;
487
+ $[19] = t9;
488
+ } else t9 = $[19];
489
+ const inset = t9;
490
+ useHeaderReshape(surfaceRef, contentRef, `${covered}:${inset}`);
491
+ const t10 = !first && "-mt-1";
492
+ let t11;
493
+ if ($[20] !== t10) {
494
+ t11 = cn("relative shrink-0 origin-top", t10);
495
+ $[20] = t10;
496
+ $[21] = t11;
497
+ } else t11 = $[21];
461
498
  const t12 = covered ? "h-[38px] pb-1" : "h-11 px-3";
462
- const t13 = first && !flat && "rounded-t-lg inset-shadow-raised-top";
463
- const t14 = covered && !first && `${CARD_TOP_EDGE} shadow-[0_-2px_5px_-3px_rgb(0_0_0/0.08)] dark:shadow-[0_-2px_5px_-3px_rgb(0_0_0/0.4)]`;
464
- const t15 = !covered && !first && `${CARD_TOP_EDGE} shadow-[0_-3px_8px_-4px_rgb(0_0_0/0.08)] dark:shadow-[0_-3px_8px_-4px_rgb(0_0_0/0.4)]`;
465
- const t16 = liftable && "hover:[--fade:1] hover:[--lift:var(--hover-lift)] hover:[--shade:var(--hover-shade)] hover:[--tilt:var(--hover-tilt)]";
466
- const t17 = liftable && "has-focus-visible:[--fade:1] has-focus-visible:[--lift:var(--hover-lift)] has-focus-visible:[--shade:var(--hover-shade)] has-focus-visible:[--tilt:var(--hover-tilt)]";
467
- const t18 = liftable && "has-data-popup-open:[--fade:1] has-data-popup-open:[--lift:var(--hover-lift)] has-data-popup-open:[--shade:var(--hover-shade)] has-data-popup-open:[--tilt:var(--hover-tilt)]";
468
- let t19;
469
- if ($[19] !== t11 || $[20] !== t12 || $[21] !== t13 || $[22] !== t14 || $[23] !== t15 || $[24] !== t16 || $[25] !== t17 || $[26] !== t18) {
470
- t19 = cn("relative flex min-w-0 items-center justify-center text-center text-sm", t11, "[--ancestor-foreground:color-mix(in_oklab,var(--color-muted-foreground),var(--color-foreground)_40%)]", "[--fade:var(--rest-fade)] [--lift:0px] [--shade:var(--rest-shade)] [--tilt:var(--rest-tilt)]", "[--shade-color:black] [--shade-strength:1] dark:[--shade-color:white] dark:[--shade-strength:var(--dark-shade-strength)]", "origin-bottom [backface-visibility:hidden] motion-reduce:transition-none!", t12, t13, t14, t15, t16, t17, t18);
471
- $[19] = t11;
472
- $[20] = t12;
473
- $[21] = t13;
474
- $[22] = t14;
475
- $[23] = t15;
476
- $[24] = t16;
477
- $[25] = t17;
478
- $[26] = t18;
499
+ const t13 = liftable && "hover:[--fade:1] hover:[--lift:var(--hover-lift)] hover:[--shade:var(--hover-shade)] hover:[--tilt:var(--hover-tilt)]";
500
+ const t14 = liftable && "has-focus-visible:[--fade:1] has-focus-visible:[--lift:var(--hover-lift)] has-focus-visible:[--shade:var(--hover-shade)] has-focus-visible:[--tilt:var(--hover-tilt)]";
501
+ const t15 = liftable && "has-data-popup-open:[--fade:1] has-data-popup-open:[--lift:var(--hover-lift)] has-data-popup-open:[--shade:var(--hover-shade)] has-data-popup-open:[--tilt:var(--hover-tilt)]";
502
+ let t16;
503
+ if ($[22] !== t12 || $[23] !== t13 || $[24] !== t14 || $[25] !== t15) {
504
+ t16 = cn("relative flex min-w-0 items-center justify-center text-center text-sm", "[--ancestor-foreground:color-mix(in_oklab,var(--color-muted-foreground),var(--color-foreground)_40%)]", "[--fade:var(--rest-fade)] [--lift:0px] [--shade:var(--rest-shade)] [--tilt:var(--rest-tilt)]", "[--shade-color:black] [--shade-strength:1] dark:[--shade-color:white] dark:[--shade-strength:var(--dark-shade-strength)]", "origin-bottom [backface-visibility:hidden] motion-reduce:transition-none!", t12, t13, t14, t15);
505
+ $[22] = t12;
506
+ $[23] = t13;
507
+ $[24] = t14;
508
+ $[25] = t15;
509
+ $[26] = t16;
510
+ } else t16 = $[26];
511
+ const t17 = `${tilt}deg`;
512
+ const t18 = `${tilt / 2}deg`;
513
+ const t19 = Math.max(0, distance - 1);
514
+ let t20;
515
+ if ($[27] !== t19) {
516
+ t20 = stepSeries(FIRST_STEP_SHADE, t19);
479
517
  $[27] = t19;
480
- } else t19 = $[27];
481
- const t20 = `${tilt}deg`;
482
- const t21 = `${tilt / 2}deg`;
483
- const t22 = `${shade}%`;
484
- const t23 = Math.max(0, distance - 1);
485
- let t24;
486
- if ($[28] !== t23) {
487
- t24 = stepSeries(FIRST_STEP_SHADE, t23);
488
- $[28] = t23;
489
- $[29] = t24;
490
- } else t24 = $[29];
491
- const t25 = `${t24}%`;
492
- const t26 = `${first ? 0 : -1.5}px`;
493
- const t27 = covered ? 1 - stepSeries(FIRST_STEP_FADE, distance - 1) : 1;
494
- let t28;
495
- if ($[30] !== covered || $[31] !== distance) {
496
- t28 = covered ? stackInset(distance) : void 0;
497
- $[30] = covered;
498
- $[31] = distance;
499
- $[32] = t28;
500
- } else t28 = $[32];
501
- let t29;
502
- if ($[33] !== t20 || $[34] !== t21 || $[35] !== t22 || $[36] !== t25 || $[37] !== t26 || $[38] !== t27 || $[39] !== t28) {
503
- t29 = {
504
- "--rest-tilt": t20,
505
- "--hover-tilt": t21,
506
- "--rest-shade": t22,
518
+ $[28] = t20;
519
+ } else t20 = $[28];
520
+ const t21 = `${first ? 0 : -1.5}px`;
521
+ const t22 = covered ? 1 - stepSeries(FIRST_STEP_FADE, distance - 1) : 1;
522
+ let t23;
523
+ if ($[29] !== shade || $[30] !== t17 || $[31] !== t18 || $[32] !== t20 || $[33] !== t21 || $[34] !== t22) {
524
+ t23 = {
525
+ "--rest-tilt": t17,
526
+ "--hover-tilt": t18,
527
+ "--rest-shade": shade,
507
528
  "--dark-shade-strength": DARK_SHADE_STRENGTH,
508
- "--hover-shade": t25,
509
- "--hover-lift": t26,
510
- "--rest-fade": t27,
511
- marginInline: t28,
529
+ "--hover-shade": t20,
530
+ "--hover-lift": t21,
531
+ "--rest-fade": t22,
512
532
  transform: `perspective(${PERSPECTIVE}px) translateY(var(--lift)) rotateX(var(--tilt))`,
513
- backgroundColor: "color-mix(in oklab, var(--color-background), var(--shade-color) calc(var(--shade) * var(--shade-strength)))",
514
- transition: HEADER_TRANSITION
533
+ transition: `transform ${DURATION}ms ${EASE}`
515
534
  };
516
- $[33] = t20;
517
- $[34] = t21;
518
- $[35] = t22;
535
+ $[29] = shade;
536
+ $[30] = t17;
537
+ $[31] = t18;
538
+ $[32] = t20;
539
+ $[33] = t21;
540
+ $[34] = t22;
541
+ $[35] = t23;
542
+ } else t23 = $[35];
543
+ const t24 = t23;
544
+ const t25 = !flat && (first ? "rounded-t-lg" : "rounded-t-sm");
545
+ const t26 = covered && !first && "shadow-[0_-2px_5px_-3px_rgb(0_0_0/0.08)] dark:shadow-[0_-2px_5px_-3px_rgb(0_0_0/0.4)]";
546
+ const t27 = !covered && !first && "shadow-[0_-3px_8px_-4px_rgb(0_0_0/0.08)] dark:shadow-[0_-3px_8px_-4px_rgb(0_0_0/0.4)]";
547
+ let t28;
548
+ if ($[36] !== t25 || $[37] !== t26 || $[38] !== t27) {
549
+ t28 = cn("bg-background absolute inset-y-0 isolate origin-top", t25, t26, t27);
519
550
  $[36] = t25;
520
551
  $[37] = t26;
521
552
  $[38] = t27;
522
553
  $[39] = t28;
523
- $[40] = t29;
524
- } else t29 = $[40];
525
- const t30 = t29;
554
+ } else t28 = $[39];
555
+ let t29;
556
+ if ($[40] !== inset) {
557
+ t29 = { insetInline: inset };
558
+ $[40] = inset;
559
+ $[41] = t29;
560
+ } else t29 = $[41];
561
+ let t30;
562
+ if ($[42] === Symbol.for("react.memo_cache_sentinel")) {
563
+ t30 = /* @__PURE__ */ jsx("div", {
564
+ className: "absolute inset-0 rounded-[inherit] motion-reduce:transition-none!",
565
+ style: {
566
+ backgroundColor: `color-mix(in oklab, var(--color-background), var(--shade-color) calc(${MAX_SHADE}% * var(--shade-strength)))`,
567
+ opacity: `calc(var(--shade) / ${MAX_SHADE})`,
568
+ transition: `opacity ${DURATION}ms ease-out`
569
+ }
570
+ });
571
+ $[42] = t30;
572
+ } else t30 = $[42];
526
573
  let t31;
527
- if ($[41] !== button || $[42] !== children || $[43] !== covered || $[44] !== popover || $[45] !== popoverOpen || $[46] !== tooltip) {
528
- t31 = button && popover ? /* @__PURE__ */ jsxs(Popover, {
574
+ if ($[43] !== flat) {
575
+ t31 = !flat && /* @__PURE__ */ jsx("div", { className: cn("absolute inset-0 rounded-[inherit]", CARD_TOP_EDGE) });
576
+ $[43] = flat;
577
+ $[44] = t31;
578
+ } else t31 = $[44];
579
+ let t32;
580
+ if ($[45] !== t28 || $[46] !== t29 || $[47] !== t31) {
581
+ t32 = /* @__PURE__ */ jsxs("div", {
582
+ ref: surfaceRef,
583
+ "aria-hidden": true,
584
+ className: t28,
585
+ style: t29,
586
+ children: [t30, t31]
587
+ });
588
+ $[45] = t28;
589
+ $[46] = t29;
590
+ $[47] = t31;
591
+ $[48] = t32;
592
+ } else t32 = $[48];
593
+ const t33 = inset || void 0;
594
+ let t34;
595
+ if ($[49] !== t33) {
596
+ t34 = { marginInline: t33 };
597
+ $[49] = t33;
598
+ $[50] = t34;
599
+ } else t34 = $[50];
600
+ let t35;
601
+ if ($[51] !== button || $[52] !== children || $[53] !== covered || $[54] !== popover || $[55] !== popoverOpen || $[56] !== tooltip) {
602
+ t35 = button && popover ? /* @__PURE__ */ jsxs(Popover, {
529
603
  open: popoverOpen,
530
604
  onOpenChange: setPopoverOpen,
531
605
  children: [/* @__PURE__ */ jsx(PopoverTrigger, { render: button }), /* @__PURE__ */ jsx(PopoverContent, {
@@ -542,38 +616,51 @@ function SidebarStackHeader(t0) {
542
616
  className: "truncate px-3 text-(--ancestor-foreground) opacity-(--fade)",
543
617
  children
544
618
  }) : children);
545
- $[41] = button;
546
- $[42] = children;
547
- $[43] = covered;
548
- $[44] = popover;
549
- $[45] = popoverOpen;
550
- $[46] = tooltip;
551
- $[47] = t31;
552
- } else t31 = $[47];
553
- let t32;
554
- if ($[48] !== t19 || $[49] !== t30 || $[50] !== t31) {
555
- t32 = /* @__PURE__ */ jsx("div", {
556
- className: t19,
557
- style: t30,
558
- children: t31
619
+ $[51] = button;
620
+ $[52] = children;
621
+ $[53] = covered;
622
+ $[54] = popover;
623
+ $[55] = popoverOpen;
624
+ $[56] = tooltip;
625
+ $[57] = t35;
626
+ } else t35 = $[57];
627
+ let t36;
628
+ if ($[58] !== t34 || $[59] !== t35) {
629
+ t36 = /* @__PURE__ */ jsx("div", {
630
+ ref: contentRef,
631
+ className: "relative flex min-w-0 flex-1 items-center justify-center self-stretch",
632
+ style: t34,
633
+ children: t35
634
+ });
635
+ $[58] = t34;
636
+ $[59] = t35;
637
+ $[60] = t36;
638
+ } else t36 = $[60];
639
+ let t37;
640
+ if ($[61] !== t16 || $[62] !== t24 || $[63] !== t32 || $[64] !== t36) {
641
+ t37 = /* @__PURE__ */ jsxs("div", {
642
+ className: t16,
643
+ style: t24,
644
+ children: [t32, t36]
559
645
  });
560
- $[48] = t19;
561
- $[49] = t30;
562
- $[50] = t31;
563
- $[51] = t32;
564
- } else t32 = $[51];
565
- let t33;
566
- if ($[52] !== t10 || $[53] !== t32) {
567
- t33 = /* @__PURE__ */ jsx("div", {
646
+ $[61] = t16;
647
+ $[62] = t24;
648
+ $[63] = t32;
649
+ $[64] = t36;
650
+ $[65] = t37;
651
+ } else t37 = $[65];
652
+ let t38;
653
+ if ($[66] !== t11 || $[67] !== t37) {
654
+ t38 = /* @__PURE__ */ jsx("div", {
568
655
  ref,
569
- className: t10,
570
- children: t32
656
+ className: t11,
657
+ children: t37
571
658
  });
572
- $[52] = t10;
573
- $[53] = t32;
574
- $[54] = t33;
575
- } else t33 = $[54];
576
- return t33;
659
+ $[66] = t11;
660
+ $[67] = t37;
661
+ $[68] = t38;
662
+ } else t38 = $[68];
663
+ return t38;
577
664
  }
578
665
 
579
666
  //#endregion
@@ -1,8 +1,32 @@
1
1
  //#region src/features/preview/components/previewStyles.ts
2
+ /**
3
+ * Whether a style or stylesheet is the site's. The studio marks its own, including those it adds
4
+ * to a frame's document, with `data-camox-studio`.
5
+ */
2
6
  function isSiteStyle(element) {
3
7
  return !element.hasAttribute("data-camox-studio");
4
8
  }
5
9
  const EMPTY_PREVIEW_DOCUMENT = "<!doctype html><html><head></head><body><div id=\"root\" data-camox-preview-root></div></body></html>";
10
+ /**
11
+ * The stylesheets of a document's head that are the site document's: not the studio's, nor those
12
+ * React hoists there for the blocks rendering in it (`data-precedence`).
13
+ */
14
+ const SITE_STYLESHEETS = "link[rel~=\"stylesheet\"], style";
15
+ const siteStylesheetsOf = (head) => Array.from(head.querySelectorAll(SITE_STYLESHEETS)).filter((element) => element.parentElement === head && isSiteStyle(element) && !element.hasAttribute("data-precedence"));
16
+ /**
17
+ * Gives a frame's document the site stylesheets of `html`, a site document: the ones it no
18
+ * longer has go, and its new ones are added, the rest of the frame left as it is.
19
+ */
20
+ function restyle(frame, html) {
21
+ const doc = frame.document;
22
+ const { DOMParser } = frame;
23
+ const wanted = siteStylesheetsOf(new DOMParser().parseFromString(html, "text/html").head);
24
+ const held = siteStylesheetsOf(doc.head);
25
+ const wantedMarkup = new Set(wanted.map((element) => element.outerHTML));
26
+ const heldMarkup = new Set(held.map((element) => element.outerHTML));
27
+ for (const element of held) if (!wantedMarkup.has(element.outerHTML)) element.remove();
28
+ for (const element of wanted) if (!heldMarkup.has(element.outerHTML)) doc.head.appendChild(doc.importNode(element, true));
29
+ }
6
30
 
7
31
  //#endregion
8
- export { EMPTY_PREVIEW_DOCUMENT, isSiteStyle };
32
+ export { EMPTY_PREVIEW_DOCUMENT, isSiteStyle, restyle };
@@ -8,6 +8,7 @@ import { PreviewEditingOwnerContext } from "../preview/previewSelection.js";
8
8
  import { StudioNoticeContext } from "../preview/studioNotice.js";
9
9
  import { ContentEditsContext } from "../../core/editing/contentEdits.js";
10
10
  import { PreviewDocumentContext } from "../runtime/PreviewDocumentContext.js";
11
+ import { restyle } from "../preview/components/previewStyles.js";
11
12
  import { FrameContext } from "../preview/components/Frame.js";
12
13
  import { DerivedPageContent } from "../page/DerivedPageContent.js";
13
14
  import { BlockPeek } from "../preview/components/BlockPeek.js";
@@ -425,7 +426,7 @@ function _temp2() {
425
426
  function _temp() {}
426
427
  /** The page at `pathname`, as the runtime renders it in the renderer's draft. */
427
428
  function FramePage(t0) {
428
- const $ = c(20);
429
+ const $ = c(24);
429
430
  const { camoxApp, codeLoaded, projectUuid, runtimeBasePath, pathname, derivedLayoutId, onRendered } = t0;
430
431
  let t1;
431
432
  if ($[0] !== derivedLayoutId || $[1] !== pathname || $[2] !== projectUuid || $[3] !== runtimeBasePath) {
@@ -449,52 +450,69 @@ function FramePage(t0) {
449
450
  } else t1 = $[4];
450
451
  const input = useRead(t1);
451
452
  if (codeLoaded) React.use(codeLoaded);
452
- const rendered = React.useEffectEvent(onRendered);
453
+ const frameWindow = React.use(FrameContext).window;
453
454
  let t2;
454
- if ($[7] !== rendered) {
455
- t2 = () => rendered();
456
- $[7] = rendered;
457
- $[8] = t2;
458
- } else t2 = $[8];
459
455
  let t3;
460
- if ($[9] !== input) {
461
- t3 = [input];
462
- $[9] = input;
456
+ if ($[7] !== frameWindow || $[8] !== input.previewDocument) {
457
+ t2 = () => {
458
+ if (frameWindow && input.previewDocument) restyle(frameWindow, input.previewDocument);
459
+ };
460
+ t3 = [frameWindow, input.previewDocument];
461
+ $[7] = frameWindow;
462
+ $[8] = input.previewDocument;
463
+ $[9] = t2;
463
464
  $[10] = t3;
464
- } else t3 = $[10];
465
- React.useEffect(t2, t3);
466
- const t4 = input.dehydratedState;
465
+ } else {
466
+ t2 = $[9];
467
+ t3 = $[10];
468
+ }
469
+ React.useLayoutEffect(t2, t3);
470
+ const rendered = React.useEffectEvent(onRendered);
471
+ let t4;
472
+ if ($[11] !== rendered) {
473
+ t4 = () => rendered();
474
+ $[11] = rendered;
475
+ $[12] = t4;
476
+ } else t4 = $[12];
467
477
  let t5;
468
- if ($[11] !== camoxApp || $[12] !== input.derived) {
469
- t5 = input.derived ? /* @__PURE__ */ jsx(DerivedPageContent, {
478
+ if ($[13] !== input) {
479
+ t5 = [input];
480
+ $[13] = input;
481
+ $[14] = t5;
482
+ } else t5 = $[14];
483
+ React.useEffect(t4, t5);
484
+ const t6 = input.dehydratedState;
485
+ let t7;
486
+ if ($[15] !== camoxApp || $[16] !== input.derived) {
487
+ t7 = input.derived ? /* @__PURE__ */ jsx(DerivedPageContent, {
470
488
  camoxApp,
471
489
  derived: input.derived
472
490
  }) : /* @__PURE__ */ jsx(EditablePageContent, {});
473
- $[11] = camoxApp;
474
- $[12] = input.derived;
475
- $[13] = t5;
476
- } else t5 = $[13];
477
- let t6;
478
- if ($[14] !== input.previewDocument || $[15] !== t5) {
479
- t6 = /* @__PURE__ */ jsx(PreviewDocumentContext.Provider, {
491
+ $[15] = camoxApp;
492
+ $[16] = input.derived;
493
+ $[17] = t7;
494
+ } else t7 = $[17];
495
+ let t8;
496
+ if ($[18] !== input.previewDocument || $[19] !== t7) {
497
+ t8 = /* @__PURE__ */ jsx(PreviewDocumentContext.Provider, {
480
498
  value: input.previewDocument,
481
- children: t5
482
- });
483
- $[14] = input.previewDocument;
484
- $[15] = t5;
485
- $[16] = t6;
486
- } else t6 = $[16];
487
- let t7;
488
- if ($[17] !== t4 || $[18] !== t6) {
489
- t7 = /* @__PURE__ */ jsx(HydrationBoundary, {
490
- state: t4,
491
- children: t6
499
+ children: t7
492
500
  });
493
- $[17] = t4;
494
- $[18] = t6;
501
+ $[18] = input.previewDocument;
495
502
  $[19] = t7;
496
- } else t7 = $[19];
497
- return t7;
503
+ $[20] = t8;
504
+ } else t8 = $[20];
505
+ let t9;
506
+ if ($[21] !== t6 || $[22] !== t8) {
507
+ t9 = /* @__PURE__ */ jsx(HydrationBoundary, {
508
+ state: t6,
509
+ children: t8
510
+ });
511
+ $[21] = t6;
512
+ $[22] = t8;
513
+ $[23] = t9;
514
+ } else t9 = $[23];
515
+ return t9;
498
516
  }
499
517
  const messageOf = (error) => error instanceof Error ? error.message : String(error);
500
518
  const ERROR_STYLE = {
@@ -1,14 +1,14 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
2
  import { SIDEBAR_GUTTER } from "../../preview/components/SidebarStack.js";
3
3
  import { ChatWidget } from "./ChatWidget.js";
4
- import { WorkersWidget } from "./WorkersWidget.js";
4
+ import { Patrol } from "./Patrol.js";
5
5
  import { c } from "react/compiler-runtime";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
 
8
8
  //#region src/features/studio/components/AgentSidebar.tsx
9
9
  /**
10
- * The sidebar on the Canvas's right side, where editors work with Camox's agent: the Workers
11
- * widget over the Chat widget, which gets the larger share of the height. Laid over the Canvas,
10
+ * The sidebar on the Canvas's right side, where editors work with Camox's agent: the Patrol
11
+ * over the Chat widget, which gets the larger share of the height. Laid over the Canvas,
12
12
  * as the Content Sidebar is over its left: only the cards have a background, and the Canvas shows
13
13
  * around them. Elsewhere in the studio it is only hidden, so the chat keeps its thread and the
14
14
  * Preview and Studio pages follow the editor's runs too.
@@ -24,7 +24,7 @@ function AgentSidebar(t0) {
24
24
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25
25
  t2 = cn("absolute inset-y-0 right-0 z-10 flex flex-col gap-2 py-2 max-md:hidden", SIDEBAR_GUTTER);
26
26
  t3 = { width: 265 };
27
- t4 = /* @__PURE__ */ jsx(WorkersWidget, { className: "min-h-0 flex-[3_1_0]" });
27
+ t4 = /* @__PURE__ */ jsx(Patrol, { className: "min-h-0 flex-[3_1_0]" });
28
28
  t5 = /* @__PURE__ */ jsx(ChatWidget, { className: "min-h-0 flex-[7_1_0]" });
29
29
  $[0] = t2;
30
30
  $[1] = t3;