camox 0.52.3 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/core/editing/createEditableBlock.js +1 -1
  2. package/dist/features/canvas/CamoxCanvas.js +103 -83
  3. package/dist/features/canvas/CanvasPageFrame.js +1 -1
  4. package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -1
  5. package/dist/features/canvas/canvasCamera.js +3 -3
  6. package/dist/features/canvas/sidebar/CanvasInspector.js +4 -159
  7. package/dist/features/canvas/sidebar/CanvasNavigator.js +41 -85
  8. package/dist/features/canvas/sidebar/CanvasSidebar.js +54 -32
  9. package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +4 -3
  10. package/dist/features/canvas/sidebar/CanvasSidebarSkeleton.js +14 -6
  11. package/dist/features/canvas/sidebar/CanvasViewHeader.js +50 -0
  12. package/dist/features/canvas/sidebar/DerivedPageInspector.js +30 -18
  13. package/dist/features/canvas/sidebar/LayoutPageOverview.js +28 -11
  14. package/dist/features/canvas/sidebar/LayoutPageSeo.js +356 -0
  15. package/dist/features/canvas/sidebar/PageInspector.js +118 -80
  16. package/dist/features/canvas/sidebar/PageOverview.js +6 -1
  17. package/dist/features/canvas/sidebar/SingletonPageInspector.js +22 -14
  18. package/dist/features/canvas/useCanvasCamera.js +36 -39
  19. package/dist/features/og/documentFonts.js +49 -0
  20. package/dist/features/og/imageResponse.d.ts +14 -5
  21. package/dist/features/og/imageResponse.js +20 -21
  22. package/dist/features/og/pageImage.js +7 -4
  23. package/dist/features/preview/CamoxPreview.js +1 -1
  24. package/dist/features/preview/components/BlockActionsPopover.js +2 -2
  25. package/dist/features/preview/components/BlockThumbnail.js +1 -1
  26. package/dist/features/preview/components/CanvasPreview.js +1 -1
  27. package/dist/features/preview/components/CreatePageModal.js +1 -1
  28. package/dist/features/preview/components/IconFieldEditor.js +1 -1
  29. package/dist/features/preview/components/ItemFieldsEditor.js +1 -1
  30. package/dist/features/preview/components/LinkFieldEditor.js +1 -1
  31. package/dist/features/preview/components/OverlayTracker.js +1 -1
  32. package/dist/features/preview/components/PageInfo.js +345 -228
  33. package/dist/features/preview/components/PagePicker.js +1 -1
  34. package/dist/features/preview/components/PageTree.js +1 -1
  35. package/dist/features/preview/components/PreviewPanel.js +1 -1
  36. package/dist/features/preview/components/PreviewToolbar.js +2 -2
  37. package/dist/features/preview/components/RepeatableItemsList.js +1 -1
  38. package/dist/features/preview/components/SelectionEditor.js +668 -652
  39. package/dist/features/preview/components/SidebarStack.js +582 -0
  40. package/dist/features/preview/components/StudioEditingFallback.js +2 -2
  41. package/dist/features/renderer/RendererFrames.js +34 -27
  42. package/dist/features/runtime/RuntimeChrome.js +1 -1
  43. package/dist/features/runtime/runtime.d.ts +5 -0
  44. package/dist/features/runtime/runtime.js +99 -30
  45. package/dist/features/studio/components/ChatSidebar.js +76 -86
  46. package/dist/features/studio/components/Navbar.js +1 -1
  47. package/dist/features/studio/components/Squareling.js +114 -0
  48. package/dist/features/studio/components/ThreadMessages.js +128 -126
  49. package/dist/features/studio/components/UserButton.js +1 -1
  50. package/dist/features/vite/bundlerLogs.js +1 -0
  51. package/dist/features/vite/runtimeDev.js +11 -0
  52. package/dist/features/vite/vite.js +3 -1
  53. package/dist/lib/studio-reads.js +18 -0
  54. package/dist/package.js +1 -1
  55. package/dist/studio.css +1 -1
  56. package/package.json +5 -5
  57. package/skills/camox/references/layout-definitions.md +11 -16
  58. package/dist/features/og/defaultFonts.js +0 -31
  59. package/dist/features/og/geist-variable-ttf-base64.js +0 -21
