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
@@ -10,10 +10,10 @@ import { PreviewEditingOwnerContext } from "../preview/previewSelection.js";
10
10
  import { Overlays } from "../preview/components/Overlays.js";
11
11
  import { PreviewFrameEffects } from "../preview/components/PreviewPanel.js";
12
12
  import { PageContent } from "../preview/CamoxPreview.js";
13
- import { CanvasFrameProviders, preventLinkNavigation, useCanvasFrame } from "./useCanvasFrame.js";
14
13
  import { CanvasOverlays } from "./CanvasOverlays.js";
15
14
  import "../preview/EditablePageContent.js";
16
15
  import { useFrameSettled } from "./canvasThumbnails.js";
16
+ import { CanvasFrameProviders, preventLinkNavigation, useCanvasFrame } from "./useCanvasFrame.js";
17
17
  import { c } from "react/compiler-runtime";
18
18
  import * as React from "react";
19
19
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -115,8 +115,6 @@ function PageFrame(t0) {
115
115
  href,
116
116
  width,
117
117
  viewportHeight,
118
- fit: "page",
119
- subject: "page",
120
118
  mode
121
119
  };
122
120
  $[5] = href;
@@ -13,12 +13,31 @@ import overlayStyles from "virtual:camox-overlay-css";
13
13
 
14
14
  //#region src/features/canvas/CanvasPreviewPageSlot.tsx
15
15
  /**
16
+ * An element's place in the Canvas row, in world space. Its own offset is only
17
+ * relative to its nearest positioned ancestor, such as its Page, so the offsets
18
+ * add up to the row.
19
+ */
20
+ function canvasRowOffset(element, row) {
21
+ let left = 0;
22
+ let top = 0;
23
+ let current = element;
24
+ while (current && current !== row) {
25
+ left += current.offsetLeft;
26
+ top += current.offsetTop;
27
+ current = current.offsetParent;
28
+ }
29
+ return {
30
+ left,
31
+ top
32
+ };
33
+ }
34
+ /**
16
35
  * The selected Page's place in the Canvas row, where the Preview's own page
17
36
  * frame shows instead of a frame of the Canvas. Toggling the Canvas then never remounts
18
37
  * the page, and neither does deselecting it, which keeps it in the frame. The slot takes the frame's height, and draws the Canvas overlays
19
38
  * over it.
20
39
  */
