camox 0.32.0 → 0.33.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 (58) hide show
  1. package/dist/core/createBlock.d.ts +1 -1
  2. package/dist/core/createBlock.js +88 -108
  3. package/dist/core/hooks/useIsEditable.js +6 -10
  4. package/dist/core/lib/fieldTypes.js +10 -219
  5. package/dist/features/agent-chat/components/AgentChatSidebar.js +172 -0
  6. package/dist/features/agent-chat/components/AgentChatThread.js +9 -9
  7. package/dist/features/preview/CamoxPreview.js +137 -729
  8. package/dist/features/preview/components/{AddBlockSheet.js → AddBlockSidebar.js} +121 -131
  9. package/dist/features/preview/components/AssetFieldEditor.js +107 -82
  10. package/dist/features/preview/components/AssetLightbox.js +4 -4
  11. package/dist/features/preview/components/AssetPickerModal.js +277 -0
  12. package/dist/features/preview/components/BlockActionsPopover.js +18 -24
  13. package/dist/features/preview/components/CreatePageModal.js +6 -6
  14. package/dist/features/preview/components/FieldToolbar.js +64 -127
  15. package/dist/features/preview/components/ItemFieldsEditor.js +98 -125
  16. package/dist/features/preview/components/LeftSidebar.js +64 -0
  17. package/dist/features/preview/components/LinkFieldEditor.js +1 -1
  18. package/dist/features/preview/components/MultipleAssetFieldEditor.js +94 -56
  19. package/dist/features/preview/components/OverlayTracker.js +15 -15
  20. package/dist/features/preview/components/Overlays.js +12 -12
  21. package/dist/features/preview/components/PageEditorSidebar.js +563 -0
  22. package/dist/features/preview/components/PageInfoSidebar.js +1453 -0
  23. package/dist/features/preview/components/PageNavigatorSidebar.js +590 -0
  24. package/dist/features/preview/components/PagePicker.js +3 -3
  25. package/dist/features/preview/components/PageStatusBadge.js +1 -1
  26. package/dist/features/preview/components/PageTree.js +204 -564
  27. package/dist/features/preview/components/PreviewPanel.js +123 -158
  28. package/dist/features/preview/components/PreviewToolbar.js +260 -256
  29. package/dist/features/preview/components/PublishDialog.js +2 -2
  30. package/dist/features/preview/components/RepeatableItemsList.js +2 -2
  31. package/dist/features/preview/components/RightSidebar.js +109 -0
  32. package/dist/features/preview/components/UnlinkAssetButton.js +2 -2
  33. package/dist/features/preview/components/useRepeatableItemActions.js +3 -138
  34. package/dist/features/preview/components/useUpdateBlockPosition.js +1 -1
  35. package/dist/features/preview/previewQueryFns.js +23 -0
  36. package/dist/features/preview/previewStore.js +46 -63
  37. package/dist/features/provider/CamoxProvider.js +255 -70
  38. package/dist/features/provider/useAdminShortcuts.js +5 -48
  39. package/dist/features/routes/pageRoute.js +1 -1
  40. package/dist/features/studio/CamoxStudio.js +6 -5
  41. package/dist/features/studio/components/EnvironmentMenu.js +2 -2
  42. package/dist/features/studio/components/Navbar.js +53 -71
  43. package/dist/features/studio/components/ProjectMenu.js +1 -1
  44. package/dist/features/studio/components/UserButton.js +1 -1
  45. package/dist/features/studio/routes.js +7 -0
  46. package/dist/features/studio/useTheme.js +67 -33
  47. package/dist/features/vite/routeGeneration.js +68 -66
  48. package/dist/hooks/use-file-upload.js +1 -1
  49. package/dist/lib/auth.js +23 -22
  50. package/dist/lib/utils.js +1 -1
  51. package/dist/studio-overlays.css +1 -1
  52. package/dist/studio.css +1 -1
  53. package/package.json +4 -4
  54. package/dist/features/agent-chat/components/AgentChatSheet.js +0 -207
  55. package/dist/features/preview/components/AssetPickerGrid.js +0 -236
  56. package/dist/features/preview/components/PageContentSheet.js +0 -608
  57. package/dist/features/preview/components/PageMetadataModal.js +0 -908
  58. package/dist/features/preview/components/PreviewSideSheet.js +0 -76