@@ -45,6 +45,12 @@ function isTextEntry(target) {
45
45
  function rescaleY(camera, scale) {
46
46
  return camera.y > 0 ? camera.y : camera.y * scale / camera.scale;
47
47
  }
48
+ const NO_INSETS = {
49
+ top: 0,
50
+ right: 0,
51
+ bottom: 0,
52
+ left: 0
53
+ };
48
54
  /** Animate only the camera's CSS properties; page trees never rerender on pan/zoom. */
49
55
  function useCanvasCamera(workspaceKey, initialTarget, row) {
50
56
  const continuity = React.useContext(ViewportContinuityContext);
@@ -102,20 +108,22 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
102
108
  capture: true,
103
109
  passive: false
104
110
  });
105
- let viewportSize = {
106
- width: viewport.clientWidth,
107
- height: viewport.clientHeight
111
+ let insets = rowRef.current?.insets ?? NO_INSETS;
112
+ const measureViewport = () => {
113
+ insets = rowRef.current?.insets ?? NO_INSETS;
114
+ return {
115
+ width: Math.max(0, viewport.clientWidth - insets.left - insets.right),
116
+ height: Math.max(0, viewport.clientHeight - insets.top - insets.bottom)
117
+ };
108
118
  };
119
+ let viewportSize = measureViewport();
109
120
  const measureContent = () => ({
110
121
  width: rowRef.current?.width ?? content.offsetWidth,
111
122
  height: content.offsetHeight
112
123
  });
113
124
  let contentSize = measureContent();
114
125
  let minimumScale = canvasMinimumScale(viewportSize, contentSize);
115
- const updateGeometry = (nextViewport = {
116
- width: viewport.clientWidth,
117
- height: viewport.clientHeight
118
- }, nextContent = measureContent()) => {
126
+ const updateGeometry = (nextViewport = measureViewport(), nextContent = measureContent()) => {
119
127
  viewportSize = nextViewport;
120
128
  contentSize = nextContent;
121
129
  minimumScale = canvasMinimumScale(viewportSize, contentSize);
@@ -128,7 +136,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
128
136
  const targetOf = (key) => key == null ? void 0 : targets().find((candidate) => candidate.key === key);
129
137
  let stretch;
130
138
  let zoomEnd = 0;
131
- let landing;
132
139
  const constrain = (next, stretched = !!stretch) => constrainCanvasCamera(next, viewportSize, contentSize, canvasMaximumScale(viewportSize, targets(), minimumScale), stretched);
133
140
  const saved = canvasStore.getSnapshot().context.views[workspaceKey];
134
141
  const savedCamera = saved && !saved.fitted ? saved.camera : void 0;
@@ -194,16 +201,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
194
201
  y: anchored.y + translation.y
195
202
  };
196
203
  if (!candidate || !fit || !isCanvasPageScale(snapped, fit.scale)) return next;
197
- const entered = selection !== candidate.key;
198
- if (entered) landing = {
199
- key: candidate.key,
200
- stretched
201
- };
202
- else if (stretched && landing && !landing.stretched) {
203
- landing.stretched = true;
204
- rowRef.current?.onSnapStretched?.();
205
- }
206
- if (entered && !stretched) rowRef.current?.onSnap?.();
207
204
  select(candidate.key);
208
205
  return {
209
206
  ...next,
@@ -236,7 +233,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
236
233
  if (!candidate || !fit || fit.scale < minimumScale) return intended;
237
234
  const held = isCanvasCameraSnapped(base, viewportSize, candidate);
238
235
  if (!isInCanvasSnapWindow(intended, viewportSize, candidate, held)) return intended;
239
- if (selection !== candidate.key) rowRef.current?.onSnap?.();
240
236
  select(candidate.key);
241
237
  gesture.intendedX += (candidate.left + candidate.width / 2) * (intended.scale - fit.scale);
242
238
  return constrain({
@@ -254,8 +250,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
254
250
  markMoving();
255
251
  applyCamera(next);
256
252
  }
257
- if (landing?.stretched && landing.key === selection) rowRef.current?.onSnap?.();
258
- landing = void 0;
259
253
  };
260
254
  const pointers = /* @__PURE__ */ new Map();
261
255
  let spaceHeld = false;
@@ -317,8 +311,8 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
317
311
  if (!wasMoving) setMoving(true);
318
312
  };
319
313
  const paintElement = (element) => {
320
- element.style.setProperty("--canvas-x", `${camera.x}px`);
321
- element.style.setProperty("--canvas-y", `${camera.y}px`);
314
+ element.style.setProperty("--canvas-x", `${camera.x + insets.left}px`);
315
+ element.style.setProperty("--canvas-y", `${camera.y + insets.top}px`);
322
316
  element.style.setProperty("--canvas-zoom", String(camera.scale));
323
317
  element.style.setProperty("--canvas-pixel-ratio", String(ownerWindow.devicePixelRatio));
324
318
  };
@@ -413,7 +407,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
413
407
  gesture = void 0;
414
408
  ownerWindow.clearTimeout(zoomEnd);
415
409
  stretch = void 0;
416
- landing = void 0;
417
410
  updateGeometry();
418
411
  camera = constrain(camera, true);
419
412
  target = constrain(destinationCamera());
@@ -435,7 +428,7 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
435
428
  const frame = element.ownerDocument.defaultView?.frameElement;
436
429
  if (!frame || flight && !flight.pan || pointers.size) return;
437
430
  const rect = element.getBoundingClientRect();
438
- const worldTop = (frame.getBoundingClientRect().top - viewport.getBoundingClientRect().top + rect.top * camera.scale - camera.y) / camera.scale;
431
+ const worldTop = (frame.getBoundingClientRect().top - viewport.getBoundingClientRect().top - insets.top + rect.top * camera.scale - camera.y) / camera.scale;
439
432
  const delta = revealScrollDelta({
440
433
  top: target.y + worldTop * target.scale,
441
434
  bottom: target.y + (worldTop + rect.height) * target.scale
@@ -483,7 +476,7 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
483
476
  const held = flight ? void 0 : targetOf(selection);
484
477
  const element = held && Array.from(content.querySelectorAll("[data-canvas-page]")).find((element) => element.dataset.canvasPage === held.key);
485
478
  const doc = element ? canvasPageIframe(element)?.contentDocument : void 0;
486
- const anchor = doc && element ? captureViewportAnchor(doc, Math.max(0, (viewport.getBoundingClientRect().top - element.getBoundingClientRect().top) / camera.scale)) : void 0;
479
+ const anchor = doc && element ? captureViewportAnchor(doc, Math.max(0, (viewport.getBoundingClientRect().top + insets.top - element.getBoundingClientRect().top) / camera.scale)) : void 0;
487
480
  return {
488
481
  camera: { ...camera },
489
482
  pathname: element ? element.dataset.canvasPathname : void 0,
@@ -505,7 +498,7 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
505
498
  } : fit;
506
499
  if (anchor && doc) {
507
500
  const y = resolveViewportAnchor(doc, anchor);
508
- const frameOffsetY = page ? (page.getBoundingClientRect().top - viewport.getBoundingClientRect().top - camera.y) / camera.scale : 0;
501
+ const frameOffsetY = page ? (page.getBoundingClientRect().top - viewport.getBoundingClientRect().top - insets.top - camera.y) / camera.scale : 0;
509
502
  next = placeCanvasPageAnchor(viewportSize, destination, y, fit.scale, frameOffsetY);
510
503
  }
511
504
  fittingInitialPage = false;
@@ -515,12 +508,10 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
515
508
  };
516
509
  if (continuity) continuity.canvas = continuityBinding;
517
510
  const resize = new ResizeObserver(() => {
518
- const nextViewport = {
519
- width: viewport.clientWidth,
520
- height: viewport.clientHeight
521
- };
511
+ const previousInsets = insets;
512
+ const nextViewport = measureViewport();
522
513
  const nextContent = measureContent();
523
- if (nextViewport.width === viewportSize.width && nextViewport.height === viewportSize.height && nextContent.width === contentSize.width && nextContent.height === contentSize.height) return;
514
+ if (insets.left === previousInsets.left && insets.top === previousInsets.top && nextViewport.width === viewportSize.width && nextViewport.height === viewportSize.height && nextContent.width === contentSize.width && nextContent.height === contentSize.height) return;
524
515
  const wasFitted = isOverview(target);
525
516
  gesture = void 0;
526
517
  const held = nextViewport.width !== viewportSize.width && !flight && !fittingInitialPage ? targetOf(selection) : void 0;
@@ -559,14 +550,14 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
559
550
  const localPoint = (x, y) => {
560
551
  const rect = viewport.getBoundingClientRect();
561
552
  return {
562
- x: x - rect.left,
563
- y: y - rect.top
553
+ x: x - rect.left - insets.left,
554
+ y: y - rect.top - insets.top
564
555
  };
565
556
  };
566
557
  const onWheel = (event) => {
567
558
  event.preventDefault();
568
- const dx = canvasWheelDelta(event.deltaX, event.deltaMode, viewport.clientWidth);
569
- const dy = canvasWheelDelta(event.deltaY, event.deltaMode, viewport.clientHeight);
559
+ const dx = canvasWheelDelta(event.deltaX, event.deltaMode, viewportSize.width);
560
+ const dy = canvasWheelDelta(event.deltaY, event.deltaMode, viewportSize.height);
570
561
  if (!dx && !dy) return;
571
562
  rowRef.current?.onEditorMove?.();
572
563
  cancelCameraFlight();
@@ -664,7 +655,13 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
664
655
  const onFocusIn = (event) => {
665
656
  if (!isControl(event.target)) return;
666
657
  const rect = event.target.getBoundingClientRect();
667
- const bounds = viewport.getBoundingClientRect();
658
+ const viewportRect = viewport.getBoundingClientRect();
659
+ const bounds = {
660
+ left: viewportRect.left + insets.left,
661
+ top: viewportRect.top + insets.top,
662
+ right: viewportRect.left + insets.left + viewportSize.width,
663
+ bottom: viewportRect.top + insets.top + viewportSize.height
664
+ };
668
665
  const dx = Math.max(0, bounds.left + 16 - rect.left) - Math.max(0, rect.right - bounds.right + 16);
669
666
  const dy = Math.max(0, bounds.top + 16 - rect.top) - Math.max(0, rect.bottom - bounds.bottom + 16);
670
667
  if (!dx && !dy) return;
@@ -689,8 +686,8 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
689
686
  "ArrowDown"
690
687
  ].includes(event.key) || event.shiftKey && event.code === "Digit1") cancelCameraFlight();
691
688
  const center = {
692
- x: viewport.clientWidth / 2,
693
- y: viewport.clientHeight / 2
689
+ x: viewportSize.width / 2,
690
+ y: viewportSize.height / 2
694
691
  };
695
692
  const factor = event.key === "+" || event.key === "=" ? 1.25 : event.key === "-" ? .8 : event.key === "0" ? 1 / target.scale : void 0;
696
693
  if (factor) {
@@ -0,0 +1,49 @@
1
+ //#region src/features/og/documentFonts.ts
2
+ const GOOGLE_FONTS_CSS2 = "https://fonts.googleapis.com/css2";
3
+ /**
4
+ * The Google Fonts families the document's stylesheet links load, in link order, so page images
5
+ * render in the site's fonts. Other stylesheets are ignored.
6
+ */
7
+ function documentGoogleFonts(document) {
8
+ return (Array.isArray(document.link) ? document.link : []).flatMap((link) => {
9
+ if (typeof link !== "object" || link === null) return [];
10
+ const { rel, href } = link;
11
+ if (rel !== "stylesheet" || typeof href !== "string") return [];
12
+ const url = URL.parse(href);
13
+ if (!url || `${url.origin}${url.pathname}` !== GOOGLE_FONTS_CSS2) return [];
14
+ return url.searchParams.getAll("family").map(parseFamily);
15
+ });
16
+ }
17
+ /**
18
+ * One css2 `family` parameter: `Inter`, or a name with its axes and their values, such as
19
+ * `Archivo:ital,wght@0,100..900;1,100..900` (one `;`-separated tuple per face).
20
+ */
21
+ function parseFamily(param) {
22
+ const [name = "", spec] = param.split(":");
23
+ if (!spec) return { name };
24
+ const [axisList = "", tupleList = ""] = spec.split("@");
25
+ const axes = axisList.split(",");
26
+ const tuples = tupleList.split(";").map((tuple) => tuple.split(","));
27
+ const valuesOf = (axis) => {
28
+ const index = axes.indexOf(axis);
29
+ return index < 0 ? [] : [...new Set(tuples.map((tuple) => tuple[index] ?? ""))];
30
+ };
31
+ const weights = valuesOf("wght");
32
+ const weightRange = weights.find(isRange);
33
+ const otherAxes = Object.fromEntries(axes.filter((axis) => axis !== "wght" && axis !== "ital").map((axis) => {
34
+ const [value = ""] = valuesOf(axis);
35
+ return [axis, isRange(value) ? value : Number(value)];
36
+ }));
37
+ return {
38
+ name,
39
+ ...weights.length > 0 && { weight: weightRange ?? weights.map(Number) },
40
+ ...axes.includes("ital") && { style: valuesOf("ital").map((ital) => ital === "1" ? "italic" : "normal") },
41
+ ...Object.keys(otherAxes).length > 0 && { axes: otherAxes }
42
+ };
43
+ }
44
+ function isRange(value) {
45
+ return value.includes("..");
46
+ }
47
+
48
+ //#endregion
49
+ export { documentGoogleFonts };
@@ -1,8 +1,17 @@
1
- import { ReactNode } from "react";
2
- import { ImageResponse as ImageResponse$1, ImageResponseOptions } from "@takumi-rs/image-response";
1
+ import { ReactElement } from "react";
2
+ import { GoogleFontFamily } from "takumi-js/helpers";
3
+ import { ImageResponse } from "takumi-js/response";
3
4
  //#region src/features/og/imageResponse.d.ts
4
- export declare class ImageResponse extends ImageResponse$1 {
5
- constructor(component: ReactNode, options?: ImageResponseOptions);
5
+ export interface ImageStyle {
6
+ /** The site's compiled stylesheet, which the image's `className`s match. */
7
+ css?: string;
8
+ /** The Google Fonts families the site loads, in fallback order. */
9
+ fonts: GoogleFontFamily[];
6
10
  }
11
+ /** Renders `element` as a PNG response, styled like the site. */
12
+ export declare function createImageResponse(element: ReactElement, size: {
13
+ width: number;
14
+ height: number;
15
+ }, { css, fonts }: ImageStyle): Promise<Response>;
7
16
  //#endregion
8
- export type { ImageResponseOptions };
17
+ export { ImageResponse };
@@ -1,26 +1,25 @@
1
- import { DEFAULT_FONTS } from "./defaultFonts.js";
2
- import { ImageResponse as ImageResponse$1 } from "@takumi-rs/image-response";
1
+ import { googleFonts } from "takumi-js/helpers";
2
+ import { ImageResponse } from "takumi-js/response";
3
3
 
4
4
  //#region src/features/og/imageResponse.ts
5
- /**
6
- * OG image renderer for the Worker.
7
- *
8
- * Under the `workerd` export condition, `@takumi-rs/image-response` picks Takumi's wasm
9
- * backend, which imports the wasm binary itself. The Cloudflare Vite plugin turns that
10
- * `.wasm` import into a `WebAssembly.Module`, and Takumi lazy-inits it inside the
11
- * response stream.
12
- *
13
- * Geist is bundled in `./defaultFonts` to compensate for the wasm renderer shipping with
14
- * no embedded fonts.
15
- */
16
- var ImageResponse = class extends ImageResponse$1 {
17
- constructor(component, options) {
18
- super(component, {
19
- ...options,
20
- fonts: options?.fonts ?? DEFAULT_FONTS
21
- });
5
+ /** Renders `element` as a PNG response, styled like the site. */
6
+ async function createImageResponse(element, size, { css, fonts }) {
7
+ return new ImageResponse(element, {
8
+ ...size,
9
+ css,
10
+ fonts: await loadFonts(fonts)
11
+ });
12
+ }
13
+ /** The families' font files, loaded lazily. Without them, the image falls back to Geist. */
14
+ async function loadFonts(families) {
15
+ if (families.length === 0) return [];
16
+ try {
17
+ return await googleFonts(families);
18
+ } catch (error) {
19
+ console.warn("[camox] Page image fonts failed to load; rendering with Geist.", error);
20
+ return [];
22
21
  }
23
- };
22
+ }
24
23
 
25
24
  //#endregion
26
- export { ImageResponse };
25
+ export { ImageResponse, createImageResponse };
@@ -78,11 +78,14 @@ function DefaultPageImage(t0) {
78
78
  } else t5 = $[8];
79
79
  return t5;
80
80
  }
81
- /** Renders a page image as a PNG response. Server only. */
82
- async function renderPageImage(element) {
81
+ /**
82
+ * Renders a page image as a PNG response, styled like the site: its elements' `className`s match
83
+ * the site's compiled stylesheet, and its text uses the site's fonts. Server only.
84
+ */
85
+ async function renderPageImage(element, style) {
83
86
  if (import.meta.env?.SSR === false) throw new Error("OG images can only be rendered on the server.");
84
- const { ImageResponse } = await import("camox/_internal/imageResponse");
85
- return new ImageResponse(element, PAGE_IMAGE_SIZE);
87
+ const { createImageResponse } = await import("camox/_internal/imageResponse");
88
+ return createImageResponse(element, PAGE_IMAGE_SIZE, style);
86
89
  }
87
90
 
88
91
  //#endregion
@@ -12,10 +12,10 @@ import { usePageDestinations } from "../../hooks/use-page-destinations.js";
12
12
  import { useLocalePath, useLocalizedPath } from "../../lib/view-locale.js";
13
13
  import { CanvasLoadingScreen } from "../canvas/CanvasLoadingScreen.js";
14
14
  import { canvasStore } from "../canvas/canvasStore.js";
15
+ import { previewStore, selectIsCanvasShown } from "./previewStore.js";
15
16
  import { SHOW_WHOLE_CANVAS_SHORTCUT, TOGGLE_CANVAS_SHORTCUT } from "./previewConstants.js";
16
17
  import { CanvasSidebarSkeleton } from "../canvas/sidebar/CanvasSidebarSkeleton.js";
17
18
  import { takeShowCanvasRequest } from "../provider/signInShortcut.js";
18
- import { previewStore, selectIsCanvasShown } from "./previewStore.js";
19
19
  import { PreviewEditingOwnerContext } from "./previewSelection.js";
20
20
  import { RendererFramesProvider, RendererSlot, useRendererHost, useRendererStatus } from "../renderer/RendererFrames.js";
21
21
  import { STUDIO_CANVAS_PATH } from "../studio/routes.js";
@@ -1,8 +1,8 @@
1
1
  import { actionsStore } from "../../provider/actionsStore.js";
2
2
  import { usePageBlocks } from "../../../lib/normalized-data.js";
3
3
  import { blockMutations, repeatableItemMutations } from "../../../lib/queries.js";
4
- import { formatShortcut } from "../../../lib/shortcuts.js";
5
4
  import { isBlockSelection, previewStore, selectionForPage, selectionItemId } from "../previewStore.js";
5
+ import { formatShortcut } from "../../../lib/shortcuts.js";
6
6
  import { entityName } from "../entityName.js";
7
7
  import { useUpdateBlockPosition } from "./useUpdateBlockPosition.js";
8
8
  import { usePreviewedPage } from "../CamoxPreview.js";
@@ -14,8 +14,8 @@ import { useMutation } from "@tanstack/react-query";
14
14
  import { Copy, Trash2 } from "lucide-react";
15
15
  import { toast } from "@camox/ui/toaster";
16
16
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
17
- import { Command as Command$1, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "@camox/ui/command";
18
17
  import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
18
+ import { Command as Command$1, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "@camox/ui/command";
19
19
 
20
20
  //#region src/features/preview/components/BlockActionsPopover.tsx
21
21
  const BlockActionsPopover = ({ block, children, open, onOpenChange, align = "start", isLayoutBlock, layoutSlot }) => {
@@ -1,6 +1,6 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
- import { useRendererHost } from "../../renderer/RendererFrames.js";
3
2
  import { useEditingFrame } from "../editingFrame.js";
3
+ import { useRendererHost } from "../../renderer/RendererFrames.js";
4
4
  import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle } from "./previewStyles.js";
5
5
  import { FrameContext } from "./Frame.js";
6
6
  import { c } from "react/compiler-runtime";
@@ -203,7 +203,7 @@ function CanvasPreview({ enabled, pathname, pagePathname, runtimeBasePath, place
203
203
  className: "relative flex min-h-0 flex-1",
204
204
  "data-preview-surface": true,
205
205
  children: [
206
- enabled && phase !== "failed" && placeholder,
206
+ enabled && phase === "preparing" && placeholder,
207
207
  /* @__PURE__ */ jsxs("div", {
208
208
  className: "relative flex min-h-0 min-w-0 flex-1 flex-col",
209
209
  "data-read-only-preview": true,
@@ -4,9 +4,9 @@ import { reads, useReadQuery } from "../../../lib/studio-reads.js";
4
4
  import { pageMutations } from "../../../lib/queries.js";
5
5
  import { useLayoutDefinitions } from "../../../lib/studio-definitions.js";
6
6
  import { useLocalizedPath } from "../../../lib/view-locale.js";
7
- import { PageNicknameField } from "./PageNicknameField.js";
8
7
  import { previewStore } from "../previewStore.js";
9
8
  import { PageLocationFieldset } from "./PageLocationFieldset.js";
9
+ import { PageNicknameField } from "./PageNicknameField.js";
10
10
  import { c } from "react/compiler-runtime";
11
11
  import { useEffect } from "react";
12
12
  import { useSelector } from "@xstate/store-react";
@@ -5,9 +5,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { Button } from "@camox/ui/button";
6
6
  import { ChevronsUpDown, SearchIcon } from "lucide-react";
7
7
  import { Combobox } from "@base-ui/react/combobox";
8
+ import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
8
9
  import { commandFilter } from "@camox/ui/command";
9
10
  import { InputGroup, InputGroupAddon, InputGroupInput } from "@camox/ui/input-group";
10
- import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
11
11
  import { defaultRangeExtractor, useVirtualizer } from "@tanstack/react-virtual";
12
12
 
13
13
  //#region src/features/preview/components/IconFieldEditor.tsx
@@ -5,8 +5,8 @@ import { DrillRow } from "./DrillRow.js";
5
5
  import { ReferenceFieldEditor } from "./ReferenceFieldEditor.js";
6
6
  import { ReferenceListFieldEditor } from "./ReferenceListFieldEditor.js";
7
7
  import { previewStore } from "../previewStore.js";
8
- import { PreviewEditingOwnerContext } from "../previewSelection.js";
9
8
  import { overlayFieldId, recordPlacementId } from "../overlayMessages.js";
9
+ import { PreviewEditingOwnerContext } from "../previewSelection.js";
10
10
  import { SidebarLexicalEditor } from "../../../core/components/lexical/SidebarLexicalEditor.js";
11
11
  import { IconFieldEditor } from "./IconFieldEditor.js";
12
12
  import { RepeatableItemsList } from "./RepeatableItemsList.js";
@@ -10,8 +10,8 @@ import { Input } from "@camox/ui/input";
10
10
  import { Label } from "@camox/ui/label";
11
11
  import { Switch } from "@camox/ui/switch";
12
12
  import { Check, ChevronsUpDown } from "lucide-react";
13
- import { Command as Command$1, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@camox/ui/command";
14
13
  import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
14
+ import { Command as Command$1, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@camox/ui/command";
15
15
  import { Tabs, TabsList, TabsTrigger } from "@camox/ui/tabs";
16
16
 
17
17
  //#region src/features/preview/components/LinkFieldEditor.tsx
@@ -1,5 +1,5 @@
1
- import { useFrame } from "./Frame.js";
2
1
  import { isOverlayMessage } from "../overlayMessages.js";
2
+ import { useFrame } from "./Frame.js";
3
3
  import { c } from "react/compiler-runtime";
4
4
  import * as React from "react";
5
5