camox 0.45.0 → 0.46.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 (39) hide show
  1. package/dist/core/hooks/useArtifactHost.js +2 -2
  2. package/dist/features/canvas/CamoxCanvas.js +438 -771
  3. package/dist/features/canvas/CanvasPageFrame.js +1 -3
  4. package/dist/features/canvas/CanvasPreviewPageSlot.js +26 -6
  5. package/dist/features/canvas/canvasCamera.js +3 -3
  6. package/dist/features/canvas/canvasFrameDocument.js +4 -19
  7. package/dist/features/canvas/canvasItems.js +12 -102
  8. package/dist/features/canvas/canvasStore.js +3 -92
  9. package/dist/features/canvas/sidebar/CanvasNavigator.js +81 -183
  10. package/dist/features/canvas/sidebar/CanvasSidebar.js +22 -28
  11. package/dist/features/canvas/sidebar/CuratedPageInspector.js +2 -2
  12. package/dist/features/canvas/useCanvasCamera.js +0 -20
  13. package/dist/features/canvas/useCanvasFrame.js +5 -6
  14. package/dist/features/preview/CamoxPreview.js +3 -3
  15. package/dist/features/preview/components/{AddBlockDialog.js → AddBlockSheet.js} +35 -32
  16. package/dist/features/preview/components/BlockActionsPopover.js +6 -6
  17. package/dist/features/preview/components/BlockThumbnail.js +31 -33
  18. package/dist/features/preview/components/Overlays.js +1 -1
  19. package/dist/features/preview/components/PageTree.js +1 -1
  20. package/dist/features/preview/components/PreviewPanel.js +1 -1
  21. package/dist/features/preview/previewStore.js +9 -9
  22. package/dist/features/provider/CoreCamoxProvider.js +9 -6
  23. package/dist/features/routes/pageRuntime.js +8 -8
  24. package/dist/features/runtime/runtime.d.ts +2 -7
  25. package/dist/features/runtime/runtime.js +6 -19
  26. package/dist/features/studio/FollowAgentProvider.js +11 -22
  27. package/dist/features/studio/components/FaviconDialog.js +3 -3
  28. package/dist/features/studio/followAgent.js +5 -14
  29. package/dist/features/studio/routes.js +1 -23
  30. package/dist/lib/auth.js +36 -24
  31. package/dist/lib/studio-reads.js +0 -20
  32. package/dist/package.js +1 -1
  33. package/dist/studio.css +1 -1
  34. package/package.json +4 -4
  35. package/skills/camox-expand/SKILL.md +5 -4
  36. package/dist/features/canvas/CanvasBlockFrame.js +0 -353
  37. package/dist/features/canvas/CanvasBlockHeader.js +0 -55
  38. package/dist/features/canvas/CanvasShelfMarker.js +0 -255
  39. package/dist/features/canvas/sidebar/CatalogBlockInspector.js +0 -189
@@ -1,224 +1,122 @@
1
- import { cn } from "../../../lib/utils.js";
2
1
  import { previewStore } from "../../preview/previewStore.js";
3
2
  import { canvasPagePath } from "../canvasPages.js";
4
3
  import { c } from "react/compiler-runtime";
5
4
  import * as React from "react";
6
5
  import { jsx, jsxs } from "react/jsx-runtime";
7
6
  import { Button } from "@camox/ui/button";
8
- import { ChevronsUpDown, Plus } from "lucide-react";
7
+ import { Plus } from "lucide-react";
9
8
  import { Command as Command$1, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, commandFilter } from "@camox/ui/command";
10
9
 
11
10
  //#region src/features/canvas/sidebar/CanvasNavigator.tsx