21
- function CanvasPreviewPageSlot({ pageId, layoutId, width, viewportHeight, selected, hovered, hidden = false, shaded = false, onActivate }) {
40
+ function CanvasPreviewPageSlot({ pageId, layoutId, width, viewportHeight, selected, hovered, shaded = false, onActivate }) {
22
41
  const ref = React.useRef(null);
23
42
  const owner = useEditingOwner({
24
43
  pageId,
@@ -41,16 +60,17 @@ function CanvasPreviewPageSlot({ pageId, layoutId, width, viewportHeight, select
41
60
  React.useLayoutEffect(() => {
42
61
  const element = ref.current;
43
62
  const viewport = element?.closest("[data-camox-canvas]");
44
- if (!frame || !element || !viewport) return;
63
+ const row = element?.closest("[data-canvas-items]");
64
+ if (!frame || !element || !viewport || !row) return;
65
+ const { left, top } = canvasRowOffset(element, row);
45
66
  const next = {
46
67
  viewport,
47
- left: element.offsetLeft,
48
- top: element.offsetTop,
68
+ left,
69
+ top,
49
70
  width,
50
71
  viewportHeight,
51
72
  mode,
52
73
  selected,
53
- hidden,
54
74
  workspace,
55
75
  onInteract
56
76
  };
@@ -100,4 +120,4 @@ function CanvasPreviewPageSlot({ pageId, layoutId, width, viewportHeight, select
100
120
  }
101
121
 
102
122
  //#endregion
103
- export { CanvasPreviewPageSlot };
123
+ export { CanvasPreviewPageSlot, canvasRowOffset };
@@ -131,7 +131,7 @@ function fitCanvasPage(viewport, page) {
131
131
  const scale = Math.max(Number.EPSILON, Math.min(2, (viewport.width - padding * 2) / Math.max(1, page.width)));
132
132
  return {
133
133
  x: (viewport.width - page.width * scale) / 2 - page.left * scale,
134
- y: Math.min(108, viewport.height / 3),
134
+ y: Math.min(76, viewport.height / 3),
135
135
  scale
136
136
  };
137
137
  }
@@ -143,11 +143,11 @@ function placeCanvasPageAnchor(viewport, page, documentY, scale, frameOffsetY =
143
143
  scale
144
144
  };
145
145
  }
146
- /** Reserve readable padding, unscaled Shelf Markers and page headers in the fitted overview. */
146
+ /** Reserve readable padding and unscaled page headers in the fitted overview. */
147
147
  function canvasInsets(viewport) {
148
148
  return {
149
149
  x: Math.min(64, viewport.width / 4),
150
- top: Math.min(148, viewport.height / 3),
150
+ top: Math.min(116, viewport.height / 3),
151
151
  bottom: Math.min(64, viewport.height / 4)
152
152
  };
153
153
  }
@@ -166,27 +166,12 @@ function pauseCanvasDocument(doc) {
166
166
  for (const element of media) if (element.isConnected && element.paused) element.play().catch(() => {});
167
167
  };
168
168
  }
169
- /**
170
- * The stylesheets of a site document's head, which shape how any content
171
- * renders in it. Pages of a site share them, while their titles, metadata and
172
- * preloads differ.
173
- */
174
- function canvasDocumentStyles(previewDocument) {
175
- if (!previewDocument) return "";
176
- const doc = new DOMParser().parseFromString(previewDocument, "text/html");
177
- return Array.from(doc.head.querySelectorAll("style, link[rel=\"stylesheet\"]")).map((style) => style.outerHTML).join("\n");
178
- }
179
- function measureCanvasDocument(iframe, root, baseline, fit = "page") {
169
+ function measureCanvasDocument(iframe, root, baseline) {
180
170
  const doc = root.ownerDocument;
181
171
  adaptCanvasStyleSheets(doc);
182
172
  iframe.style.height = `${baseline}px`;
183
173
  const bodyRect = doc.body.getBoundingClientRect();
184
174
  const rootRect = root.getBoundingClientRect();
185
- if (fit === "content") {
186
- const height = exactBottom(rootRect.bottom, root.scrollHeight + rootRect.top);
187
- iframe.style.height = `${Math.max(1, height)}px`;
188
- return;
189
- }
190
175
  const height = exactBottom(Math.max(rootRect.bottom, bodyRect.bottom), doc.body.scrollHeight + bodyRect.top, doc.documentElement.scrollHeight, root.scrollHeight + rootRect.top);
191
176
  iframe.style.height = `${Math.max(baseline, height)}px`;
192
177
  }
@@ -208,7 +193,7 @@ function exactBottom(boxesBottom, ...scrollBottoms) {
208
193
  * While `suspended` (a resting frame, whose content is hidden), the frame keeps its size.
209
194
  * Once `resume` is called, it re-measures only if its content or size changed meanwhile.
210
195
  */
211
- function observeCanvasDocument(iframe, root, viewportHeight, fit = "page", suspended = () => false) {
196
+ function observeCanvasDocument(iframe, root, viewportHeight, suspended = () => false) {
212
197
  const doc = root.ownerDocument;
213
198
  const baseline = Math.max(1, viewportHeight);
214
199
  doc.documentElement.style.setProperty("--camox-viewport-height", `${baseline}px`);
@@ -242,7 +227,7 @@ function observeCanvasDocument(iframe, root, viewportHeight, fit = "page", suspe
242
227
  function measure() {
243
228
  frame = 0;
244
229
  if (suspended()) return;
245
- measureCanvasDocument(iframe, root, baseline, fit);
230
+ measureCanvasDocument(iframe, root, baseline);
246
231
  const elements = /* @__PURE__ */ new Set([
247
232
  doc.body,
248
233
  root,
@@ -314,4 +299,4 @@ function observeCanvasDocument(iframe, root, viewportHeight, fit = "page", suspe
314
299
  }
315
300
 
316
301
  //#endregion
317
- export { BLOCK_OVERLAP_SELECTOR, adaptCanvasStyleSheets, adoptCanvasDocument, canvasBlockOverlap, canvasDocumentStyles, canvasPageHref, createCanvasDocument, logicalViewportValue, measureCanvasDocument, observeCanvasDocument, pauseCanvasDocument };
302
+ export { BLOCK_OVERLAP_SELECTOR, adaptCanvasStyleSheets, adoptCanvasDocument, canvasBlockOverlap, canvasPageHref, createCanvasDocument, logicalViewportValue, measureCanvasDocument, observeCanvasDocument, pauseCanvasDocument };
@@ -2,114 +2,24 @@ import { CANVAS_DEVICES } from "./canvasPages.js";
2
2
 
3
3
  //#region src/features/canvas/canvasItems.ts
4
4
  const CANVAS_ITEM_GAP = 240;
5
- const CANVAS_SHELF_GAP = 480;
6
5
  const CANVAS_SLOT_WIDTH = CANVAS_DEVICES.desktop.width;
7
- /** Per-kind layout and camera behavior. */
8
- const CANVAS_ITEM_KINDS = {
9
- page: {
10
- width: CANVAS_SLOT_WIDTH,
11
- snappable: true
12
- },
13
- block: {
14
- width: CANVAS_SLOT_WIDTH,
15
- snappable: true
16
- }
17
- };
18
- /** The key of a block's item in the Canvas row. */
19
- function canvasBlockKey(blockId) {
20
- return `block:${blockId}`;
21
- }
22
- /** The Block Catalog: every page-content Block Definition, alphabetically by title. */
23
- function getBlockCatalog(blocks) {
24
- return blocks.filter((block) => !block._internal.layoutOnly).sort((a, b) => a._internal.title.localeCompare(b._internal.title));
25
- }
26
- function getCanvasShelves(pages, blocks) {
27
- return [{
28
- key: "shelf:pages",
29
- label: "Pages",
30
- items: pages.map((page) => ({
31
- kind: "page",
32
- key: page.key,
33
- page
34
- }))
35
- }, {
36
- key: "shelf:blocks",
37
- label: "Blocks",
38
- shownWhenEmpty: true,
39
- collapsedByDefault: true,
40
- unmountedWhenCollapsed: true,
41
- items: getBlockCatalog(blocks).map((block) => ({
42
- kind: "block",
43
- key: canvasBlockKey(block._internal.id),
44
- block
45
- }))
46
- }];
47
- }
48
6
  /**
49
- * World-space positions of the Canvas row, matching its flex layout, and the
50
- * markers above it. Items of collapsed shelves are left out of the row; each
51
- * collapsed shelf, or empty one that is still shown, takes a zero-width slot
52
- * instead, so its marker keeps a place and shows as collapsed.
7
+ * World-space positions of the Canvas row, matching its flex layout: every Page
8
+ * side by side, in Canvas order.
53
9
  */
54
- function positionCanvasShelves(shelves, collapsed = /* @__PURE__ */ new Set()) {
55
- const items = [];
56
- const markers = [];
57
- let left = 0;
58
- for (const shelf of shelves) {
59
- if (!isCanvasShelfShown(shelf)) continue;
60
- const start = left;
61
- const marker = {
62
- key: shelf.key,
63
- label: shelf.label,
64
- count: shelf.items.length
65
- };
66
- if (collapsed.has(shelf.key) || shelf.items.length === 0) {
67
- markers.push({
68
- ...marker,
69
- start,
70
- end: start,
71
- collapsed: true
72
- });
73
- left += 480;
74
- continue;
75
- }
76
- for (const item of shelf.items) {
77
- const { width } = CANVAS_ITEM_KINDS[item.kind];
78
- items.push({
79
- ...item,
80
- left,
81
- width
82
- });
83
- left += width + 240;
84
- }
85
- const end = left - 240;
86
- markers.push({
87
- ...marker,
88
- start,
89
- end,
90
- collapsed: false
91
- });
92
- left = end + 480;
93
- }
10
+ function positionCanvasPages(pages) {
94
11
  return {
95
- items,
96
- markers,
97
- width: Math.max(0, left - 480)
12
+ items: pages.map((page, index) => ({
13
+ key: page.key,
14
+ page,
15
+ left: index * (CANVAS_SLOT_WIDTH + 240),
16
+ width: CANVAS_SLOT_WIDTH
17
+ })),
18
+ width: Math.max(0, pages.length * (CANVAS_SLOT_WIDTH + 240) - 240)
98
19
  };
99
20
  }
100
- /** The shelves collapsed when the Canvas first opens in a Studio session. */
101
- function getDefaultCollapsedShelves(shelves) {
102
- return shelves.filter((shelf) => shelf.collapsedByDefault).map(({ key }) => key);
103
- }
104
- /** Whether a shelf has a marker and a place in the row. */
105
- function isCanvasShelfShown(shelf) {
106
- return shelf.items.length > 0 || shelf.shownWhenEmpty === true;
107
- }
108
- function isCanvasBlock(item) {
109
- return item.kind === "block";
110
- }
111
21
  function canvasSnapTargets(items) {
112
- return items.filter((item) => CANVAS_ITEM_KINDS[item.kind].snappable).map(({ key, left, width }) => ({
22
+ return items.map(({ key, left, width }) => ({
113
23
  key,
114
24
  left,
115
25
  width
@@ -117,4 +27,4 @@ function canvasSnapTargets(items) {
117
27
  }
118
28
 
119
29
  //#endregion
120
- export { CANVAS_SLOT_WIDTH, canvasBlockKey, canvasSnapTargets, getBlockCatalog, getCanvasShelves, getDefaultCollapsedShelves, isCanvasBlock, isCanvasShelfShown, positionCanvasShelves };
30
+ export { CANVAS_SLOT_WIDTH, canvasSnapTargets, positionCanvasPages };
@@ -1,5 +1,3 @@
1
- import { c } from "react/compiler-runtime";
2
- import * as React from "react";
3
1
  import { createStore } from "@xstate/store";
4
2
 
5
3
  //#region src/features/canvas/canvasStore.ts
@@ -10,36 +8,17 @@ import { createStore } from "@xstate/store";
10
8
  const canvasStore = createStore({
11
9
  context: {
12
10
  views: {},
13
- collapsedShelves: {},
11
+ /** Workspaces whose camera is moving, until it has been still for a moment. */
14
12
  moving: {},
13
+ /** Counts requests to zoom out to the whole Canvas; a mounted Canvas answers each new one. */
15
14
  overviewRequests: 0
16
15
  },
17
16
  on: {
18
17
  reset: () => ({
19
18
  views: {},
20
- collapsedShelves: {},
21
19
  moving: {},
22
20
  overviewRequests: 0
23
21
  }),
24
- openShelves: (context, event) => ({
25
- ...context,
26
- collapsedShelves: {
27
- ...context.collapsedShelves,
28
- [event.workspaceKey]: event.collapsed
29
- }
30
- }),
31
- setShelfCollapsed: (context, event) => {
32
- const collapsed = context.collapsedShelves[event.workspaceKey] ?? [];
33
- if (collapsed.includes(event.shelfKey) === event.collapsed) return context;
34
- const next = event.collapsed ? [...collapsed, event.shelfKey] : collapsed.filter((key) => key !== event.shelfKey);
35
- return {
36
- ...context,
37
- collapsedShelves: {
38
- ...context.collapsedShelves,
39
- [event.workspaceKey]: next
40
- }
41
- };
42
- },
43
22
  requestOverview: (context) => ({
44
23
  ...context,
45
24
  overviewRequests: context.overviewRequests + 1
@@ -76,74 +55,6 @@ const canvasStore = createStore({
76
55
  }
77
56
  }
78
57
  });
79
- function subscribe(onChange) {
80
- const subscription = canvasStore.subscribe(onChange);
81
- return () => subscription.unsubscribe();
82
- }
83
- const noShelves = [];
84
- /**
85
- * The keys of a workspace's collapsed shelves. The first time a workspace opens
86
- * in the session, `defaults` are collapsed. It always opens with the shelf
87
- * holding the selection expanded, so the camera can start on the selection.
88
- */
89
- function useCollapsedShelves(workspaceKey, defaults, selectedShelf) {
90
- const $ = c(12);
91
- let t0;
92
- if ($[0] !== workspaceKey) {
93
- t0 = () => canvasStore.getSnapshot().context.collapsedShelves[workspaceKey];
94
- $[0] = workspaceKey;
95
- $[1] = t0;
96
- } else t0 = $[1];
97
- const stored = React.useSyncExternalStore(subscribe, t0, _temp);
98
- let t1;
99
- if ($[2] !== defaults || $[3] !== selectedShelf || $[4] !== workspaceKey) {
100
- t1 = () => (canvasStore.getSnapshot().context.collapsedShelves[workspaceKey] ?? defaults).filter((key) => key !== selectedShelf);
101
- $[2] = defaults;
102
- $[3] = selectedShelf;
103
- $[4] = workspaceKey;
104
- $[5] = t1;
105
- } else t1 = $[5];
106
- const [opening, setOpening] = React.useState(t1);
107
- let t2;
108
- let t3;
109
- if ($[6] !== opening || $[7] !== workspaceKey) {
110
- t2 = () => {
111
- if (!opening) return;
112
- canvasStore.send({
113
- type: "openShelves",
114
- workspaceKey,
115
- collapsed: opening
116
- });
117
- setOpening(null);
118
- };
119
- t3 = [opening, workspaceKey];
120
- $[6] = opening;
121
- $[7] = workspaceKey;
122
- $[8] = t2;
123
- $[9] = t3;
124
- } else {
125
- t2 = $[8];
126
- t3 = $[9];
127
- }
128
- React.useLayoutEffect(t2, t3);
129
- const keys = opening ?? stored ?? noShelves;
130
- let t4;
131
- if ($[10] !== keys) {
132
- t4 = new Set(keys);
133
- $[10] = keys;
134
- $[11] = t4;
135
- } else t4 = $[11];
136
- return t4;
137
- }
138
- function _temp() {}
139
- function setShelfCollapsed(workspaceKey, shelfKey, collapsed) {
140
- canvasStore.send({
141
- type: "setShelfCollapsed",
142
- workspaceKey,
143
- shelfKey,
144
- collapsed
145
- });
146
- }
147
58
 
148
59
  //#endregion
149
- export { canvasStore, setShelfCollapsed, useCollapsedShelves };
60
+ export { canvasStore };