camox 0.69.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.
Files changed (89) hide show
  1. package/dist/core/createApp.d.ts +4 -103
  2. package/dist/core/createApp.js +3 -24
  3. package/dist/core/createBlock.d.ts +2 -2
  4. package/dist/core/createBlock.js +55 -192
  5. package/dist/core/createLayout.d.ts +6 -77
  6. package/dist/core/createLayout.js +12 -50
  7. package/dist/core/editing/createEditableBlock.d.ts +9 -75
  8. package/dist/core/editing/createEditableBlock.js +258 -640
  9. package/dist/core/hooks/useArtifactHost.js +56 -46
  10. package/dist/core/hooks/useIsEditable.js +2 -2
  11. package/dist/core/lib/artifact.d.ts +4 -0
  12. package/dist/core/lib/artifactData.js +8 -3
  13. package/dist/core/lib/artifactHost.js +40 -0
  14. package/dist/core/lib/artifactRuntime.js +20 -1
  15. package/dist/core/lib/contentType.d.ts +23 -38
  16. package/dist/core/lib/contentType.js +34 -62
  17. package/dist/core/lib/iconTypes.d.ts +0 -3
  18. package/dist/core/lib/icons.js +63 -51
  19. package/dist/core/lib/recordScope.js +1 -1
  20. package/dist/features/canvas/CanvasOverlays.js +94 -176
  21. package/dist/features/canvas/CanvasPageFrame.js +62 -68
  22. package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -3
  23. package/dist/features/canvas/canvasOverlayGeometry.js +1 -7
  24. package/dist/features/canvas/sidebar/CuratedPageInspector.js +3 -21
  25. package/dist/features/canvas/sidebar/LayoutPageBlocks.js +1 -2
  26. package/dist/features/canvas/useCanvasCamera.js +1 -1
  27. package/dist/features/canvas/useCanvasFrame.js +1 -1
  28. package/dist/features/content/ContentCollection.js +2 -2
  29. package/dist/features/content/collection-form.js +6 -7
  30. package/dist/features/page/CuratedPageContent.js +15 -24
  31. package/dist/features/page/UnplacedBlockContent.js +1 -6
  32. package/dist/features/preview/CamoxPreview.js +30 -33
  33. package/dist/features/preview/EditablePageContent.js +131 -151
  34. package/dist/features/preview/components/BlockActionsPopover.js +43 -152
  35. package/dist/features/preview/components/CanvasPreview.js +2 -2
  36. package/dist/features/preview/components/Frame.js +3 -3
  37. package/dist/features/preview/components/IconFieldEditor.js +1 -1
  38. package/dist/features/preview/components/ItemFieldsEditor.js +1 -3
  39. package/dist/features/preview/components/Overlays.js +18 -84
  40. package/dist/features/preview/components/PageTree.js +57 -103
  41. package/dist/features/preview/components/PreviewPanel.js +8 -11
  42. package/dist/features/preview/components/RecordCombobox.js +65 -78
  43. package/dist/features/preview/components/ReferenceFieldEditor.js +1 -19
  44. package/dist/features/preview/components/ReferenceListFieldEditor.js +1 -19
  45. package/dist/features/preview/components/RemovedBlockPlaceholder.js +2 -2
  46. package/dist/features/preview/components/SelectionEditor.js +5 -4
  47. package/dist/features/preview/components/useRepeatableItemActions.js +18 -24
  48. package/dist/features/preview/components/useUpdateBlockPosition.js +1 -1
  49. package/dist/features/preview/overlayMessages.js +1 -4
  50. package/dist/features/preview/previewStore.js +3 -23
  51. package/dist/features/renderer/RendererApp.js +17 -141
  52. package/dist/features/renderer/definitionAnswers.js +1 -8
  53. package/dist/features/renderer/rendererHost.js +1 -31
  54. package/dist/features/renderer/studioConnection.js +1 -25
  55. package/dist/features/runtime/pageApp.js +8 -4
  56. package/dist/features/runtime/runtime.js +3 -3
  57. package/dist/features/studio/agentRunToast.js +71 -0
  58. package/dist/features/studio/components/ChatWidget.js +2 -0
  59. package/dist/features/studio/components/DraftBar.js +2 -3
  60. package/dist/features/studio/components/DraftsControl.js +150 -176
  61. package/dist/features/studio/components/ReviewDraftSheet.js +344 -548
  62. package/dist/features/studio/components/ThreadMessages.js +89 -22
  63. package/dist/features/studio/draftLabel.js +1 -1
  64. package/dist/features/studio/studioStore.js +5 -17
  65. package/dist/features/studio/viewSwitch.js +2 -4
  66. package/dist/features/vite/blockBoilerplate.js +1 -1
  67. package/dist/features/vite/definitionsSync.js +1 -8
  68. package/dist/lib/queries.js +0 -1
  69. package/dist/lib/studio-definitions.js +0 -2
  70. package/dist/lib/studio-reads.js +0 -5
  71. package/dist/lib/use-thread.js +196 -138
  72. package/dist/package.js +1 -1
  73. package/dist/studio-overlays.css +0 -29
  74. package/dist/studio.css +1 -1
  75. package/package.json +6 -4
  76. package/skills/camox/SKILL.md +1 -1
  77. package/skills/camox/references/artifacts.md +89 -5
  78. package/skills/camox/references/block-definitions.md +21 -25
  79. package/skills/camox/references/dom-integrations.md +1 -1
  80. package/skills/camox/references/layout-definitions.md +3 -12
  81. package/skills/camox/references/routed-layouts.md +2 -2
  82. package/skills/camox-expand/SKILL.md +5 -5
  83. package/skills/camoxify/SKILL.md +4 -4
  84. package/dist/features/canvas/canvasInsertionSeams.js +0 -44
  85. package/dist/features/preview/components/AddBlockSheet.js +0 -314
  86. package/dist/features/preview/components/BlockPeek.js +0 -66
  87. package/dist/features/preview/components/BlockThumbnail.js +0 -281
  88. package/dist/features/preview/components/blockGridNavigation.js +0 -15
  89. 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 };