12
- /** Blocks behind a bordered toggle, collapsed until opened. */
13
- function CollapsedBlocks(t0) {
14
- const $ = c(14);
15
- const { label, children } = t0;
16
- const [expanded, setExpanded] = React.useState(false);
17
- let t1;
18
- let t2;
19
- let t3;
20
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21
- t1 = cn("hover:bg-accent/75 flex w-full cursor-default items-center gap-2 px-3 py-2 text-left text-sm", "focus-visible:rounded-none focus-visible:ring-inset");
22
- t2 = () => setExpanded(_temp);
23
- t3 = /* @__PURE__ */ jsx(ChevronsUpDown, { className: "text-muted-foreground size-4 shrink-0" });
24
- $[0] = t1;
25
- $[1] = t2;
26
- $[2] = t3;
27
- } else {
28
- t1 = $[0];
29
- t2 = $[1];
30
- t3 = $[2];
31
- }
32
- let t4;
33
- if ($[3] !== label) {
34
- t4 = /* @__PURE__ */ jsx("span", {
35
- className: "truncate",
36
- children: label
37
- });
38
- $[3] = label;
39
- $[4] = t4;
40
- } else t4 = $[4];
41
- let t5;
42
- if ($[5] !== expanded || $[6] !== t4) {
43
- t5 = /* @__PURE__ */ jsxs("button", {
44
- type: "button",
45
- "aria-expanded": expanded,
46
- className: t1,
47
- onClick: t2,
48
- children: [t3, t4]
49
- });
50
- $[5] = expanded;
51
- $[6] = t4;
52
- $[7] = t5;
53
- } else t5 = $[7];
54
- let t6;
55
- if ($[8] !== children || $[9] !== expanded) {
56
- t6 = expanded && /* @__PURE__ */ jsx("div", {
57
- className: "border-input border-t p-1",
58
- children
59
- });
60
- $[8] = children;
61
- $[9] = expanded;
62
- $[10] = t6;
63
- } else t6 = $[10];
64
- let t7;
65
- if ($[11] !== t5 || $[12] !== t6) {
66
- t7 = /* @__PURE__ */ jsxs("div", {
67
- className: "border-input flex flex-col overflow-hidden rounded-lg border shadow-xs",
68
- children: [t5, t6]
69
- });
70
- $[11] = t5;
71
- $[12] = t6;
72
- $[13] = t7;
73
- } else t7 = $[13];
74
- return t7;
75
- }
76
- function _temp(value) {
77
- return !value;
78
- }
79
- function canvasItemLabel(item) {
80
- return item.kind === "page" ? item.page.title : item.block._internal.title;
81
- }
82
- /** Search an item by its label, and a Page by its path too, never by its key. */
83
- function canvasItemKeywords(item) {
84
- const label = canvasItemLabel(item);
85
- const path = item.kind === "page" ? canvasPagePath(item.page) : null;
86
- return path ? [label, path] : [label];
87
- }
88
11
  /**
89
- * The Canvas Navigator, shown while nothing is selected: every item of the Canvas,
90
- * in Canvas order, grouped under its shelf with its count, and a search that takes
91
- * focus. Pages show with their path; blocks start collapsed, though a search still
92
- * finds them. Picking an item shows it: the camera goes to it.
12
+ * The Canvas Navigator, shown while nothing is selected: every Page of the Canvas,
13
+ * in Canvas order, with its path, and a search that takes focus. Picking a Page
14
+ * shows it: the camera goes to it.
93
15
  */