@@ -0,0 +1,563 @@
1
+ import { blockMutations, blockQueries, fileQueries, repeatableItemMutations } from "../../../lib/queries.js";
2
+ import { trackClientEvent } from "../../../lib/telemetry-client.js";
3
+ import { cn } from "../../../lib/utils.js";
4
+ import { previewStore, selectionBlockId, selectionField, selectionItemId } from "../previewStore.js";
5
+ import { isFileMarker } from "../../../lib/normalized-data.js";
6
+ import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
7
+ import { useRequireDraftSource } from "../../../core/hooks/useRequireDraftSource.js";
8
+ import { fieldTypesDictionary } from "../../../core/lib/fieldTypes.js";
9
+ import { SingleAssetFieldEditor } from "./AssetFieldEditor.js";
10
+ import { useRepeatableItemActions } from "./useRepeatableItemActions.js";
11
+ import { ItemFieldsEditor, formatFieldName } from "./ItemFieldsEditor.js";
12
+ import { LinkFieldEditor } from "./LinkFieldEditor.js";
13
+ import { MultipleAssetFieldEditor } from "./MultipleAssetFieldEditor.js";
14
+ import { Label } from "@camox/ui/label";
15
+ import { useMutation, useQueries, useQuery } from "@tanstack/react-query";
16
+ import { useSelector } from "@xstate/store-react";
17
+ import * as React from "react";
18
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
+ import { Button } from "@camox/ui/button";
20
+ import { CircleMinus, CirclePlus, CornerLeftUp } from "lucide-react";
21
+ import { Spinner } from "@camox/ui/spinner";
22
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@camox/ui/select";
23
+ import { Switch } from "@camox/ui/switch";
24
+
25
+ //#region src/features/preview/components/PageEditorSidebar.tsx
26
+ const getSettingsFields = (schema) => {
27
+ const properties = schema?.properties;
28
+ if (!properties) return [];
29
+ return Object.keys(properties).map((fieldName) => {
30
+ const prop = properties[fieldName];
31
+ return {
32
+ name: fieldName,
33
+ fieldType: prop.fieldType,
34
+ label: prop.title,
35
+ enumLabels: prop.enumLabels,
36
+ enumValues: prop.enum
37
+ };
38
+ });
39
+ };
40
+ /**
41
+ * Builds the path of fieldNames from the block root to the given item,
42
+ * then walks the schema down that path to return the sub-schema for the item's fields.
43
+ */
44
+ const getSchemaForItem = (contentSchema, itemId, itemsMap) => {
45
+ const path = [];
46
+ let current = itemsMap.get(itemId);
47
+ while (current) {
48
+ path.unshift(current.fieldName);
49
+ current = current.parentItemId ? itemsMap.get(current.parentItemId) : void 0;
50
+ }
51
+ let schema = contentSchema;
52
+ for (const fieldName of path) {
53
+ const prop = schema?.properties?.[fieldName];
54
+ if (!prop?.items) return null;
55
+ schema = prop.items;
56
+ }
57
+ return schema;
58
+ };
59
+ /**
60
+ * Like `getSchemaForItem` but returns the **array** schema (one level above
61
+ * the items schema), where per-item settings metadata lives.
62
+ */
63
+ const getArraySchemaForItem = (contentSchema, itemId, itemsMap) => {
64
+ const path = [];
65
+ let current = itemsMap.get(itemId);
66
+ while (current) {
67
+ path.unshift(current.fieldName);
68
+ current = current.parentItemId ? itemsMap.get(current.parentItemId) : void 0;
69
+ }
70
+ let schema = contentSchema;
71
+ for (let i = 0; i < path.length; i++) {
72
+ const prop = schema?.properties?.[path[i]];
73
+ if (!prop?.items) return null;
74
+ if (i === path.length - 1) return prop;
75
+ schema = prop.items;
76
+ }
77
+ return null;
78
+ };
79
+ /**
80
+ * Builds the ancestor chain from root to this item (inclusive).
81
+ * Returns items in order from root-most ancestor to the item itself.
82
+ */
83
+ const buildAncestorChain = (itemId, itemsMap) => {
84
+ const chain = [];
85
+ let current = itemsMap.get(itemId);
86
+ while (current) {
87
+ chain.unshift(current);
88
+ current = current.parentItemId ? itemsMap.get(current.parentItemId) : void 0;
89
+ }
90
+ return chain;
91
+ };
92
+ const PageEditorSidebar = () => {
93
+ const camoxApp = useCamoxApp();
94
+ const updateContent = useMutation(blockMutations.updateContent());
95
+ const updateSettings = useMutation(blockMutations.updateSettings());
96
+ const updateRepeatableContent = useMutation(repeatableItemMutations.updateContent());
97
+ const updateRepeatableSettings = useMutation(repeatableItemMutations.updateSettings());
98
+ const requireDraft = useRequireDraftSource();
99
+ const selection = useSelector(previewStore, (state) => state.context.selection);
100
+ const iframeElement = useSelector(previewStore, (state_0) => state_0.context.iframeElement);
101
+ const isReadOnly = useSelector(previewStore, (state_1) => state_1.context.previewSource) !== "draft";
102
+ const postToIframe = React.useCallback((message) => {
103
+ if (!iframeElement?.contentWindow) return;
104
+ iframeElement.contentWindow.postMessage(message, "*");
105
+ }, [iframeElement]);
106
+ const blockId = selectionBlockId(selection);
107
+ const currentItemId = selectionItemId(selection);
108
+ const fieldInfo = selectionField(selection);
109
+ const { data: blockBundle } = useQuery({
110
+ ...blockQueries.get(blockId),
111
+ enabled: blockId != null
112
+ });
113
+ const block = blockBundle?.block ?? null;
114
+ const itemsMap = React.useMemo(() => new Map((blockBundle?.repeatableItems ?? []).map((i) => [i.id, i])), [blockBundle?.repeatableItems]);
115
+ const fileIds = React.useMemo(() => (blockBundle?.files ?? []).map((f) => f.id), [blockBundle?.files]);
116
+ const fileResults = useQueries({ queries: fileIds.map((id) => fileQueries.get(id)) });
117
+ const filesMap = React.useMemo(() => {
118
+ const map = new Map((blockBundle?.files ?? []).map((f_0) => [f_0.id, f_0]));
119
+ for (let i_0 = 0; i_0 < fileIds.length; i_0++) {
120
+ const data = fileResults[i_0]?.data;
121
+ if (data) map.set(data.id, data);
122
+ }
123
+ return map;
124
+ }, [
125
+ blockBundle?.files,
126
+ fileIds,
127
+ fileResults
128
+ ]);
129
+ const blockDef = block ? camoxApp.getBlockById(block.type) : null;
130
+ const settingsFields = React.useMemo(() => {
131
+ return blockDef ? getSettingsFields(blockDef._internal.settingsSchema) : [];
132
+ }, [blockDef]);
133
+ const itemArraySchema = React.useMemo(() => {
134
+ if (!blockDef || currentItemId == null) return null;
135
+ return getArraySchemaForItem(blockDef._internal.contentSchema, currentItemId, itemsMap);
136
+ }, [
137
+ blockDef,
138
+ currentItemId,
139
+ itemsMap
140
+ ]);
141
+ const itemSettingsFields = React.useMemo(() => {
142
+ return getSettingsFields(itemArraySchema?.itemSettingsSchema);
143
+ }, [itemArraySchema]);
144
+ const currentSchema = React.useMemo(() => {
145
+ if (!blockDef) return null;
146
+ if (currentItemId == null) return blockDef._internal.contentSchema;
147
+ return getSchemaForItem(blockDef._internal.contentSchema, currentItemId, itemsMap);
148
+ }, [
149
+ blockDef,
150
+ currentItemId,
151
+ itemsMap
152
+ ]);
153
+ const currentItem = currentItemId != null ? itemsMap.get(currentItemId) : null;
154
+ const isItemLoading = currentItemId != null && !currentItem;
155
+ const siblingCount = React.useMemo(() => {
156
+ if (!currentItem) return 0;
157
+ let count = 0;
158
+ for (const it of itemsMap.values()) if (it.fieldName === currentItem.fieldName && it.parentItemId === currentItem.parentItemId) count++;
159
+ return count;
160
+ }, [currentItem, itemsMap]);
161
+ const { canAdd: canAddSibling, addItem: addSibling, canRemove: canRemoveCurrent, removeItem: removeCurrent } = useRepeatableItemActions({
162
+ blockId: block?.id ?? -1,
163
+ fieldName: currentItem?.fieldName ?? "",
164
+ parentItemId: currentItem?.parentItemId ?? null,
165
+ arraySchema: itemArraySchema,
166
+ siblingCount
167
+ });
168
+ const rawCurrentData = currentItem ? currentItem.content : block?.content ?? {};
169
+ const currentData = React.useMemo(() => {
170
+ const resolveFile = (marker) => {
171
+ const file = filesMap.get(marker._fileId);
172
+ return file ? {
173
+ url: file.url,
174
+ alt: file.alt,
175
+ filename: file.filename,
176
+ mimeType: file.mimeType,
177
+ _fileId: marker._fileId
178
+ } : {
179
+ url: "",
180
+ alt: "",
181
+ filename: "",
182
+ mimeType: ""
183
+ };
184
+ };
185
+ const resolveValue = (value) => {
186
+ if (isFileMarker(value)) return resolveFile(value);
187
+ if (Array.isArray(value)) return value.map(resolveValue);
188
+ if (value && typeof value === "object" && !Array.isArray(value)) {
189
+ const obj = value;
190
+ const resolved = {};
191
+ for (const [k, v] of Object.entries(obj)) resolved[k] = resolveValue(v);
192
+ return resolved;
193
+ }
194
+ return value;
195
+ };
196
+ const resolved_0 = {};
197
+ for (const [key, value_0] of Object.entries(rawCurrentData)) resolved_0[key] = resolveValue(value_0);
198
+ return resolved_0;
199
+ }, [rawCurrentData, filesMap]);
200
+ const isViewingLink = fieldInfo?.fieldType === "Link";
201
+ const linkFieldName = isViewingLink ? fieldInfo.fieldName : null;
202
+ const isViewingImage = fieldInfo?.fieldType === "Image";
203
+ const imageFieldName = isViewingImage ? fieldInfo.fieldName : null;
204
+ const isViewingFile = fieldInfo?.fieldType === "File";
205
+ const fileFieldName = isViewingFile ? fieldInfo.fieldName : null;
206
+ const isViewingAsset = isViewingImage || isViewingFile;
207
+ const assetFieldName = imageFieldName ?? fileFieldName;
208
+ const assetType = isViewingImage ? "Image" : "File";
209
+ const isMultipleAsset = React.useMemo(() => {
210
+ if (!isViewingAsset || !assetFieldName) return false;
211
+ const prop = currentSchema?.properties?.[assetFieldName];
212
+ return prop?.fieldType === "ImageList" || prop?.fieldType === "FileList";
213
+ }, [
214
+ isViewingAsset,
215
+ assetFieldName,
216
+ currentSchema
217
+ ]);
218
+ const sessionDirtyRef = React.useRef(false);
219
+ const trackedBlockIdRef = React.useRef(null);
220
+ React.useEffect(() => {
221
+ if (!block) {
222
+ trackedBlockIdRef.current = null;
223
+ return;
224
+ }
225
+ if (trackedBlockIdRef.current === block.id) return;
226
+ trackedBlockIdRef.current = block.id;
227
+ sessionDirtyRef.current = false;
228
+ trackClientEvent("page_editor_sidebar_opened", { blockType: block.type });
229
+ }, [block]);
230
+ React.useEffect(() => {
231
+ if (!block) return;
232
+ return () => {
233
+ if (!sessionDirtyRef.current) return;
234
+ trackClientEvent("block_edited", {
235
+ via: "page-editor-sidebar",
236
+ blockType: block.type
237
+ });
238
+ sessionDirtyRef.current = false;
239
+ };
240
+ }, [block]);
241
+ const fieldIdPrefix = React.useId();
242
+ const handleBlockFieldChange = React.useCallback((fieldName, value_1) => {
243
+ if (!block) return;
244
+ if (!requireDraft()) return;
245
+ sessionDirtyRef.current = true;
246
+ updateContent.mutate({
247
+ id: block.id,
248
+ content: { [fieldName]: value_1 }
249
+ });
250
+ }, [
251
+ block,
252
+ updateContent,
253
+ requireDraft
254
+ ]);
255
+ const handleItemFieldChange = React.useCallback((fieldName_0, value_2) => {
256
+ if (currentItemId == null) return;
257
+ if (!requireDraft()) return;
258
+ sessionDirtyRef.current = true;
259
+ updateRepeatableContent.mutate({
260
+ id: currentItemId,
261
+ content: { [fieldName_0]: value_2 }
262
+ });
263
+ }, [
264
+ currentItemId,
265
+ updateRepeatableContent,
266
+ requireDraft
267
+ ]);
268
+ const activeFieldChangeHandler = currentItemId != null ? handleItemFieldChange : handleBlockFieldChange;
269
+ const ancestorChain = React.useMemo(() => currentItemId != null ? buildAncestorChain(currentItemId, itemsMap) : [], [currentItemId, itemsMap]);
270
+ if (!block || !blockDef || !currentSchema) return /* @__PURE__ */ jsxs("div", {
271
+ className: "text-muted-foreground flex flex-1 items-center justify-center px-2 text-sm",
272
+ children: [/* @__PURE__ */ jsx(Spinner, { className: "mr-2 size-3.5" }), " Loading block..."]
273
+ });
274
+ const fieldHasOwnView = fieldInfo ? fieldTypesDictionary[fieldInfo.fieldType].hasOwnView : false;
275
+ const [pageNavigationItem, ...contentNavigationItems] = [
276
+ {
277
+ key: "page",
278
+ label: "Page",
279
+ isCurrent: false,
280
+ onClick: () => previewStore.send({ type: "clearSelection" })
281
+ },
282
+ {
283
+ key: "block",
284
+ label: block.summary || blockDef._internal.title,
285
+ isCurrent: ancestorChain.length === 0 && !fieldHasOwnView,
286
+ onClick: () => previewStore.send({
287
+ type: "setFocusedBlock",
288
+ blockId: block.id
289
+ })
290
+ },
291
+ ...ancestorChain.map((ancestor) => ({
292
+ key: `item-${ancestor.id}`,
293
+ label: ancestor.summary || formatFieldName(ancestor.fieldName),
294
+ isCurrent: ancestor.id === currentItemId && !fieldHasOwnView && ancestor.id === ancestorChain[ancestorChain.length - 1]?.id,
295
+ onClick: () => previewStore.send({
296
+ type: "selectItem",
297
+ blockId: block.id,
298
+ itemId: ancestor.id
299
+ })
300
+ })),
301
+ ...fieldHasOwnView && fieldInfo ? [{
302
+ key: `field-${fieldInfo.fieldName}`,
303
+ label: currentSchema?.properties?.[fieldInfo.fieldName]?.title ?? formatFieldName(fieldInfo.fieldName),
304
+ isCurrent: true,
305
+ onClick: void 0
306
+ }] : []
307
+ ];
308
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
309
+ className: "border-border flex flex-col gap-1.5 border-b px-2 py-4",
310
+ children: /* @__PURE__ */ jsxs("nav", {
311
+ "aria-label": "Selection path",
312
+ className: "text-muted-foreground text-sm",
313
+ children: [pageNavigationItem?.onClick ? /* @__PURE__ */ jsx("button", {
314
+ type: "button",
315
+ className: "hover:text-foreground block min-w-0 cursor-pointer truncate py-0.5 text-left transition-colors",
316
+ onClick: pageNavigationItem.onClick,
317
+ children: pageNavigationItem.label
318
+ }) : /* @__PURE__ */ jsx("span", {
319
+ className: "text-foreground block min-w-0 truncate py-0.5 font-medium",
320
+ children: pageNavigationItem?.label
321
+ }), /* @__PURE__ */ jsx("ol", {
322
+ className: "flex flex-col",
323
+ children: contentNavigationItems.map((item, index) => {
324
+ return /* @__PURE__ */ jsxs("li", {
325
+ className: "relative min-w-0 pl-6",
326
+ children: [/* @__PURE__ */ jsx("svg", {
327
+ "aria-hidden": "true",
328
+ className: "text-muted-foreground pointer-events-none absolute top-0 left-0 h-full w-4",
329
+ fill: "none",
330
+ preserveAspectRatio: "none",
331
+ viewBox: "0 0 16 24",
332
+ children: /* @__PURE__ */ jsx("path", {
333
+ d: index < contentNavigationItems.length - 1 ? "M1 0 V24 M1 5 Q1 12 8 12 H16" : "M1 0 V5 Q1 12 8 12 H16",
334
+ stroke: "currentColor",
335
+ strokeLinecap: "square",
336
+ strokeWidth: "1.25",
337
+ vectorEffect: "non-scaling-stroke"
338
+ })
339
+ }), item.onClick ? /* @__PURE__ */ jsx("button", {
340
+ type: "button",
341
+ className: cn("hover:text-foreground relative -top-px block min-w-0 cursor-pointer truncate py-0.5 text-left transition-colors", item.isCurrent && "text-foreground font-medium"),
342
+ onClick: item.onClick,
343
+ children: item.label
344
+ }) : /* @__PURE__ */ jsx("span", {
345
+ className: "text-foreground relative -top-px block min-w-0 truncate py-0.5 font-medium",
346
+ children: item.label
347
+ })]
348
+ }, item.key);
349
+ })
350
+ })]
351
+ })
352
+ }), /* @__PURE__ */ jsxs("div", {
353
+ className: "relative flex-1 overflow-auto",
354
+ children: [isReadOnly && /* @__PURE__ */ jsx("button", {
355
+ type: "button",
356
+ "aria-label": "Switch to draft to edit",
357
+ className: "absolute inset-0 z-10 h-full w-full cursor-not-allowed",
358
+ onClick: () => previewStore.send({ type: "requestDraftSwitch" })
359
+ }), /* @__PURE__ */ jsx("div", {
360
+ className: cn(isReadOnly && "pointer-events-none opacity-50"),
361
+ children: isItemLoading ? /* @__PURE__ */ jsx("div", {
362
+ className: "flex h-full items-center justify-center py-12",
363
+ children: /* @__PURE__ */ jsx(Spinner, {})
364
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
365
+ currentItemId == null && !fieldHasOwnView && settingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
366
+ className: "border-border space-y-4 border-b px-2 py-4",
367
+ children: [/* @__PURE__ */ jsx(Label, {
368
+ className: "text-muted-foreground",
369
+ children: "Settings"
370
+ }), settingsFields.map((field) => {
371
+ const label = field.label ?? formatFieldName(field.name);
372
+ const settingsValues = block.settings ?? {};
373
+ if (field.fieldType === "Enum") {
374
+ const value_3 = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? "";
375
+ return /* @__PURE__ */ jsxs("div", {
376
+ className: "space-y-2",
377
+ children: [/* @__PURE__ */ jsx(Label, {
378
+ htmlFor: `setting-${field.name}`,
379
+ children: label
380
+ }), /* @__PURE__ */ jsxs(Select, {
381
+ value: value_3,
382
+ onValueChange: (newValue) => {
383
+ if (!requireDraft()) return;
384
+ sessionDirtyRef.current = true;
385
+ updateSettings.mutate({
386
+ id: block.id,
387
+ settings: { [field.name]: newValue }
388
+ });
389
+ },
390
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
391
+ id: `setting-${field.name}`,
392
+ children: /* @__PURE__ */ jsx(SelectValue, {})
393
+ }), /* @__PURE__ */ jsx(SelectContent, { children: field.enumValues?.map((enumValue) => /* @__PURE__ */ jsx(SelectItem, {
394
+ value: enumValue,
395
+ children: field.enumLabels?.[enumValue] ?? enumValue
396
+ }, enumValue)) })]
397
+ })]
398
+ }, field.name);
399
+ }
400
+ if (field.fieldType === "Boolean") {
401
+ const checked = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? false;
402
+ return /* @__PURE__ */ jsxs("div", {
403
+ className: "flex items-center justify-between",
404
+ children: [/* @__PURE__ */ jsx(Label, {
405
+ htmlFor: `setting-${field.name}`,
406
+ children: label
407
+ }), /* @__PURE__ */ jsx(Switch, {
408
+ id: `setting-${field.name}`,
409
+ checked,
410
+ onCheckedChange: (newValue_0) => {
411
+ if (!requireDraft()) return;
412
+ sessionDirtyRef.current = true;
413
+ updateSettings.mutate({
414
+ id: block.id,
415
+ settings: { [field.name]: newValue_0 }
416
+ });
417
+ }
418
+ })]
419
+ }, field.name);
420
+ }
421
+ return null;
422
+ })]
423
+ }),
424
+ currentItemId != null && !fieldHasOwnView && itemSettingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
425
+ className: "border-border space-y-4 border-b px-2 py-4",
426
+ children: [/* @__PURE__ */ jsx(Label, {
427
+ className: "text-muted-foreground",
428
+ children: "Settings"
429
+ }), itemSettingsFields.map((field_0) => {
430
+ const label_0 = field_0.label ?? formatFieldName(field_0.name);
431
+ const itemSettingsValues = currentItem?.settings ?? {};
432
+ const itemSettingsSchemaProps = itemArraySchema?.itemSettingsSchema?.properties;
433
+ if (field_0.fieldType === "Enum") {
434
+ const value_4 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? "";
435
+ return /* @__PURE__ */ jsxs("div", {
436
+ className: "space-y-2",
437
+ children: [/* @__PURE__ */ jsx(Label, {
438
+ htmlFor: `item-setting-${field_0.name}`,
439
+ children: label_0
440
+ }), /* @__PURE__ */ jsxs(Select, {
441
+ value: value_4,
442
+ onValueChange: (newValue_1) => {
443
+ if (!requireDraft()) return;
444
+ sessionDirtyRef.current = true;
445
+ updateRepeatableSettings.mutate({
446
+ id: currentItemId,
447
+ settings: { [field_0.name]: newValue_1 }
448
+ });
449
+ },
450
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
451
+ id: `item-setting-${field_0.name}`,
452
+ children: /* @__PURE__ */ jsx(SelectValue, {})
453
+ }), /* @__PURE__ */ jsx(SelectContent, { children: field_0.enumValues?.map((enumValue_0) => /* @__PURE__ */ jsx(SelectItem, {
454
+ value: enumValue_0,
455
+ children: field_0.enumLabels?.[enumValue_0] ?? enumValue_0
456
+ }, enumValue_0)) })]
457
+ })]
458
+ }, field_0.name);
459
+ }
460
+ if (field_0.fieldType === "Boolean") {
461
+ const checked_0 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? false;
462
+ return /* @__PURE__ */ jsxs("div", {
463
+ className: "flex items-center justify-between",
464
+ children: [/* @__PURE__ */ jsx(Label, {
465
+ htmlFor: `item-setting-${field_0.name}`,
466
+ children: label_0
467
+ }), /* @__PURE__ */ jsx(Switch, {
468
+ id: `item-setting-${field_0.name}`,
469
+ checked: checked_0,
470
+ onCheckedChange: (newValue_2) => {
471
+ if (!requireDraft()) return;
472
+ sessionDirtyRef.current = true;
473
+ updateRepeatableSettings.mutate({
474
+ id: currentItemId,
475
+ settings: { [field_0.name]: newValue_2 }
476
+ });
477
+ }
478
+ })]
479
+ }, field_0.name);
480
+ }
481
+ return null;
482
+ })]
483
+ }),
484
+ isViewingAsset && assetFieldName && isMultipleAsset && /* @__PURE__ */ jsx(MultipleAssetFieldEditor, {
485
+ fieldName: assetFieldName,
486
+ assetType,
487
+ currentData,
488
+ onFieldChange: activeFieldChangeHandler
489
+ }),
490
+ isViewingAsset && assetFieldName && !isMultipleAsset && /* @__PURE__ */ jsx(SingleAssetFieldEditor, {
491
+ fieldName: assetFieldName,
492
+ assetType,
493
+ currentData,
494
+ onFieldChange: activeFieldChangeHandler
495
+ }),
496
+ !isViewingAsset && isViewingLink && linkFieldName && /* @__PURE__ */ jsx("div", {
497
+ className: "px-2 py-4",
498
+ children: /* @__PURE__ */ jsx(LinkFieldEditor, {
499
+ fieldName: linkFieldName,
500
+ linkValue: currentData[linkFieldName] ?? {
501
+ type: "external",
502
+ text: "",
503
+ href: "",
504
+ newTab: false
505
+ },
506
+ onSave: (fieldName_1, value_5) => {
507
+ activeFieldChangeHandler(fieldName_1, value_5);
508
+ }
509
+ })
510
+ }),
511
+ !isViewingAsset && !isViewingLink && (currentItemId == null || currentItem) && /* @__PURE__ */ jsx(ItemFieldsEditor, {
512
+ schema: currentSchema,
513
+ data: currentData,
514
+ blockId: block.id,
515
+ itemId: currentItemId ?? void 0,
516
+ onFieldChange: activeFieldChangeHandler,
517
+ postToIframe,
518
+ filesMap,
519
+ itemsMap,
520
+ fieldIdPrefix
521
+ }, currentItemId ?? `block-${block.id}`),
522
+ !isViewingAsset && !isViewingLink && currentItemId != null && currentItem && /* @__PURE__ */ jsxs("div", {
523
+ className: "border-border flex items-center gap-1 border-t px-2 py-4",
524
+ children: [
525
+ canAddSibling && /* @__PURE__ */ jsxs(Button, {
526
+ type: "button",
527
+ variant: "ghost",
528
+ size: "sm",
529
+ className: "text-muted-foreground justify-start",
530
+ onClick: () => {
531
+ if (!requireDraft()) return;
532
+ addSibling({ afterPosition: currentItem.position });
533
+ },
534
+ children: [/* @__PURE__ */ jsx(CirclePlus, { className: "h-4 w-4" }), "Add item"]
535
+ }),
536
+ canRemoveCurrent && /* @__PURE__ */ jsxs(Button, {
537
+ type: "button",
538
+ variant: "ghost",
539
+ size: "sm",
540
+ className: "text-muted-foreground justify-start",
541
+ onClick: () => {
542
+ if (!requireDraft()) return;
543
+ removeCurrent(currentItemId, { onSuccess: () => previewStore.send({ type: "selectParent" }) });
544
+ },
545
+ children: [/* @__PURE__ */ jsx(CircleMinus, { className: "h-4 w-4" }), "Remove item"]
546
+ }),
547
+ /* @__PURE__ */ jsxs(Button, {
548
+ type: "button",
549
+ variant: "ghost",
550
+ size: "sm",
551
+ className: "text-muted-foreground justify-start",
552
+ onClick: () => previewStore.send({ type: "selectParent" }),
553
+ children: [/* @__PURE__ */ jsx(CornerLeftUp, { className: "h-4 w-4" }), "Select parent"]
554
+ })
555
+ ]
556
+ })
557
+ ] })
558
+ })]
559
+ })] });
560
+ };
561
+
562
+ //#endregion
563
+ export { PageEditorSidebar };