camox 0.45.0 → 0.47.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.
- package/dist/core/hooks/useArtifactHost.js +2 -2
- package/dist/features/canvas/CamoxCanvas.js +438 -771
- package/dist/features/canvas/CanvasPageFrame.js +1 -3
- package/dist/features/canvas/CanvasPreviewPageSlot.js +26 -6
- package/dist/features/canvas/canvasCamera.js +24 -9
- package/dist/features/canvas/canvasFrameDocument.js +4 -19
- package/dist/features/canvas/canvasItems.js +12 -102
- package/dist/features/canvas/canvasStore.js +3 -92
- package/dist/features/canvas/sidebar/CanvasNavigator.js +81 -183
- package/dist/features/canvas/sidebar/CanvasSidebar.js +22 -28
- package/dist/features/canvas/sidebar/CuratedPageInspector.js +2 -2
- package/dist/features/canvas/useCanvasCamera.js +16 -31
- package/dist/features/canvas/useCanvasFrame.js +5 -6
- package/dist/features/preview/CamoxPreview.js +137 -120
- package/dist/features/preview/components/{AddBlockDialog.js → AddBlockSheet.js} +35 -32
- package/dist/features/preview/components/BlockActionsPopover.js +6 -6
- package/dist/features/preview/components/BlockThumbnail.js +31 -33
- package/dist/features/preview/components/Overlays.js +1 -1
- package/dist/features/preview/components/PageTree.js +1 -1
- package/dist/features/preview/components/PreviewPanel.js +1 -1
- package/dist/features/preview/components/PreviewToolbar.js +74 -85
- package/dist/features/preview/previewStore.js +9 -9
- package/dist/features/provider/CamoxProvider.js +8 -16
- package/dist/features/provider/CoreCamoxProvider.js +10 -12
- package/dist/features/provider/signInShortcut.js +60 -0
- package/dist/features/routes/pageRuntime.js +8 -8
- package/dist/features/runtime/RuntimeChrome.js +1 -1
- package/dist/features/runtime/pageApp.js +11 -39
- package/dist/features/runtime/runtime.d.ts +2 -7
- package/dist/features/runtime/runtime.js +6 -19
- package/dist/features/studio/FollowAgentProvider.js +11 -22
- package/dist/features/studio/components/FaviconDialog.js +3 -3
- package/dist/features/studio/followAgent.js +5 -14
- package/dist/features/studio/routes.js +1 -23
- package/dist/lib/auth.js +36 -24
- package/dist/lib/studio-reads.js +0 -20
- package/dist/package.js +1 -1
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/skills/camox-expand/SKILL.md +5 -4
- package/dist/features/canvas/CanvasBlockFrame.js +0 -353
- package/dist/features/canvas/CanvasBlockHeader.js +0 -55
- package/dist/features/canvas/CanvasShelfMarker.js +0 -255
- package/dist/features/canvas/sidebar/CatalogBlockInspector.js +0 -189
- package/dist/features/provider/LocalhostPreviewProvider.js +0 -158
|
@@ -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,
|
|
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
|
-
|
|
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
|
|
48
|
-
top
|
|
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 };
|
|
@@ -24,16 +24,31 @@ const MAX_CANVAS_ZOOM = 2;
|
|
|
24
24
|
*/
|
|
25
25
|
const MIN_CANVAS_ZOOM = .1;
|
|
26
26
|
/** Log-scale zoom per pixel of Ctrl/⌘ + wheel, including trackpad pinches. */
|
|
27
|
-
const CANVAS_WHEEL_ZOOM_RATE = .
|
|
27
|
+
const CANVAS_WHEEL_ZOOM_RATE = .012672;
|
|
28
28
|
/** Relative (log-scale) bands make the detent feel the same at every page width. */
|
|
29
|
-
const ZOOM_SNAP_CAPTURE = .
|
|
30
|
-
const ZOOM_SNAP_RELEASE = .
|
|
29
|
+
const ZOOM_SNAP_CAPTURE = .168;
|
|
30
|
+
const ZOOM_SNAP_RELEASE = .216;
|
|
31
31
|
/** Fractions of viewport width: acquire near center, then hold a wider detent. */
|
|
32
|
-
const PAN_SNAP_CAPTURE = .
|
|
33
|
-
const PAN_SNAP_RELEASE = .
|
|
32
|
+
const PAN_SNAP_CAPTURE = .144;
|
|
33
|
+
const PAN_SNAP_RELEASE = .216;
|
|
34
34
|
function isCanvasPageScale(scale, pageScale) {
|
|
35
35
|
return pageScale !== void 0 && Math.abs(Math.log(scale / pageScale)) < 1e-8;
|
|
36
36
|
}
|
|
37
|
+
/** Signed screen distance from a target's center to the viewport's center. */
|
|
38
|
+
function canvasTargetOffset(camera, viewport, target) {
|
|
39
|
+
return camera.x + (target.left + target.width / 2) * camera.scale - viewport.width / 2;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Near a target's page fit in both zoom and position, so a pan snaps onto a
|
|
43
|
+
* page without its zoom matching exactly. A held snap releases from wider bands
|
|
44
|
+
* than it captures.
|
|
45
|
+
*/
|
|
46
|
+
function isInCanvasSnapWindow(camera, viewport, target, held) {
|
|
47
|
+
const fit = fitCanvasPage(viewport, target);
|
|
48
|
+
const zoomBand = held ? ZOOM_SNAP_RELEASE : ZOOM_SNAP_CAPTURE;
|
|
49
|
+
const panBand = viewport.width * (held ? PAN_SNAP_RELEASE : PAN_SNAP_CAPTURE);
|
|
50
|
+
return Math.abs(Math.log(camera.scale / fit.scale)) <= zoomBand && Math.abs(canvasTargetOffset(camera, viewport, target)) <= panBand;
|
|
51
|
+
}
|
|
37
52
|
/** Snapped to a target: at its page fit, centered on it. */
|
|
38
53
|
function isCanvasCameraSnapped(camera, viewport, target) {
|
|
39
54
|
const fit = fitCanvasPage(viewport, target);
|
|
@@ -131,7 +146,7 @@ function fitCanvasPage(viewport, page) {
|
|
|
131
146
|
const scale = Math.max(Number.EPSILON, Math.min(2, (viewport.width - padding * 2) / Math.max(1, page.width)));
|
|
132
147
|
return {
|
|
133
148
|
x: (viewport.width - page.width * scale) / 2 - page.left * scale,
|
|
134
|
-
y: Math.min(
|
|
149
|
+
y: Math.min(76, viewport.height / 3),
|
|
135
150
|
scale
|
|
136
151
|
};
|
|
137
152
|
}
|
|
@@ -143,11 +158,11 @@ function placeCanvasPageAnchor(viewport, page, documentY, scale, frameOffsetY =
|
|
|
143
158
|
scale
|
|
144
159
|
};
|
|
145
160
|
}
|
|
146
|
-
/** Reserve readable padding
|
|
161
|
+
/** Reserve readable padding and unscaled page headers in the fitted overview. */
|
|
147
162
|
function canvasInsets(viewport) {
|
|
148
163
|
return {
|
|
149
164
|
x: Math.min(64, viewport.width / 4),
|
|
150
|
-
top: Math.min(
|
|
165
|
+
top: Math.min(116, viewport.height / 3),
|
|
151
166
|
bottom: Math.min(64, viewport.height / 4)
|
|
152
167
|
};
|
|
153
168
|
}
|
|
@@ -226,4 +241,4 @@ function canvasWheelDelta(delta, mode, viewportSize) {
|
|
|
226
241
|
}
|
|
227
242
|
|
|
228
243
|
//#endregion
|
|
229
|
-
export { CANVAS_OVERSCROLL, CANVAS_WHEEL_ZOOM_RATE, MIN_CANVAS_ZOOM, PAN_SNAP_CAPTURE, PAN_SNAP_RELEASE, ZOOM_SNAP_CAPTURE, ZOOM_SNAP_RELEASE, ZOOM_STRETCH, canvasInsets, canvasMaximumScale, canvasMinimumScale, canvasOverview, canvasVerticalRail, canvasWheelDelta, constrainCanvasCamera, findCanvasSnapTarget, fitCanvas, fitCanvasPage, isCanvasCameraSnapped, isCanvasPageScale, placeCanvasPageAnchor, snapCanvasZoom, stretchCanvasZoom, unstretchCanvasZoom, zoomCanvasAt };
|
|
244
|
+
export { CANVAS_OVERSCROLL, CANVAS_WHEEL_ZOOM_RATE, MIN_CANVAS_ZOOM, PAN_SNAP_CAPTURE, PAN_SNAP_RELEASE, ZOOM_SNAP_CAPTURE, ZOOM_SNAP_RELEASE, ZOOM_STRETCH, canvasInsets, canvasMaximumScale, canvasMinimumScale, canvasOverview, canvasTargetOffset, canvasVerticalRail, canvasWheelDelta, constrainCanvasCamera, findCanvasSnapTarget, fitCanvas, fitCanvasPage, isCanvasCameraSnapped, isCanvasPageScale, isInCanvasSnapWindow, placeCanvasPageAnchor, snapCanvasZoom, stretchCanvasZoom, unstretchCanvasZoom, zoomCanvasAt };
|
|
@@ -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,
|
|
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
|
|
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,
|
|
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
|
|
50
|
-
*
|
|
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
|
|
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
|
-
|
|
97
|
-
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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
|
|
60
|
+
export { canvasStore };
|