camox 0.71.0 → 0.72.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/createApp.d.ts +4 -103
- package/dist/core/createApp.js +3 -24
- package/dist/core/createBlock.d.ts +2 -2
- package/dist/core/createBlock.js +41 -185
- package/dist/core/createLayout.d.ts +6 -77
- package/dist/core/createLayout.js +12 -50
- package/dist/core/editing/createEditableBlock.d.ts +9 -75
- package/dist/core/editing/createEditableBlock.js +258 -640
- package/dist/core/hooks/useIsEditable.js +2 -2
- package/dist/core/lib/artifactData.js +8 -3
- package/dist/core/lib/contentType.d.ts +23 -38
- package/dist/core/lib/contentType.js +34 -62
- package/dist/core/lib/iconTypes.d.ts +0 -3
- package/dist/core/lib/icons.js +63 -51
- package/dist/core/lib/recordScope.js +1 -1
- package/dist/features/canvas/CanvasOverlays.js +94 -176
- package/dist/features/canvas/CanvasPageFrame.js +62 -68
- package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -3
- package/dist/features/canvas/canvasOverlayGeometry.js +1 -7
- package/dist/features/canvas/sidebar/CuratedPageInspector.js +3 -21
- package/dist/features/canvas/sidebar/LayoutPageBlocks.js +1 -2
- package/dist/features/canvas/useCanvasCamera.js +1 -1
- package/dist/features/canvas/useCanvasFrame.js +1 -1
- package/dist/features/content/ContentCollection.js +2 -2
- package/dist/features/content/collection-form.js +6 -7
- package/dist/features/page/CuratedPageContent.js +15 -24
- package/dist/features/page/UnplacedBlockContent.js +1 -6
- package/dist/features/preview/CamoxPreview.js +30 -33
- package/dist/features/preview/EditablePageContent.js +131 -151
- package/dist/features/preview/components/BlockActionsPopover.js +43 -152
- package/dist/features/preview/components/CanvasPreview.js +2 -2
- package/dist/features/preview/components/Frame.js +3 -3
- package/dist/features/preview/components/IconFieldEditor.js +1 -1
- package/dist/features/preview/components/ItemFieldsEditor.js +1 -3
- package/dist/features/preview/components/Overlays.js +18 -84
- package/dist/features/preview/components/PageTree.js +57 -103
- package/dist/features/preview/components/PreviewPanel.js +8 -11
- package/dist/features/preview/components/RecordCombobox.js +65 -78
- package/dist/features/preview/components/ReferenceFieldEditor.js +1 -19
- package/dist/features/preview/components/ReferenceListFieldEditor.js +1 -19
- package/dist/features/preview/components/RemovedBlockPlaceholder.js +2 -2
- package/dist/features/preview/components/SelectionEditor.js +5 -4
- package/dist/features/preview/components/useRepeatableItemActions.js +18 -24
- package/dist/features/preview/components/useUpdateBlockPosition.js +1 -1
- package/dist/features/preview/overlayMessages.js +1 -4
- package/dist/features/preview/previewStore.js +3 -23
- package/dist/features/renderer/RendererApp.js +17 -141
- package/dist/features/renderer/definitionAnswers.js +1 -8
- package/dist/features/renderer/rendererHost.js +1 -31
- package/dist/features/renderer/studioConnection.js +1 -25
- package/dist/features/runtime/pageApp.js +1 -1
- package/dist/features/runtime/runtime.js +3 -3
- package/dist/features/studio/components/ThreadMessages.js +89 -22
- package/dist/features/vite/blockBoilerplate.js +1 -1
- package/dist/features/vite/definitionsSync.js +1 -8
- package/dist/lib/queries.js +0 -1
- package/dist/lib/studio-definitions.js +0 -2
- package/dist/lib/studio-reads.js +0 -5
- package/dist/lib/use-thread.js +196 -138
- package/dist/package.js +1 -1
- package/dist/studio-overlays.css +0 -29
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/skills/camox/SKILL.md +1 -1
- package/skills/camox/references/artifacts.md +4 -4
- package/skills/camox/references/block-definitions.md +21 -25
- package/skills/camox/references/dom-integrations.md +1 -1
- package/skills/camox/references/layout-definitions.md +3 -12
- package/skills/camox/references/routed-layouts.md +2 -2
- package/skills/camox-expand/SKILL.md +5 -5
- package/skills/camoxify/SKILL.md +4 -4
- package/dist/features/canvas/canvasInsertionSeams.js +0 -44
- package/dist/features/preview/components/AddBlockSheet.js +0 -314
- package/dist/features/preview/components/BlockPeek.js +0 -66
- package/dist/features/preview/components/BlockThumbnail.js +0 -281
- package/dist/features/preview/components/blockGridNavigation.js +0 -15
- package/dist/features/preview/referencePickerFocus.js +0 -29
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import { useProjectUuid } from "../../../lib/auth.js";
|
|
2
|
-
import { usePageBlocks } from "../../../lib/normalized-data.js";
|
|
3
|
-
import { reads, useReadQuery, useStudioCache } from "../../../lib/studio-reads.js";
|
|
4
|
-
import { blockMutations } from "../../../lib/queries.js";
|
|
5
|
-
import { useBlockDefinitions } from "../../../lib/studio-definitions.js";
|
|
6
|
-
import { useLocalePath } from "../../../lib/view-locale.js";
|
|
7
|
-
import { previewStore, sameEditingOwner } from "../previewStore.js";
|
|
8
|
-
import { useRendererHost } from "../../renderer/RendererFrames.js";
|
|
9
|
-
import { getGridNavigationIndex } from "./blockGridNavigation.js";
|
|
10
|
-
import { BlockThumbnail } from "./BlockThumbnail.js";
|
|
11
|
-
import { usePreviewedPage } from "../CamoxPreview.js";
|
|
12
|
-
import * as React from "react";
|
|
13
|
-
import { generateUuidv7 } from "@camox/api-contract/ids";
|
|
14
|
-
import { generateKeyBetween } from "fractional-indexing";
|
|
15
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
-
import { useSelector } from "@xstate/store-react";
|
|
17
|
-
import { useMutation } from "@tanstack/react-query";
|
|
18
|
-
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@camox/ui/sheet";
|
|
19
|
-
import { Badge } from "@camox/ui/badge";
|
|
20
|
-
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, commandFilter } from "@camox/ui/command";
|
|
21
|
-
|
|
22
|
-
//#region src/features/preview/components/AddBlockSheet.tsx
|
|
23
|
-
const AddBlockSheet = ({ focusCreatedBlock = true }) => {
|
|
24
|
-
const [highlightedValue, setHighlightedValue] = React.useState("");
|
|
25
|
-
const [search, setSearch] = React.useState("");
|
|
26
|
-
const [error, setError] = React.useState(null);
|
|
27
|
-
const submitting = React.useRef(false);
|
|
28
|
-
const mounted = React.useRef(false);
|
|
29
|
-
React.useEffect(() => {
|
|
30
|
-
mounted.current = true;
|
|
31
|
-
return () => {
|
|
32
|
-
mounted.current = false;
|
|
33
|
-
};
|
|
34
|
-
}, []);
|
|
35
|
-
const gridRef = React.useRef(null);
|
|
36
|
-
const sheet = useSelector(previewStore, (state) => state.context.addBlockSheet);
|
|
37
|
-
const studioCache = useStudioCache();
|
|
38
|
-
const projectUuid = useProjectUuid();
|
|
39
|
-
const pathname = useLocalePath();
|
|
40
|
-
const createBlock = useMutation({
|
|
41
|
-
...blockMutations.create(),
|
|
42
|
-
onMutate: (variables) => {
|
|
43
|
-
const edit = studioCache.edit();
|
|
44
|
-
const page = reads.pageStructure({
|
|
45
|
-
path: pathname,
|
|
46
|
-
projectUuid
|
|
47
|
-
});
|
|
48
|
-
const previousPage = edit.get(page);
|
|
49
|
-
if (!previousPage) return { edit };
|
|
50
|
-
const blockIds = previousPage.page.blockIds;
|
|
51
|
-
const pageBlocks = blockIds.map((id) => edit.get(reads.blockBundle(id))?.block).filter((b) => b != null);
|
|
52
|
-
const { afterPosition } = variables;
|
|
53
|
-
let position;
|
|
54
|
-
if (afterPosition == null) {
|
|
55
|
-
const lastBlock = pageBlocks[pageBlocks.length - 1];
|
|
56
|
-
position = generateKeyBetween(lastBlock?.position ?? null, null);
|
|
57
|
-
} else if (afterPosition === "") {
|
|
58
|
-
const firstBlock = pageBlocks[0];
|
|
59
|
-
position = generateKeyBetween(null, firstBlock?.position ?? null);
|
|
60
|
-
} else {
|
|
61
|
-
let afterIndex = -1;
|
|
62
|
-
for (let i = pageBlocks.length - 1; i >= 0; i--) if (String(pageBlocks[i].position) <= afterPosition) {
|
|
63
|
-
afterIndex = i;
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
const nextBlock = afterIndex >= 0 ? pageBlocks[afterIndex + 1] : pageBlocks[0];
|
|
67
|
-
position = generateKeyBetween(afterIndex >= 0 ? pageBlocks[afterIndex].position : null, nextBlock?.position ?? null);
|
|
68
|
-
}
|
|
69
|
-
const now = Date.now();
|
|
70
|
-
const optimisticId = variables.id ?? generateUuidv7();
|
|
71
|
-
const optimisticBlock = {
|
|
72
|
-
id: optimisticId,
|
|
73
|
-
pageId: variables.pageId,
|
|
74
|
-
layoutId: null,
|
|
75
|
-
type: variables.type,
|
|
76
|
-
content: variables.content,
|
|
77
|
-
settings: variables.settings ?? null,
|
|
78
|
-
slot: null,
|
|
79
|
-
summary: "",
|
|
80
|
-
title: "",
|
|
81
|
-
position,
|
|
82
|
-
createdAt: now,
|
|
83
|
-
updatedAt: now
|
|
84
|
-
};
|
|
85
|
-
edit.set(reads.blockBundle(optimisticId), {
|
|
86
|
-
block: optimisticBlock,
|
|
87
|
-
repeatableItems: [],
|
|
88
|
-
files: []
|
|
89
|
-
});
|
|
90
|
-
const insertIndex = pageBlocks.findIndex((b) => b.position > position);
|
|
91
|
-
const newBlockIds = [...blockIds];
|
|
92
|
-
if (insertIndex === -1) newBlockIds.push(optimisticId);
|
|
93
|
-
else newBlockIds.splice(insertIndex, 0, optimisticId);
|
|
94
|
-
edit.set(page, {
|
|
95
|
-
...previousPage,
|
|
96
|
-
page: {
|
|
97
|
-
...previousPage.page,
|
|
98
|
-
blockIds: newBlockIds
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
return { edit };
|
|
102
|
-
},
|
|
103
|
-
onError: (_error, _variables, context) => context?.edit.rollback(),
|
|
104
|
-
onSettled: (_data, _error, _variables, context) => context?.edit.settle()
|
|
105
|
-
});
|
|
106
|
-
const { data: project } = useReadQuery(reads.project({ projectUuid }));
|
|
107
|
-
const rendererHost = useRendererHost();
|
|
108
|
-
const availableBlocks = useBlockDefinitions().filter((block) => !block.layoutOnly);
|
|
109
|
-
const page = usePreviewedPage();
|
|
110
|
-
const { pageBlocks } = usePageBlocks(page);
|
|
111
|
-
const { data: totalCounts = {} } = useReadQuery(reads.blockUsageCounts({ projectId: project?.id ?? 0 }), { enabled: !!project });
|
|
112
|
-
const filteredBlocks = availableBlocks.map((block) => ({
|
|
113
|
-
block,
|
|
114
|
-
score: search.trim() ? commandFilter(block.id, search.trim(), [block.title, block.description]) : 1
|
|
115
|
-
})).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score || (totalCounts[b.block.id] ?? 0) - (totalCounts[a.block.id] ?? 0)).map(({ block }) => block);
|
|
116
|
-
const selectedValue = filteredBlocks.some((block) => block.id === highlightedValue) ? highlightedValue : filteredBlocks[0]?.id ?? "";
|
|
117
|
-
const pageCounts = React.useMemo(() => {
|
|
118
|
-
const counts = {};
|
|
119
|
-
if (!page) return counts;
|
|
120
|
-
for (const block of pageBlocks) counts[block.type] = (counts[block.type] ?? 0) + 1;
|
|
121
|
-
return counts;
|
|
122
|
-
}, [page, pageBlocks]);
|
|
123
|
-
const [previousSheet, setPreviousSheet] = React.useState(sheet);
|
|
124
|
-
if (sheet !== previousSheet) {
|
|
125
|
-
setPreviousSheet(sheet);
|
|
126
|
-
setHighlightedValue("");
|
|
127
|
-
setSearch("");
|
|
128
|
-
setError(null);
|
|
129
|
-
}
|
|
130
|
-
const handleAddBlock = async (block) => {
|
|
131
|
-
if (!page || !sheet || submitting.current) return;
|
|
132
|
-
submitting.current = true;
|
|
133
|
-
setError(null);
|
|
134
|
-
const owner = {
|
|
135
|
-
kind: "page",
|
|
136
|
-
pageId: page.page.id
|
|
137
|
-
};
|
|
138
|
-
try {
|
|
139
|
-
const bundle = (rendererHost ? await initialBundleFrom(rendererHost, block.id) : null) ?? {
|
|
140
|
-
content: block.defaultContent,
|
|
141
|
-
settings: block.defaultSettings,
|
|
142
|
-
repeatableItems: []
|
|
143
|
-
};
|
|
144
|
-
const { id: blockId } = await createBlock.mutateAsync({
|
|
145
|
-
id: generateUuidv7(),
|
|
146
|
-
pageId: page.page.id,
|
|
147
|
-
type: block.id,
|
|
148
|
-
content: bundle.content,
|
|
149
|
-
settings: bundle.settings,
|
|
150
|
-
afterPosition: sheet.afterPosition,
|
|
151
|
-
repeatableItems: bundle.repeatableItems
|
|
152
|
-
});
|
|
153
|
-
const context = previewStore.getSnapshot().context;
|
|
154
|
-
if (!mounted.current) return;
|
|
155
|
-
if (focusCreatedBlock && !sameEditingOwner(context.editingContext, owner)) return;
|
|
156
|
-
if (context.addBlockSheet !== sheet) return;
|
|
157
|
-
previewStore.send({ type: "closeAddBlockSheet" });
|
|
158
|
-
if (focusCreatedBlock) previewStore.send({
|
|
159
|
-
type: "focusCreatedBlock",
|
|
160
|
-
...owner,
|
|
161
|
-
blockId
|
|
162
|
-
});
|
|
163
|
-
} catch {
|
|
164
|
-
if (previewStore.getSnapshot().context.addBlockSheet === sheet) setError("Could not add this block. Please try again.");
|
|
165
|
-
} finally {
|
|
166
|
-
submitting.current = false;
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
const displayCount = (blockId) => {
|
|
170
|
-
const total = totalCounts[blockId] ?? 0;
|
|
171
|
-
if (total === 0) return "Never used";
|
|
172
|
-
const page = pageCounts[blockId] ?? "none";
|
|
173
|
-
return `${total} use${total > 1 ? "s" : ""} (${page} here)`;
|
|
174
|
-
};
|
|
175
|
-
return /* @__PURE__ */ jsx(Sheet, {
|
|
176
|
-
open: sheet != null,
|
|
177
|
-
onOpenChange: (open) => {
|
|
178
|
-
if (!open && !submitting.current) previewStore.send({ type: "closeAddBlockSheet" });
|
|
179
|
-
},
|
|
180
|
-
children: /* @__PURE__ */ jsxs(SheetContent, {
|
|
181
|
-
side: "right",
|
|
182
|
-
className: "gap-0 overflow-hidden data-[side=right]:w-full data-[side=right]:sm:max-w-4xl",
|
|
183
|
-
children: [
|
|
184
|
-
/* @__PURE__ */ jsxs(SheetHeader, {
|
|
185
|
-
className: "pr-12",
|
|
186
|
-
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." })]
|
|
187
|
-
}),
|
|
188
|
-
/* @__PURE__ */ jsxs(Command, {
|
|
189
|
-
label: "Search blocks",
|
|
190
|
-
shouldFilter: false,
|
|
191
|
-
value: selectedValue,
|
|
192
|
-
onValueChange: setHighlightedValue,
|
|
193
|
-
className: "h-0 min-h-0 flex-1 rounded-none! bg-transparent! px-4! pt-0! pb-0!",
|
|
194
|
-
onKeyDown: (e) => {
|
|
195
|
-
if (e.nativeEvent.isComposing || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
196
|
-
if (![
|
|
197
|
-
"ArrowLeft",
|
|
198
|
-
"ArrowRight",
|
|
199
|
-
"ArrowUp",
|
|
200
|
-
"ArrowDown"
|
|
201
|
-
].includes(e.key)) return;
|
|
202
|
-
e.preventDefault();
|
|
203
|
-
const grid = gridRef.current?.querySelector("[cmdk-group-items]");
|
|
204
|
-
if (!grid || submitting.current) return;
|
|
205
|
-
const items = Array.from(grid.querySelectorAll("[cmdk-item]:not([data-disabled=\"true\"])"));
|
|
206
|
-
const columns = getComputedStyle(grid).gridTemplateColumns.split(" ").length;
|
|
207
|
-
const index = items.findIndex((item) => item.dataset.selected === "true");
|
|
208
|
-
const item = items[getGridNavigationIndex(index, items.length, columns, e.key)];
|
|
209
|
-
if (!item) return;
|
|
210
|
-
setHighlightedValue(item.dataset.value ?? "");
|
|
211
|
-
item.scrollIntoView({ block: "nearest" });
|
|
212
|
-
},
|
|
213
|
-
children: [/* @__PURE__ */ jsx(CommandInput, {
|
|
214
|
-
placeholder: "Search blocks...",
|
|
215
|
-
"aria-label": "Search blocks",
|
|
216
|
-
value: search,
|
|
217
|
-
onValueChange: (value) => {
|
|
218
|
-
setSearch(value);
|
|
219
|
-
setHighlightedValue("");
|
|
220
|
-
},
|
|
221
|
-
className: "px-0 pt-0",
|
|
222
|
-
autoFocus: true
|
|
223
|
-
}), /* @__PURE__ */ jsxs(CommandList, {
|
|
224
|
-
label: "Available blocks",
|
|
225
|
-
className: "mt-4 max-h-none min-h-0 flex-1 pb-4",
|
|
226
|
-
"aria-busy": createBlock.isPending,
|
|
227
|
-
children: [/* @__PURE__ */ jsx(CommandEmpty, { children: "No blocks found." }), /* @__PURE__ */ jsx(CommandGroup, {
|
|
228
|
-
ref: gridRef,
|
|
229
|
-
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",
|
|
230
|
-
children: filteredBlocks.map((block) => /* @__PURE__ */ jsxs(CommandItem, {
|
|
231
|
-
value: block.id,
|
|
232
|
-
keywords: [block.title, block.description],
|
|
233
|
-
disabled: createBlock.isPending,
|
|
234
|
-
hideCheck: true,
|
|
235
|
-
onSelect: () => {
|
|
236
|
-
handleAddBlock(block);
|
|
237
|
-
},
|
|
238
|
-
className: "group bg-card data-[selected=true]:border-primary flex flex-col items-stretch gap-0 overflow-hidden rounded-lg border-2 p-0",
|
|
239
|
-
children: [/* @__PURE__ */ jsx(BlockThumbnail, {
|
|
240
|
-
blockType: block.id,
|
|
241
|
-
title: block.title,
|
|
242
|
-
className: "h-44"
|
|
243
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
244
|
-
className: "border-border flex flex-col gap-1 border-t px-3 py-2",
|
|
245
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
246
|
-
className: "flex items-center justify-between gap-2",
|
|
247
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
248
|
-
className: "min-w-0 leading-5 font-medium",
|
|
249
|
-
children: block.title
|
|
250
|
-
}), block.synced && /* @__PURE__ */ jsx(Badge, {
|
|
251
|
-
variant: "secondary",
|
|
252
|
-
size: "sm",
|
|
253
|
-
children: "Synced"
|
|
254
|
-
})]
|
|
255
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
256
|
-
className: "text-muted-foreground text-xs leading-4",
|
|
257
|
-
children: displayCount(block.id)
|
|
258
|
-
})]
|
|
259
|
-
})]
|
|
260
|
-
}, block.id))
|
|
261
|
-
})]
|
|
262
|
-
})]
|
|
263
|
-
}),
|
|
264
|
-
(createBlock.isPending || error) && /* @__PURE__ */ jsxs("div", {
|
|
265
|
-
className: "border-border border-t p-4",
|
|
266
|
-
children: [createBlock.isPending && /* @__PURE__ */ jsx("p", {
|
|
267
|
-
role: "status",
|
|
268
|
-
children: "Adding block…"
|
|
269
|
-
}), error && /* @__PURE__ */ jsx("p", {
|
|
270
|
-
role: "alert",
|
|
271
|
-
className: "text-destructive",
|
|
272
|
-
children: error
|
|
273
|
-
})]
|
|
274
|
-
})
|
|
275
|
-
]
|
|
276
|
-
})
|
|
277
|
-
});
|
|
278
|
-
};
|
|
279
|
-
/**
|
|
280
|
-
* How long adding a block waits for the renderer frame to give the block's initial content
|
|
281
|
-
* before it uses the synced defaults. A tuning value.
|
|
282
|
-
*/
|
|
283
|
-
const INITIAL_BUNDLE_WAIT_MS = 3e3;
|
|
284
|
-
/**
|
|
285
|
-
* The initial content a `blockType` block's code builds, asked of the renderer frame. Null,
|
|
286
|
-
* for the synced defaults, when the renderer runs another SDK version, which answers nothing,
|
|
287
|
-
* or hasn't answered within `INITIAL_BUNDLE_WAIT_MS`, as when it never connects.
|
|
288
|
-
*/
|
|
289
|
-
function initialBundleFrom(host, blockType) {
|
|
290
|
-
return new Promise((resolve, reject) => {
|
|
291
|
-
const settle = (bundle) => {
|
|
292
|
-
clearTimeout(timeout);
|
|
293
|
-
stopWatching();
|
|
294
|
-
resolve(bundle);
|
|
295
|
-
};
|
|
296
|
-
const timeout = setTimeout(() => settle(null), INITIAL_BUNDLE_WAIT_MS);
|
|
297
|
-
const watch = () => {
|
|
298
|
-
if (host.status() === "updating") settle(null);
|
|
299
|
-
};
|
|
300
|
-
const stopWatching = host.subscribeStatus(watch);
|
|
301
|
-
watch();
|
|
302
|
-
host.ask({
|
|
303
|
-
type: "initialBundle",
|
|
304
|
-
blockType
|
|
305
|
-
}).then(settle, (error) => {
|
|
306
|
-
clearTimeout(timeout);
|
|
307
|
-
stopWatching();
|
|
308
|
-
reject(error);
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
//#endregion
|
|
314
|
-
export { AddBlockSheet, INITIAL_BUNDLE_WAIT_MS };
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { NormalizedDataProvider } from "../../../lib/normalized-data.js";
|
|
2
|
-
import { c } from "react/compiler-runtime";
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { jsx } from "react/jsx-runtime";
|
|
5
|
-
|
|
6
|
-
//#region src/features/preview/components/BlockPeek.tsx
|
|
7
|
-
/**
|
|
8
|
-
* A block rendered by its code from its peek bundle: what its thumbnail shows. It catches no
|
|
9
|
-
* error: a broken block shows no thumbnail, which is for the thumbnail to do.
|
|
10
|
-
*/
|
|
11
|
-
const BlockPeek = (t0) => {
|
|
12
|
-
const $ = c(13);
|
|
13
|
-
const { block } = t0;
|
|
14
|
-
let t1;
|
|
15
|
-
if ($[0] !== block._internal) {
|
|
16
|
-
t1 = block._internal.getPeekBundle();
|
|
17
|
-
$[0] = block._internal;
|
|
18
|
-
$[1] = t1;
|
|
19
|
-
} else t1 = $[1];
|
|
20
|
-
const bundle = t1;
|
|
21
|
-
const t2 = bundle.block.content;
|
|
22
|
-
const t3 = bundle.block.settings;
|
|
23
|
-
let t4;
|
|
24
|
-
if ($[2] !== block._internal.id || $[3] !== t2 || $[4] !== t3) {
|
|
25
|
-
t4 = {
|
|
26
|
-
_id: "thumbnail",
|
|
27
|
-
type: block._internal.id,
|
|
28
|
-
content: t2,
|
|
29
|
-
settings: t3,
|
|
30
|
-
position: ""
|
|
31
|
-
};
|
|
32
|
-
$[2] = block._internal.id;
|
|
33
|
-
$[3] = t2;
|
|
34
|
-
$[4] = t3;
|
|
35
|
-
$[5] = t4;
|
|
36
|
-
} else t4 = $[5];
|
|
37
|
-
let t5;
|
|
38
|
-
if ($[6] !== block._internal.Component || $[7] !== t4) {
|
|
39
|
-
t5 = /* @__PURE__ */ jsx(block._internal.Component, {
|
|
40
|
-
blockData: t4,
|
|
41
|
-
mode: "peek"
|
|
42
|
-
});
|
|
43
|
-
$[6] = block._internal.Component;
|
|
44
|
-
$[7] = t4;
|
|
45
|
-
$[8] = t5;
|
|
46
|
-
} else t5 = $[8];
|
|
47
|
-
let t6;
|
|
48
|
-
if ($[9] !== bundle.files || $[10] !== bundle.repeatableItems || $[11] !== t5) {
|
|
49
|
-
t6 = /* @__PURE__ */ jsx(React.Suspense, {
|
|
50
|
-
fallback: null,
|
|
51
|
-
children: /* @__PURE__ */ jsx(NormalizedDataProvider, {
|
|
52
|
-
files: bundle.files,
|
|
53
|
-
repeatableItems: bundle.repeatableItems,
|
|
54
|
-
children: t5
|
|
55
|
-
})
|
|
56
|
-
});
|
|
57
|
-
$[9] = bundle.files;
|
|
58
|
-
$[10] = bundle.repeatableItems;
|
|
59
|
-
$[11] = t5;
|
|
60
|
-
$[12] = t6;
|
|
61
|
-
} else t6 = $[12];
|
|
62
|
-
return t6;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
//#endregion
|
|
66
|
-
export { BlockPeek };
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import { cn } from "../../../lib/utils.js";
|
|
2
|
-
import { useEditingFrame } from "../editingFrame.js";
|
|
3
|
-
import { useRendererHost } from "../../renderer/RendererFrames.js";
|
|
4
|
-
import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle } from "./previewStyles.js";
|
|
5
|
-
import { FrameContext } from "./Frame.js";
|
|
6
|
-
import { c } from "react/compiler-runtime";
|
|
7
|
-
import * as React from "react";
|
|
8
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
import { createPortal } from "react-dom";
|
|
10
|
-
|
|
11
|
-
//#region src/features/preview/components/BlockThumbnail.tsx
|
|
12
|
-
const VIEWPORT_WIDTH = 1200;
|
|
13
|
-
const VIEWPORT_HEIGHT = 800;
|
|
14
|
-
const ThumbnailFrame = (t0) => {
|
|
15
|
-
const $ = c(26);
|
|
16
|
-
const { blockType, title, width } = t0;
|
|
17
|
-
const sourceDocument = useEditingFrame().document;
|
|
18
|
-
const [frame, setFrame] = React.useState(null);
|
|
19
|
-
const [root, setRoot] = React.useState(null);
|
|
20
|
-
const [content, setContent] = React.useState(null);
|
|
21
|
-
const [contentHeight, setContentHeight] = React.useState(VIEWPORT_HEIGHT);
|
|
22
|
-
const initialization = React.useRef(0);
|
|
23
|
-
let t1;
|
|
24
|
-
if ($[0] !== frame?.contentDocument || $[1] !== sourceDocument) {
|
|
25
|
-
t1 = () => {
|
|
26
|
-
const doc = frame?.contentDocument;
|
|
27
|
-
const mount = doc?.querySelector("[data-camox-preview-root]");
|
|
28
|
-
if (!doc || !mount) return;
|
|
29
|
-
const source = sourceDocument ?? document;
|
|
30
|
-
doc.head.replaceChildren();
|
|
31
|
-
const base = doc.createElement("base");
|
|
32
|
-
base.href = source.baseURI;
|
|
33
|
-
doc.head.appendChild(base);
|
|
34
|
-
const stylesheetsLoaded = [];
|
|
35
|
-
source.querySelectorAll("style, link[rel=\"stylesheet\"]").forEach((style) => {
|
|
36
|
-
if (!isSiteStyle(style)) return;
|
|
37
|
-
const clone = style.cloneNode(true);
|
|
38
|
-
if (clone.localName === "link") stylesheetsLoaded.push(new Promise((resolve) => {
|
|
39
|
-
clone.addEventListener("load", resolve, { once: true });
|
|
40
|
-
clone.addEventListener("error", resolve, { once: true });
|
|
41
|
-
}));
|
|
42
|
-
doc.head.appendChild(clone);
|
|
43
|
-
});
|
|
44
|
-
if (source !== document) for (const [from, to] of [
|
|
45
|
-
[source.documentElement, doc.documentElement],
|
|
46
|
-
[source.body, doc.body],
|
|
47
|
-
[source.querySelector("[data-camox-preview-root]"), mount]
|
|
48
|
-
]) {
|
|
49
|
-
if (!from || !to) continue;
|
|
50
|
-
for (const attribute of Array.from(from.attributes)) if (!attribute.name.startsWith("on")) to.setAttribute(attribute.name, attribute.value);
|
|
51
|
-
}
|
|
52
|
-
const reset = doc.createElement("style");
|
|
53
|
-
reset.textContent = "\n html, body { margin: 0 !important; overflow: hidden !important; }\n *, *::before, *::after {\n animation: none !important;\n transition: none !important;\n caret-color: transparent !important;\n }\n ";
|
|
54
|
-
doc.head.appendChild(reset);
|
|
55
|
-
doc.body.setAttribute("inert", "");
|
|
56
|
-
const generation = initialization.current = initialization.current + 1;
|
|
57
|
-
Promise.all(stylesheetsLoaded).then(() => {
|
|
58
|
-
if (initialization.current === generation) setRoot(mount);
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
$[0] = frame?.contentDocument;
|
|
62
|
-
$[1] = sourceDocument;
|
|
63
|
-
$[2] = t1;
|
|
64
|
-
} else t1 = $[2];
|
|
65
|
-
const initialize = t1;
|
|
66
|
-
let t2;
|
|
67
|
-
let t3;
|
|
68
|
-
if ($[3] !== initialize) {
|
|
69
|
-
t2 = () => {
|
|
70
|
-
initialize();
|
|
71
|
-
};
|
|
72
|
-
t3 = [initialize];
|
|
73
|
-
$[3] = initialize;
|
|
74
|
-
$[4] = t2;
|
|
75
|
-
$[5] = t3;
|
|
76
|
-
} else {
|
|
77
|
-
t2 = $[4];
|
|
78
|
-
t3 = $[5];
|
|
79
|
-
}
|
|
80
|
-
React.useEffect(t2, t3);
|
|
81
|
-
let t4;
|
|
82
|
-
let t5;
|
|
83
|
-
if ($[6] !== content) {
|
|
84
|
-
t4 = () => {
|
|
85
|
-
if (!content) return;
|
|
86
|
-
const observer = new ResizeObserver(() => {
|
|
87
|
-
setContentHeight(content.getBoundingClientRect().height);
|
|
88
|
-
});
|
|
89
|
-
observer.observe(content);
|
|
90
|
-
return () => observer.disconnect();
|
|
91
|
-
};
|
|
92
|
-
t5 = [content];
|
|
93
|
-
$[6] = content;
|
|
94
|
-
$[7] = t4;
|
|
95
|
-
$[8] = t5;
|
|
96
|
-
} else {
|
|
97
|
-
t4 = $[7];
|
|
98
|
-
t5 = $[8];
|
|
99
|
-
}
|
|
100
|
-
React.useEffect(t4, t5);
|
|
101
|
-
const t6 = contentHeight * width / VIEWPORT_WIDTH;
|
|
102
|
-
let t7;
|
|
103
|
-
if ($[9] !== t6) {
|
|
104
|
-
t7 = { height: t6 };
|
|
105
|
-
$[9] = t6;
|
|
106
|
-
$[10] = t7;
|
|
107
|
-
} else t7 = $[10];
|
|
108
|
-
const t8 = `${title} preview`;
|
|
109
|
-
const t9 = `scale(${width / VIEWPORT_WIDTH})`;
|
|
110
|
-
let t10;
|
|
111
|
-
if ($[11] !== t9) {
|
|
112
|
-
t10 = {
|
|
113
|
-
width: VIEWPORT_WIDTH,
|
|
114
|
-
height: VIEWPORT_HEIGHT,
|
|
115
|
-
transform: t9
|
|
116
|
-
};
|
|
117
|
-
$[11] = t9;
|
|
118
|
-
$[12] = t10;
|
|
119
|
-
} else t10 = $[12];
|
|
120
|
-
let t11;
|
|
121
|
-
if ($[13] !== initialize || $[14] !== t10 || $[15] !== t8) {
|
|
122
|
-
t11 = /* @__PURE__ */ jsx("iframe", {
|
|
123
|
-
ref: setFrame,
|
|
124
|
-
title: t8,
|
|
125
|
-
srcDoc: EMPTY_PREVIEW_DOCUMENT,
|
|
126
|
-
onLoad: initialize,
|
|
127
|
-
tabIndex: -1,
|
|
128
|
-
className: "pointer-events-none absolute top-0 left-0 origin-top-left border-0",
|
|
129
|
-
style: t10
|
|
130
|
-
});
|
|
131
|
-
$[13] = initialize;
|
|
132
|
-
$[14] = t10;
|
|
133
|
-
$[15] = t8;
|
|
134
|
-
$[16] = t11;
|
|
135
|
-
} else t11 = $[16];
|
|
136
|
-
let t12;
|
|
137
|
-
if ($[17] !== blockType || $[18] !== content || $[19] !== frame || $[20] !== root) {
|
|
138
|
-
t12 = root && createPortal(/* @__PURE__ */ jsx(FrameContext.Provider, {
|
|
139
|
-
value: {
|
|
140
|
-
window: frame?.contentWindow ?? null,
|
|
141
|
-
iframeElement: frame
|
|
142
|
-
},
|
|
143
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
144
|
-
ref: setContent,
|
|
145
|
-
style: { display: "flow-root" },
|
|
146
|
-
children: content && /* @__PURE__ */ jsx(ThumbnailContent, {
|
|
147
|
-
blockType,
|
|
148
|
-
slot: content
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
}), root);
|
|
152
|
-
$[17] = blockType;
|
|
153
|
-
$[18] = content;
|
|
154
|
-
$[19] = frame;
|
|
155
|
-
$[20] = root;
|
|
156
|
-
$[21] = t12;
|
|
157
|
-
} else t12 = $[21];
|
|
158
|
-
let t13;
|
|
159
|
-
if ($[22] !== t11 || $[23] !== t12 || $[24] !== t7) {
|
|
160
|
-
t13 = /* @__PURE__ */ jsxs("div", {
|
|
161
|
-
className: "relative max-h-full w-full shrink-0 overflow-hidden",
|
|
162
|
-
style: t7,
|
|
163
|
-
children: [t11, t12]
|
|
164
|
-
});
|
|
165
|
-
$[22] = t11;
|
|
166
|
-
$[23] = t12;
|
|
167
|
-
$[24] = t7;
|
|
168
|
-
$[25] = t13;
|
|
169
|
-
} else t13 = $[25];
|
|
170
|
-
return t13;
|
|
171
|
-
};
|
|
172
|
-
/**
|
|
173
|
-
* What the thumbnail shows: the block rendered into `slot` by the renderer frame, which runs the
|
|
174
|
-
* draft's code.
|
|
175
|
-
*/
|
|
176
|
-
const ThumbnailContent = (t0) => {
|
|
177
|
-
const $ = c(6);
|
|
178
|
-
const { blockType, slot } = t0;
|
|
179
|
-
const host = useRendererHost();
|
|
180
|
-
const thumbnailId = React.useId();
|
|
181
|
-
let t1;
|
|
182
|
-
let t2;
|
|
183
|
-
if ($[0] !== blockType || $[1] !== host || $[2] !== slot || $[3] !== thumbnailId) {
|
|
184
|
-
t1 = () => {
|
|
185
|
-
if (!host) return;
|
|
186
|
-
host.showThumbnail(thumbnailId, slot, blockType);
|
|
187
|
-
return () => host.hideThumbnail(thumbnailId);
|
|
188
|
-
};
|
|
189
|
-
t2 = [
|
|
190
|
-
host,
|
|
191
|
-
thumbnailId,
|
|
192
|
-
slot,
|
|
193
|
-
blockType
|
|
194
|
-
];
|
|
195
|
-
$[0] = blockType;
|
|
196
|
-
$[1] = host;
|
|
197
|
-
$[2] = slot;
|
|
198
|
-
$[3] = thumbnailId;
|
|
199
|
-
$[4] = t1;
|
|
200
|
-
$[5] = t2;
|
|
201
|
-
} else {
|
|
202
|
-
t1 = $[4];
|
|
203
|
-
t2 = $[5];
|
|
204
|
-
}
|
|
205
|
-
React.useLayoutEffect(t1, t2);
|
|
206
|
-
return null;
|
|
207
|
-
};
|
|
208
|
-
/** A block's thumbnail, rendered by the renderer frame with the draft's code. */
|
|
209
|
-
const BlockThumbnail = (t0) => {
|
|
210
|
-
const $ = c(13);
|
|
211
|
-
const { blockType, title, className } = t0;
|
|
212
|
-
const ref = React.useRef(null);
|
|
213
|
-
const [isVisible, setIsVisible] = React.useState(false);
|
|
214
|
-
const [width, setWidth] = React.useState(0);
|
|
215
|
-
const renderable = !!useRendererHost();
|
|
216
|
-
let t1;
|
|
217
|
-
let t2;
|
|
218
|
-
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
219
|
-
t1 = () => {
|
|
220
|
-
const element = ref.current;
|
|
221
|
-
if (!element) return;
|
|
222
|
-
const intersectionObserver = new IntersectionObserver((t3) => {
|
|
223
|
-
const [entry] = t3;
|
|
224
|
-
setIsVisible(entry.isIntersecting);
|
|
225
|
-
});
|
|
226
|
-
const resizeObserver = new ResizeObserver((t4) => {
|
|
227
|
-
const [entry_0] = t4;
|
|
228
|
-
setWidth(entry_0.contentRect.width);
|
|
229
|
-
});
|
|
230
|
-
intersectionObserver.observe(element);
|
|
231
|
-
resizeObserver.observe(element);
|
|
232
|
-
return () => {
|
|
233
|
-
intersectionObserver.disconnect();
|
|
234
|
-
resizeObserver.disconnect();
|
|
235
|
-
};
|
|
236
|
-
};
|
|
237
|
-
t2 = [];
|
|
238
|
-
$[0] = t1;
|
|
239
|
-
$[1] = t2;
|
|
240
|
-
} else {
|
|
241
|
-
t1 = $[0];
|
|
242
|
-
t2 = $[1];
|
|
243
|
-
}
|
|
244
|
-
React.useEffect(t1, t2);
|
|
245
|
-
let t3;
|
|
246
|
-
if ($[2] !== className) {
|
|
247
|
-
t3 = cn("bg-muted pointer-events-none relative flex h-28 w-full items-center overflow-hidden", className);
|
|
248
|
-
$[2] = className;
|
|
249
|
-
$[3] = t3;
|
|
250
|
-
} else t3 = $[3];
|
|
251
|
-
let t4;
|
|
252
|
-
if ($[4] !== blockType || $[5] !== isVisible || $[6] !== renderable || $[7] !== title || $[8] !== width) {
|
|
253
|
-
t4 = renderable && isVisible && width > 0 && /* @__PURE__ */ jsx(ThumbnailFrame, {
|
|
254
|
-
blockType,
|
|
255
|
-
title,
|
|
256
|
-
width
|
|
257
|
-
});
|
|
258
|
-
$[4] = blockType;
|
|
259
|
-
$[5] = isVisible;
|
|
260
|
-
$[6] = renderable;
|
|
261
|
-
$[7] = title;
|
|
262
|
-
$[8] = width;
|
|
263
|
-
$[9] = t4;
|
|
264
|
-
} else t4 = $[9];
|
|
265
|
-
let t5;
|
|
266
|
-
if ($[10] !== t3 || $[11] !== t4) {
|
|
267
|
-
t5 = /* @__PURE__ */ jsx("div", {
|
|
268
|
-
ref,
|
|
269
|
-
className: t3,
|
|
270
|
-
"aria-hidden": "true",
|
|
271
|
-
children: t4
|
|
272
|
-
});
|
|
273
|
-
$[10] = t3;
|
|
274
|
-
$[11] = t4;
|
|
275
|
-
$[12] = t5;
|
|
276
|
-
} else t5 = $[12];
|
|
277
|
-
return t5;
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
//#endregion
|
|
281
|
-
export { BlockThumbnail };
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
//#region src/features/preview/components/blockGridNavigation.ts
|
|
2
|
-
/** Move within a non-wrapping grid, clamping down into a short final row. */
|
|
3
|
-
function getGridNavigationIndex(index, count, columns, key) {
|
|
4
|
-
if (count === 0) return -1;
|
|
5
|
-
if (index < 0) return 0;
|
|
6
|
-
const column = index % columns;
|
|
7
|
-
if (key === "ArrowLeft") return column > 0 ? index - 1 : index;
|
|
8
|
-
if (key === "ArrowRight") return column < columns - 1 ? Math.min(index + 1, count - 1) : index;
|
|
9
|
-
if (key === "ArrowUp") return index >= columns ? index - columns : index;
|
|
10
|
-
if (key === "ArrowDown") return (Math.floor(index / columns) + 1) * columns < count ? Math.min(index + columns, count - 1) : index;
|
|
11
|
-
return index;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
//#endregion
|
|
15
|
-
export { getGridNavigationIndex };
|