94
16
  function CanvasNavigator(t0) {
95
- const $ = c(20);
96
- const { shelves, selectedKey, onShowItem } = t0;
17
+ const $ = c(17);
18
+ const { pages, selectedKey, onShowPage } = t0;
97
19
  const [search, setSearch] = React.useState("");
98
20
  let t1;
99
21
  if ($[0] !== search) {
100
- t1 = search.trim();
101
- $[0] = search;
102
- $[1] = t1;
103
- } else t1 = $[1];
104
- const searching = t1 !== "";
105
- let t2;
106
- if ($[2] !== onShowItem || $[3] !== selectedKey) {
107
- t2 = (item) => {
108
- const selected = item.key === selectedKey;
109
- const path = item.kind === "page" ? canvasPagePath(item.page) : null;
110
- return /* @__PURE__ */ jsx(CommandItem, {
111
- value: item.key,
112
- keywords: canvasItemKeywords(item),
113
- "data-checked": selected,
114
- "aria-current": selected ? "true" : void 0,
115
- onSelect: () => onShowItem(item.key),
116
- children: item.kind === "page" ? /* @__PURE__ */ jsxs("div", {
117
- className: "flex min-w-0 flex-col py-0.5",
118
- children: [/* @__PURE__ */ jsx("span", {
119
- className: "truncate font-medium",
120
- children: canvasItemLabel(item)
121
- }), path && /* @__PURE__ */ jsx("span", {
122
- className: "text-muted-foreground truncate font-mono text-xs",
123
- children: path
124
- })]
125
- }) : /* @__PURE__ */ jsx("span", {
126
- className: "truncate",
127
- children: canvasItemLabel(item)
128
- })
129
- }, item.key);
130
- };
131
- $[2] = onShowItem;
132
- $[3] = selectedKey;
133
- $[4] = t2;
134
- } else t2 = $[4];
135
- const renderItem = t2;
136
- let t3;
137
- if ($[5] !== search) {
138
- t3 = /* @__PURE__ */ jsx(CommandInput, {
139
- placeholder: "Search the Canvas…",
22
+ t1 = /* @__PURE__ */ jsx(CommandInput, {
23
+ placeholder: "Search pages…",
140
24
  autoFocus: true,
141
25
  value: search,
142
26
  onValueChange: setSearch
143
27
  });
144
- $[5] = search;
145
- $[6] = t3;
146
- } else t3 = $[6];
28
+ $[0] = search;
29
+ $[1] = t1;
30
+ } else t1 = $[1];
31
+ let t2;
32
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
33
+ t2 = /* @__PURE__ */ jsx(CommandEmpty, { children: "Nothing found." });
34
+ $[2] = t2;
35
+ } else t2 = $[2];
36
+ const t3 = `Pages (${pages.length})`;
147
37
  let t4;
148
- if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
149
- t4 = /* @__PURE__ */ jsx(CommandEmpty, { children: "Nothing found." });
150
- $[7] = t4;
151
- } else t4 = $[7];
152
- let t5;
153
- if ($[8] !== renderItem || $[9] !== searching || $[10] !== shelves) {
154
- let t6;
155
- if ($[12] !== renderItem || $[13] !== searching) {
156
- t6 = (shelf) => {
157
- const heading = `${shelf.label} (${shelf.items.length})`;
158
- if (shelf.key === "shelf:pages") return /* @__PURE__ */ jsxs(CommandGroup, {
159
- heading,
160
- children: [shelf.items.map(renderItem), /* @__PURE__ */ jsx("div", {
161
- className: "p-1",
162
- children: /* @__PURE__ */ jsxs(Button, {
163
- type: "button",
164
- variant: "secondary",
165
- className: "w-full",
166
- onClick: _temp3,
167
- children: [/* @__PURE__ */ jsx(Plus, {}), "Create page"]
168
- })
169
- })]
170
- }, shelf.key);
171
- return /* @__PURE__ */ jsx(CommandGroup, {
172
- heading: searching ? heading : void 0,
173
- children: searching || shelf.items.length === 0 ? shelf.items.map(renderItem) : /* @__PURE__ */ jsx("div", {
174
- className: "p-1",
175
- children: /* @__PURE__ */ jsx(CollapsedBlocks, {
176
- label: `${shelf.items.length} ${shelf.items.length === 1 ? "block" : "blocks"}`,
177
- children: shelf.items.map(renderItem)
178
- })
38
+ if ($[3] !== onShowPage || $[4] !== pages || $[5] !== selectedKey) {
39
+ let t5;
40
+ if ($[7] !== onShowPage || $[8] !== selectedKey) {
41
+ t5 = (page) => {
42
+ const selected = page.key === selectedKey;
43
+ const path = canvasPagePath(page);
44
+ return /* @__PURE__ */ jsx(CommandItem, {
45
+ value: page.key,
46
+ keywords: path ? [page.title, path] : [page.title],
47
+ "data-checked": selected,
48
+ "aria-current": selected ? "true" : void 0,
49
+ onSelect: () => onShowPage(page.key),
50
+ children: /* @__PURE__ */ jsxs("div", {
51
+ className: "flex min-w-0 flex-col py-0.5",
52
+ children: [/* @__PURE__ */ jsx("span", {
53
+ className: "truncate font-medium",
54
+ children: page.title
55
+ }), path && /* @__PURE__ */ jsx("span", {
56
+ className: "text-muted-foreground truncate font-mono text-xs",
57
+ children: path
58
+ })]
179
59
  })
180
- }, shelf.key);
60
+ }, page.key);
181
61
  };
182
- $[12] = renderItem;
183
- $[13] = searching;
184
- $[14] = t6;
185
- } else t6 = $[14];
186
- t5 = shelves.map(t6);
187
- $[8] = renderItem;
188
- $[9] = searching;
189
- $[10] = shelves;
190
- $[11] = t5;
191
- } else t5 = $[11];
62
+ $[7] = onShowPage;
63
+ $[8] = selectedKey;
64
+ $[9] = t5;
65
+ } else t5 = $[9];
66
+ t4 = pages.map(t5);
67
+ $[3] = onShowPage;
68
+ $[4] = pages;
69
+ $[5] = selectedKey;
70
+ $[6] = t4;
71
+ } else t4 = $[6];
72
+ let t5;
73
+ if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
74
+ t5 = /* @__PURE__ */ jsx("div", {
75
+ className: "p-1",
76
+ children: /* @__PURE__ */ jsxs(Button, {
77
+ type: "button",
78
+ variant: "secondary",
79
+ className: "w-full",
80
+ onClick: _temp2,
81
+ children: [/* @__PURE__ */ jsx(Plus, {}), "Create page"]
82
+ })
83
+ });
84
+ $[10] = t5;
85
+ } else t5 = $[10];
192
86
  let t6;
193
- if ($[15] !== t5) {
87
+ if ($[11] !== t3 || $[12] !== t4) {
194
88
  t6 = /* @__PURE__ */ jsxs(CommandList, {
195
89
  className: "max-h-none flex-1",
196
- children: [t4, t5]
90
+ children: [t2, /* @__PURE__ */ jsxs(CommandGroup, {
91
+ heading: t3,
92
+ children: [t4, t5]
93
+ })]
197
94
  });
198
- $[15] = t5;
199
- $[16] = t6;
200
- } else t6 = $[16];
95
+ $[11] = t3;
96
+ $[12] = t4;
97
+ $[13] = t6;
98
+ } else t6 = $[13];
201
99
  let t7;
202
- if ($[17] !== t3 || $[18] !== t6) {
100
+ if ($[14] !== t1 || $[15] !== t6) {
203
101
  t7 = /* @__PURE__ */ jsx("div", {
204
102
  "data-canvas-navigator": true,
205
103
  className: "flex min-h-0 flex-1 flex-col",
206
104
  children: /* @__PURE__ */ jsxs(Command$1, {
207
105
  className: "rounded-none! bg-transparent",
208
- filter: _temp2,
209
- children: [t3, t6]
106
+ filter: _temp,
107
+ children: [t1, t6]
210
108
  })
211
109
  });
212
- $[17] = t3;
213
- $[18] = t6;
214
- $[19] = t7;
215
- } else t7 = $[19];
110
+ $[14] = t1;
111
+ $[15] = t6;
112
+ $[16] = t7;
113
+ } else t7 = $[16];
216
114
  return t7;
217
115
  }
218
- function _temp3() {
116
+ function _temp2() {
219
117
  return previewStore.send({ type: "openCreatePageModal" });
220
118
  }
221
- function _temp2(_value, search_0, keywords) {
119
+ function _temp(_value, search_0, keywords) {
222
120
  return commandFilter(keywords?.join(" ") ?? "", search_0);
223
121
  }
224
122
 
@@ -1,7 +1,6 @@
1
1
  import { CanvasSidebarFrame } from "./CanvasSidebarFrame.js";
2
2
  import { CreatePageModal } from "../../preview/components/CreatePageModal.js";
3
3
  import { CanvasNavigator } from "./CanvasNavigator.js";
4
- import { CatalogBlockInspector } from "./CatalogBlockInspector.js";
5
4
  import { PageInspector } from "./PageInspector.js";
6
5
  import { c } from "react/compiler-runtime";
7
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -9,50 +8,45 @@ import { jsx, jsxs } from "react/jsx-runtime";
9
8
  //#region src/features/canvas/sidebar/CanvasSidebar.tsx
10
9
  /**
11
10
  * The sidebar beside the Canvas. Without a selection, it shows the Canvas Navigator;
12
- * with one, the Canvas Inspector of the selected Page or block of the Block Catalog.
11
+ * with one, the Canvas Inspector of the selected Page.
13
12
  */
14
13
  function CanvasSidebar(t0) {
15
- const $ = c(12);
16
- const { page, catalog, navigator, onShowWholeCanvas, onRenamePage, owner: t1, pageRecord } = t0;
14
+ const $ = c(11);
15
+ const { page, navigator, onShowWholeCanvas, onRenamePage, owner: t1, pageRecord } = t0;
17
16
  const owner = t1 === void 0 ? null : t1;
18
- const t2 = catalog?.block._internal.title ?? page?.title ?? "Canvas navigator";
17
+ const t2 = page?.title ?? "Canvas navigator";
19
18
  let t3;
20
- if ($[0] !== catalog || $[1] !== navigator || $[2] !== onRenamePage || $[3] !== onShowWholeCanvas || $[4] !== owner || $[5] !== page || $[6] !== pageRecord) {
21
- t3 = catalog ? /* @__PURE__ */ jsx(CatalogBlockInspector, {
22
- block: catalog.block,
23
- projectId: catalog.projectId,
24
- onBack: onShowWholeCanvas
25
- }) : page ? /* @__PURE__ */ jsx(PageInspector, {
19
+ if ($[0] !== navigator || $[1] !== onRenamePage || $[2] !== onShowWholeCanvas || $[3] !== owner || $[4] !== page || $[5] !== pageRecord) {
20
+ t3 = page ? /* @__PURE__ */ jsx(PageInspector, {
26
21
  page,
27
22
  owner,
28
23
  pageRecord,
29
24
  onBack: onShowWholeCanvas,
30
25
  onRenamePage
31
26
  }) : /* @__PURE__ */ jsx(CanvasNavigator, { ...navigator });
32
- $[0] = catalog;
33
- $[1] = navigator;
34
- $[2] = onRenamePage;
35
- $[3] = onShowWholeCanvas;
36
- $[4] = owner;
37
- $[5] = page;
38
- $[6] = pageRecord;
39
- $[7] = t3;
40
- } else t3 = $[7];
27
+ $[0] = navigator;
28
+ $[1] = onRenamePage;
29
+ $[2] = onShowWholeCanvas;
30
+ $[3] = owner;
31
+ $[4] = page;
32
+ $[5] = pageRecord;
33
+ $[6] = t3;
34
+ } else t3 = $[6];
41
35
  let t4;
42
- if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
36
+ if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
43
37
  t4 = /* @__PURE__ */ jsx(CreatePageModal, {});
44
- $[8] = t4;
45
- } else t4 = $[8];
38
+ $[7] = t4;
39
+ } else t4 = $[7];
46
40
  let t5;
47
- if ($[9] !== t2 || $[10] !== t3) {
41
+ if ($[8] !== t2 || $[9] !== t3) {
48
42
  t5 = /* @__PURE__ */ jsxs(CanvasSidebarFrame, {
49
43
  label: t2,
50
44
  children: [t3, t4]
51
45
  });
52
- $[9] = t2;
53
- $[10] = t3;
54
- $[11] = t5;
55
- } else t5 = $[11];
46
+ $[8] = t2;
47
+ $[9] = t3;
48
+ $[10] = t5;
49
+ } else t5 = $[10];
56
50
  return t5;
57
51
  }
58
52
 
@@ -1,7 +1,7 @@
1
1
  import { useProjectUuid } from "../../../lib/auth.js";
2
2
  import { reads, useRead } from "../../../lib/studio-reads.js";
3
3
  import { useLocalePath } from "../../../lib/view-locale.js";
4
- import { AddBlockDialog } from "../../preview/components/AddBlockDialog.js";
4
+ import { AddBlockSheet } from "../../preview/components/AddBlockSheet.js";
5
5
  import { PreviewEditingOwnerContext } from "../../preview/previewSelection.js";
6
6
  import { PageInfo } from "../../preview/components/PageInfo.js";
7
7
  import { PageTree } from "../../preview/components/PageTree.js";
@@ -51,7 +51,7 @@ function CuratedPageInspector() {
51
51
  const t3 = owner?.kind === "page" && owner.pageId === pageId;
52
52
  let t4;
53
53
  if ($[6] !== t3) {
54
- t4 = /* @__PURE__ */ jsx(AddBlockDialog, { focusCreatedBlock: t3 });
54
+ t4 = /* @__PURE__ */ jsx(AddBlockSheet, { focusCreatedBlock: t3 });
55
55
  $[6] = t3;
56
56
  $[7] = t4;
57
57
  } else t4 = $[7];
@@ -71,13 +71,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
71
71
  };
72
72
  }, []);
73
73
  /**
74
- * Pan immediately so a world x sits at or right of a screen x; never pans
75
- * otherwise, nor while a selection holds the camera.
76
- */
77
- const keepInView = React.useCallback((worldX, screenX) => {
78
- flightControls.current?.keepInView(worldX, screenX);
79
- }, []);
80
- /**
81
74
  * Pan vertically to an element inside a Canvas frame, such as a selected block,
82
75
  * unless enough of it already shows. A page flight or a drag in progress keeps the camera.
83
76
  */
@@ -433,18 +426,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
433
426
  };
434
427
  flightControls.current = {
435
428
  flyTo: startFlight,
436
- keepInView: (worldX, screenX) => {
437
- const x = screenX - worldX * camera.scale;
438
- if (selection || camera.x >= x) return;
439
- cancelCameraFlight();
440
- restoredCamera = void 0;
441
- fittingInitialPage = false;
442
- gesture = void 0;
443
- applyCamera({
444
- ...camera,
445
- x
446
- }, true);
447
- },
448
429
  reveal: (element, immediate) => {
449
430
  const frame = element.ownerDocument.defaultView?.frameElement;
450
431
  if (!frame || flight && !flight.pan || pointers.size) return;
@@ -793,7 +774,6 @@ function useCanvasCamera(workspaceKey, initialTarget, row) {
793
774
  contentRef,
794
775
  flyTo,
795
776
  mirrorCamera,
796
- keepInView,
797
777
  reveal,
798
778
  fitAll
799
779
  };
@@ -20,7 +20,7 @@ function toError(error) {
20
20
  * Unless live, the frame's document is paused. A resting frame keeps its size:
21
21
  * its content is hidden, and must not shrink the frame and move the Canvas.
22
22
  */
23
- function useCanvasFrame({ previewDocument, href, width, viewportHeight, fit, subject, mode }) {
23
+ function useCanvasFrame({ previewDocument, href, width, viewportHeight, mode }) {
24
24
  const iframeRef = React.useRef(null);
25
25
  const [srcDoc, setSrcDoc] = React.useState();
26
26
  const [mount, setMount] = React.useState(null);
@@ -44,7 +44,7 @@ function useCanvasFrame({ previewDocument, href, width, viewportHeight, fit, sub
44
44
  React.useLayoutEffect(() => {
45
45
  const iframe = iframeRef.current;
46
46
  if (!iframe || !mount) return;
47
- const stop = observeCanvasDocument(iframe, mount, viewportHeight, fit, () => restingRef.current);
47
+ const stop = observeCanvasDocument(iframe, mount, viewportHeight, () => restingRef.current);
48
48
  sizing.current = stop;
49
49
  return () => {
50
50
  stop();
@@ -53,8 +53,7 @@ function useCanvasFrame({ previewDocument, href, width, viewportHeight, fit, sub
53
53
  }, [
54
54
  mount,
55
55
  viewportHeight,
56
- width,
57
- fit
56
+ width
58
57
  ]);
59
58
  React.useEffect(() => {
60
59
  if (!mount || live) return;
@@ -73,7 +72,7 @@ function useCanvasFrame({ previewDocument, href, width, viewportHeight, fit, sub
73
72
  try {
74
73
  const loaded = iframeRef.current;
75
74
  const root = (loaded?.contentDocument)?.querySelector("[data-camox-preview-root]");
76
- if (!root) throw new Error(`The ${subject} preview document has no content root.`);
75
+ if (!root) throw new Error("The page preview document has no content root.");
77
76
  if (root === mount) return;
78
77
  root.replaceChildren();
79
78
  setMount(root);
@@ -82,7 +81,7 @@ function useCanvasFrame({ previewDocument, href, width, viewportHeight, fit, sub
82
81
  setFailure(toError(error));
83
82
  }
84
83
  },
85
- onError: () => setFailure(/* @__PURE__ */ new Error(`The ${subject} preview document could not load.`))
84
+ onError: () => setFailure(/* @__PURE__ */ new Error("The page preview document could not load."))
86
85
  }
87
86
  };
88
87
  }
@@ -20,7 +20,7 @@ import { DerivedPageContent } from "../page/DerivedPageContent.js";
20
20
  import { STUDIO_CANVAS_PATH } from "../studio/routes.js";
21
21
  import { studioStore } from "../studio/studioStore.js";
22
22
  import { previewStore, selectIsCanvasShown } from "./previewStore.js";
23
- import { AddBlockDialog } from "./components/AddBlockDialog.js";
23
+ import { AddBlockSheet } from "./components/AddBlockSheet.js";
24
24
  import { CanvasPreview, prefetchCanvas } from "./components/CanvasPreview.js";
25
25
  import { CreatePageModal } from "./components/CreatePageModal.js";
26
26
  import { PreviewEditingOwnerContext } from "./previewSelection.js";
@@ -667,7 +667,7 @@ const PreviewShell = (t0) => {
667
667
  t3 = () => {
668
668
  if (!isMobileStudio) return;
669
669
  previewStore.send({ type: "hideCanvas" });
670
- previewStore.send({ type: "closeAddBlockDialog" });
670
+ previewStore.send({ type: "closeAddBlockSheet" });
671
671
  };
672
672
  t4 = [isMobileStudio];
673
673
  $[4] = isMobileStudio;
@@ -823,7 +823,7 @@ const PreviewShell = (t0) => {
823
823
  } else t18 = $[33];
824
824
  let t19;
825
825
  if ($[34] !== isCanvasEnabled || $[35] !== pageId) {
826
- t19 = !isCanvasEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CreatePageModal, {}), pageId != null && /* @__PURE__ */ jsx(AddBlockDialog, {})] });
826
+ t19 = !isCanvasEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CreatePageModal, {}), pageId != null && /* @__PURE__ */ jsx(AddBlockSheet, {})] });
827
827
  $[34] = isCanvasEnabled;
828
828
  $[35] = pageId;
829
829
  $[36] = t19;
@@ -14,12 +14,12 @@ import { generateKeyBetween } from "fractional-indexing";
14
14
  import { useSelector } from "@xstate/store-react";
15
15
  import { jsx, jsxs } from "react/jsx-runtime";
16
16
  import { useMutation } from "@tanstack/react-query";
17
- import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@camox/ui/dialog";
17
+ import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@camox/ui/sheet";
18
18
  import { Badge } from "@camox/ui/badge";
19
19
  import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, commandFilter } from "@camox/ui/command";
20
20
 
21
- //#region src/features/preview/components/AddBlockDialog.tsx
22
- const AddBlockDialog = ({ focusCreatedBlock = true }) => {
21
+ //#region src/features/preview/components/AddBlockSheet.tsx
22
+ const AddBlockSheet = ({ focusCreatedBlock = true }) => {
23
23
  const [highlightedValue, setHighlightedValue] = React.useState("");
24
24
  const [search, setSearch] = React.useState("");
25
25
  const [error, setError] = React.useState(null);
@@ -32,7 +32,7 @@ const AddBlockDialog = ({ focusCreatedBlock = true }) => {
32
32
  };
33
33
  }, []);
34
34
  const gridRef = React.useRef(null);
35
- const dialog = useSelector(previewStore, (state) => state.context.addBlockDialog);
35
+ const sheet = useSelector(previewStore, (state) => state.context.addBlockSheet);
36
36
  const studioCache = useStudioCache();
37
37
  const projectUuid = useProjectUuid();
38
38
  const pathname = useLocalePath();
@@ -117,15 +117,15 @@ const AddBlockDialog = ({ focusCreatedBlock = true }) => {
117
117
  for (const block of pageBlocks) counts[block.type] = (counts[block.type] ?? 0) + 1;
118
118
  return counts;
119
119
  }, [page, pageBlocks]);
120
- const [previousDialog, setPreviousDialog] = React.useState(dialog);
121
- if (dialog !== previousDialog) {
122
- setPreviousDialog(dialog);
120
+ const [previousSheet, setPreviousSheet] = React.useState(sheet);
121
+ if (sheet !== previousSheet) {
122
+ setPreviousSheet(sheet);
123
123
  setHighlightedValue("");
124
124
  setSearch("");
125
125
  setError(null);
126
126
  }
127
127
  const handleAddBlock = async (block) => {
128
- if (!page || !dialog || submitting.current) return;
128
+ if (!page || !sheet || submitting.current) return;
129
129
  submitting.current = true;
130
130
  setError(null);
131
131
  const owner = {
@@ -140,21 +140,21 @@ const AddBlockDialog = ({ focusCreatedBlock = true }) => {
140
140
  type: block._internal.id,
141
141
  content: bundle.content,
142
142
  settings: bundle.settings,
143
- afterPosition: dialog.afterPosition,
143
+ afterPosition: sheet.afterPosition,
144
144
  repeatableItems: bundle.repeatableItems
145
145
  });
146
146
  const context = previewStore.getSnapshot().context;
147
147
  if (!mounted.current) return;
148
148
  if (focusCreatedBlock && !sameEditingOwner(context.editingContext, owner)) return;
149
- if (context.addBlockDialog !== dialog) return;
150
- previewStore.send({ type: "closeAddBlockDialog" });
149
+ if (context.addBlockSheet !== sheet) return;
150
+ previewStore.send({ type: "closeAddBlockSheet" });
151
151
  if (focusCreatedBlock) previewStore.send({
152
152
  type: "focusCreatedBlock",
153
153
  ...owner,
154
154
  blockId
155
155
  });
156
156
  } catch {
157
- if (previewStore.getSnapshot().context.addBlockDialog === dialog) setError("Could not add this block. Please try again.");
157
+ if (previewStore.getSnapshot().context.addBlockSheet === sheet) setError("Could not add this block. Please try again.");
158
158
  } finally {
159
159
  submitting.current = false;
160
160
  }
@@ -165,24 +165,25 @@ const AddBlockDialog = ({ focusCreatedBlock = true }) => {
165
165
  const page = pageCounts[blockId] ?? "none";
166
166
  return `${total} use${total > 1 ? "s" : ""} (${page} here)`;
167
167
  };
168
- return /* @__PURE__ */ jsx(Dialog, {
169
- open: dialog != null,
168
+ return /* @__PURE__ */ jsx(Sheet, {
169
+ open: sheet != null,
170
170
  onOpenChange: (open) => {
171
- if (!open && !submitting.current) previewStore.send({ type: "closeAddBlockDialog" });
171
+ if (!open && !submitting.current) previewStore.send({ type: "closeAddBlockSheet" });
172
172
  },
173
- children: /* @__PURE__ */ jsxs(DialogContent, {
174
- className: "flex h-[min(85dvh,900px)] flex-col gap-4 overflow-hidden sm:max-w-6xl",
173
+ children: /* @__PURE__ */ jsxs(SheetContent, {
174
+ side: "right",
175
+ className: "gap-0 overflow-hidden data-[side=right]:w-full data-[side=right]:sm:max-w-4xl",
175
176
  children: [
176
- /* @__PURE__ */ jsxs(DialogHeader, {
177
- className: "pr-8",
178
- children: [/* @__PURE__ */ jsx(DialogTitle, { children: "Add a block" }), /* @__PURE__ */ jsx(DialogDescription, { children: "Search blocks, use the arrow keys to browse, and press Enter to add." })]
177
+ /* @__PURE__ */ jsxs(SheetHeader, {
178
+ className: "pr-12",
179
+ children: [/* @__PURE__ */ jsx(SheetTitle, { children: "Add a block" }), /* @__PURE__ */ jsx(SheetDescription, { children: "Search blocks, use the arrow keys to browse, and press Enter to add." })]
179
180
  }),
180
181
  /* @__PURE__ */ jsxs(Command, {
181
182
  label: "Search blocks",
182
183
  shouldFilter: false,
183
184
  value: selectedValue,
184
185
  onValueChange: setHighlightedValue,
185
- className: "h-0 min-h-0 flex-1 rounded-none! bg-transparent! p-0!",
186
+ className: "h-0 min-h-0 flex-1 rounded-none! bg-transparent! px-4! pt-0! pb-0!",
186
187
  onKeyDown: (e) => {
187
188
  if (e.nativeEvent.isComposing || e.altKey || e.ctrlKey || e.metaKey) return;
188
189
  if (![
@@ -214,11 +215,11 @@ const AddBlockDialog = ({ focusCreatedBlock = true }) => {
214
215
  autoFocus: true
215
216
  }), /* @__PURE__ */ jsxs(CommandList, {
216
217
  label: "Available blocks",
217
- className: "mt-4 max-h-none min-h-0 flex-1 pb-2",
218
+ className: "mt-4 max-h-none min-h-0 flex-1 pb-4",
218
219
  "aria-busy": createBlock.isPending,
219
220
  children: [/* @__PURE__ */ jsx(CommandEmpty, { children: "No blocks found." }), /* @__PURE__ */ jsx(CommandGroup, {
220
221
  ref: gridRef,
221
- className: "p-0 [&>[cmdk-group-items]]:grid [&>[cmdk-group-items]]:grid-cols-1 [&>[cmdk-group-items]]:gap-4 sm:[&>[cmdk-group-items]]:grid-cols-2 lg:[&>[cmdk-group-items]]:grid-cols-3",
222
+ className: "@container p-0 [&>[cmdk-group-items]]:grid [&>[cmdk-group-items]]:grid-cols-1 [&>[cmdk-group-items]]:gap-4 @xl:[&>[cmdk-group-items]]:grid-cols-2 @3xl:[&>[cmdk-group-items]]:grid-cols-3",
222
223
  children: filteredBlocks.map((block) => /* @__PURE__ */ jsxs(CommandItem, {
223
224
  value: block._internal.id,
224
225
  keywords: [block._internal.title, block._internal.description ?? ""],
@@ -252,14 +253,16 @@ const AddBlockDialog = ({ focusCreatedBlock = true }) => {
252
253
  })]
253
254
  })]
254
255
  }),
255
- createBlock.isPending && /* @__PURE__ */ jsx("p", {
256
- role: "status",
257
- children: "Adding block…"
258
- }),
259
- error && /* @__PURE__ */ jsx("p", {
260
- role: "alert",
261
- className: "text-destructive",
262
- children: error
256
+ (createBlock.isPending || error) && /* @__PURE__ */ jsxs("div", {
257
+ className: "border-border border-t p-4",
258
+ children: [createBlock.isPending && /* @__PURE__ */ jsx("p", {
259
+ role: "status",
260
+ children: "Adding block…"
261
+ }), error && /* @__PURE__ */ jsx("p", {
262
+ role: "alert",
263
+ className: "text-destructive",
264
+ children: error
265
+ })]
263
266
  })
264
267
  ]
265
268
  })
@@ -267,4 +270,4 @@ const AddBlockDialog = ({ focusCreatedBlock = true }) => {
267
270
  };
268
271
 
269
272
  //#endregion
270
- export { AddBlockDialog };
273
+ export { AddBlockSheet };