camox 0.24.1 → 0.26.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 (26) hide show
  1. package/dist/core/hooks/useRequireDraftSource.js +27 -0
  2. package/dist/features/preview/CamoxPreview.d.ts +4 -0
  3. package/dist/features/preview/CamoxPreview.js +454 -181
  4. package/dist/features/preview/components/AddBlockSheet.js +79 -75
  5. package/dist/features/preview/components/AssetLightbox.js +1 -1
  6. package/dist/features/preview/components/BlockActionsPopover.js +39 -22
  7. package/dist/features/preview/components/DraftSwitchDialog.js +66 -0
  8. package/dist/features/preview/components/EditPageModal.js +9 -9
  9. package/dist/features/preview/components/LinkFieldEditor.js +1 -1
  10. package/dist/features/preview/components/Overlays.js +6 -2
  11. package/dist/features/preview/components/PageContentSheet.js +222 -190
  12. package/dist/features/preview/components/PagePicker.js +81 -3
  13. package/dist/features/preview/components/PageTree.js +418 -327
  14. package/dist/features/preview/components/PreviewToolbar.js +172 -158
  15. package/dist/features/preview/components/PublishDialog.js +111 -0
  16. package/dist/features/preview/components/useRepeatableItemActions.js +26 -20
  17. package/dist/features/preview/components/useUpdateBlockPosition.js +10 -9
  18. package/dist/features/preview/previewStore.js +38 -1
  19. package/dist/features/provider/useAdminShortcuts.js +7 -2
  20. package/dist/features/routes/pageRoute.d.ts +6 -2
  21. package/dist/features/routes/pageRoute.js +10 -6
  22. package/dist/features/vite/vite.js +6 -5
  23. package/dist/lib/normalized-data.js +87 -88
  24. package/dist/lib/queries.js +12 -6
  25. package/dist/studio.css +1 -1
  26. package/package.json +4 -4
@@ -3,8 +3,10 @@ import { previewStore, selectionBlockId, selectionField, selectionItemId } from
3
3
  import { PreviewSideSheet, Sheet } from "./PreviewSideSheet.js";
4
4
  import { actionsStore } from "../../provider/actionsStore.js";
5
5
  import { blockMutations, blockQueries, fileQueries, repeatableItemMutations } from "../../../lib/queries.js";
6
+ import { cn } from "../../../lib/utils.js";
6
7
  import { isFileMarker } from "../../../lib/normalized-data.js";
7
8
  import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
9
+ import { useRequireDraftSource } from "../../../core/hooks/useRequireDraftSource.js";
8
10
  import { fieldTypesDictionary } from "../../../core/lib/fieldTypes.js";
9
11
  import { SingleAssetFieldEditor } from "./AssetFieldEditor.js";
10
12
  import { useRepeatableItemActions } from "./useRepeatableItemActions.js";
@@ -17,11 +19,11 @@ import { useSelector } from "@xstate/store-react";
17
19
  import * as React from "react";
18
20
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
21
  import { Button } from "@camox/ui/button";
22
+ import { Switch } from "@camox/ui/switch";
20
23
  import { CircleMinus, CirclePlus, CornerLeftUp } from "lucide-react";
21
24
  import { Spinner } from "@camox/ui/spinner";
22
25
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@camox/ui/dropdown-menu";
23
26
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@camox/ui/select";
24
- import { Switch } from "@camox/ui/switch";
25
27
  import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@camox/ui/breadcrumb";
26
28
 
27
29
  //#region src/features/preview/components/PageContentSheet.tsx
@@ -97,9 +99,11 @@ const PageContentSheet = () => {
97
99
  const updateSettings = useMutation(blockMutations.updateSettings());
98
100
  const updateRepeatableContent = useMutation(repeatableItemMutations.updateContent());
99
101
  const updateRepeatableSettings = useMutation(repeatableItemMutations.updateSettings());
102
+ const requireDraft = useRequireDraftSource();
100
103
  const isOpen = useSelector(previewStore, (state) => state.context.isPageContentSheetOpen);
101
104
  const selection = useSelector(previewStore, (state_0) => state_0.context.selection);
102
105
  const iframeElement = useSelector(previewStore, (state_1) => state_1.context.iframeElement);
106
+ const isReadOnly = useSelector(previewStore, (state_2) => state_2.context.previewSource) !== "draft";
103
107
  const postToIframe = React.useCallback((message) => {
104
108
  if (!iframeElement?.contentWindow) return;
105
109
  iframeElement.contentWindow.postMessage(message, "*");
@@ -269,20 +273,30 @@ const PageContentSheet = () => {
269
273
  }, [blockId, isOpen]);
270
274
  const handleBlockFieldChange = React.useCallback((fieldName, value_1) => {
271
275
  if (!block) return;
276
+ if (!requireDraft()) return;
272
277
  sessionDirtyRef.current = true;
273
278
  updateContent.mutate({
274
279
  id: block.id,
275
280
  content: { [fieldName]: value_1 }
276
281
  });
277
- }, [block, updateContent]);
282
+ }, [
283
+ block,
284
+ updateContent,
285
+ requireDraft
286
+ ]);
278
287
  const handleItemFieldChange = React.useCallback((fieldName_0, value_2) => {
279
288
  if (currentItemId == null) return;
289
+ if (!requireDraft()) return;
280
290
  sessionDirtyRef.current = true;
281
291
  updateRepeatableContent.mutate({
282
292
  id: currentItemId,
283
293
  content: { [fieldName_0]: value_2 }
284
294
  });
285
- }, [currentItemId, updateRepeatableContent]);
295
+ }, [
296
+ currentItemId,
297
+ updateRepeatableContent,
298
+ requireDraft
299
+ ]);
286
300
  const activeFieldChangeHandler = currentItemId != null ? handleItemFieldChange : handleBlockFieldChange;
287
301
  const handleOpenChange = (open) => {
288
302
  if (open) return;
@@ -378,196 +392,214 @@ const PageContentSheet = () => {
378
392
  ]
379
393
  })
380
394
  })]
381
- }), /* @__PURE__ */ jsx("div", {
382
- className: "flex-1 overflow-auto",
383
- children: isItemLoading ? /* @__PURE__ */ jsx("div", {
384
- className: "flex h-full items-center justify-center py-12",
385
- children: /* @__PURE__ */ jsx(Spinner, {})
386
- }) : /* @__PURE__ */ jsxs(Fragment, { children: [
387
- currentItemId == null && !fieldHasOwnView && settingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
388
- className: "border-border space-y-4 border-b px-4 py-4",
389
- children: [/* @__PURE__ */ jsx(Label, {
390
- className: "text-muted-foreground",
391
- children: "Settings"
392
- }), settingsFields.map((field) => {
393
- const label = field.label ?? formatFieldName(field.name);
394
- const settingsValues = block.settings ?? {};
395
- if (field.fieldType === "Enum") {
396
- const value_3 = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? "";
397
- return /* @__PURE__ */ jsxs("div", {
398
- className: "space-y-2",
399
- children: [/* @__PURE__ */ jsx(Label, {
400
- htmlFor: `setting-${field.name}`,
401
- children: label
402
- }), /* @__PURE__ */ jsxs(Select, {
403
- value: value_3,
404
- onValueChange: (newValue) => {
405
- sessionDirtyRef.current = true;
406
- updateSettings.mutate({
407
- id: block.id,
408
- settings: { [field.name]: newValue }
409
- });
410
- },
411
- children: [/* @__PURE__ */ jsx(SelectTrigger, {
395
+ }), /* @__PURE__ */ jsxs("div", {
396
+ className: "relative flex-1 overflow-auto",
397
+ children: [isReadOnly && /* @__PURE__ */ jsx("button", {
398
+ type: "button",
399
+ "aria-label": "Switch to draft to edit",
400
+ className: "absolute inset-0 z-10 h-full w-full cursor-not-allowed",
401
+ onClick: () => previewStore.send({ type: "requestDraftSwitch" })
402
+ }), /* @__PURE__ */ jsx("div", {
403
+ className: cn(isReadOnly && "pointer-events-none opacity-50"),
404
+ children: isItemLoading ? /* @__PURE__ */ jsx("div", {
405
+ className: "flex h-full items-center justify-center py-12",
406
+ children: /* @__PURE__ */ jsx(Spinner, {})
407
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
408
+ currentItemId == null && !fieldHasOwnView && settingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
409
+ className: "border-border space-y-4 border-b px-4 py-4",
410
+ children: [/* @__PURE__ */ jsx(Label, {
411
+ className: "text-muted-foreground",
412
+ children: "Settings"
413
+ }), settingsFields.map((field) => {
414
+ const label = field.label ?? formatFieldName(field.name);
415
+ const settingsValues = block.settings ?? {};
416
+ if (field.fieldType === "Enum") {
417
+ const value_3 = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? "";
418
+ return /* @__PURE__ */ jsxs("div", {
419
+ className: "space-y-2",
420
+ children: [/* @__PURE__ */ jsx(Label, {
421
+ htmlFor: `setting-${field.name}`,
422
+ children: label
423
+ }), /* @__PURE__ */ jsxs(Select, {
424
+ value: value_3,
425
+ onValueChange: (newValue) => {
426
+ if (!requireDraft()) return;
427
+ sessionDirtyRef.current = true;
428
+ updateSettings.mutate({
429
+ id: block.id,
430
+ settings: { [field.name]: newValue }
431
+ });
432
+ },
433
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
434
+ id: `setting-${field.name}`,
435
+ children: /* @__PURE__ */ jsx(SelectValue, {})
436
+ }), /* @__PURE__ */ jsx(SelectContent, { children: field.enumValues?.map((enumValue) => /* @__PURE__ */ jsx(SelectItem, {
437
+ value: enumValue,
438
+ children: field.enumLabels?.[enumValue] ?? enumValue
439
+ }, enumValue)) })]
440
+ })]
441
+ }, field.name);
442
+ }
443
+ if (field.fieldType === "Boolean") {
444
+ const checked = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? false;
445
+ return /* @__PURE__ */ jsxs("div", {
446
+ className: "flex items-center justify-between",
447
+ children: [/* @__PURE__ */ jsx(Label, {
448
+ htmlFor: `setting-${field.name}`,
449
+ children: label
450
+ }), /* @__PURE__ */ jsx(Switch, {
412
451
  id: `setting-${field.name}`,
413
- children: /* @__PURE__ */ jsx(SelectValue, {})
414
- }), /* @__PURE__ */ jsx(SelectContent, { children: field.enumValues?.map((enumValue) => /* @__PURE__ */ jsx(SelectItem, {
415
- value: enumValue,
416
- children: field.enumLabels?.[enumValue] ?? enumValue
417
- }, enumValue)) })]
418
- })]
419
- }, field.name);
420
- }
421
- if (field.fieldType === "Boolean") {
422
- const checked = settingsValues[field.name] ?? (blockDef._internal.settingsSchema?.properties?.[field.name])?.default ?? false;
423
- return /* @__PURE__ */ jsxs("div", {
424
- className: "flex items-center justify-between",
425
- children: [/* @__PURE__ */ jsx(Label, {
426
- htmlFor: `setting-${field.name}`,
427
- children: label
428
- }), /* @__PURE__ */ jsx(Switch, {
429
- id: `setting-${field.name}`,
430
- checked,
431
- onCheckedChange: (newValue_0) => {
432
- sessionDirtyRef.current = true;
433
- updateSettings.mutate({
434
- id: block.id,
435
- settings: { [field.name]: newValue_0 }
436
- });
437
- }
438
- })]
439
- }, field.name);
440
- }
441
- return null;
442
- })]
443
- }),
444
- currentItemId != null && !fieldHasOwnView && itemSettingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
445
- className: "border-border space-y-4 border-b px-4 py-4",
446
- children: [/* @__PURE__ */ jsx(Label, {
447
- className: "text-muted-foreground",
448
- children: "Settings"
449
- }), itemSettingsFields.map((field_0) => {
450
- const label_0 = field_0.label ?? formatFieldName(field_0.name);
451
- const itemSettingsValues = currentItem?.settings ?? {};
452
- const itemSettingsSchemaProps = itemArraySchema?.itemSettingsSchema?.properties;
453
- if (field_0.fieldType === "Enum") {
454
- const value_4 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? "";
455
- return /* @__PURE__ */ jsxs("div", {
456
- className: "space-y-2",
457
- children: [/* @__PURE__ */ jsx(Label, {
458
- htmlFor: `item-setting-${field_0.name}`,
459
- children: label_0
460
- }), /* @__PURE__ */ jsxs(Select, {
461
- value: value_4,
462
- onValueChange: (newValue_1) => {
463
- sessionDirtyRef.current = true;
464
- updateRepeatableSettings.mutate({
465
- id: currentItemId,
466
- settings: { [field_0.name]: newValue_1 }
467
- });
468
- },
469
- children: [/* @__PURE__ */ jsx(SelectTrigger, {
452
+ checked,
453
+ onCheckedChange: (newValue_0) => {
454
+ if (!requireDraft()) return;
455
+ sessionDirtyRef.current = true;
456
+ updateSettings.mutate({
457
+ id: block.id,
458
+ settings: { [field.name]: newValue_0 }
459
+ });
460
+ }
461
+ })]
462
+ }, field.name);
463
+ }
464
+ return null;
465
+ })]
466
+ }),
467
+ currentItemId != null && !fieldHasOwnView && itemSettingsFields.length > 0 && /* @__PURE__ */ jsxs("div", {
468
+ className: "border-border space-y-4 border-b px-4 py-4",
469
+ children: [/* @__PURE__ */ jsx(Label, {
470
+ className: "text-muted-foreground",
471
+ children: "Settings"
472
+ }), itemSettingsFields.map((field_0) => {
473
+ const label_0 = field_0.label ?? formatFieldName(field_0.name);
474
+ const itemSettingsValues = currentItem?.settings ?? {};
475
+ const itemSettingsSchemaProps = itemArraySchema?.itemSettingsSchema?.properties;
476
+ if (field_0.fieldType === "Enum") {
477
+ const value_4 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? "";
478
+ return /* @__PURE__ */ jsxs("div", {
479
+ className: "space-y-2",
480
+ children: [/* @__PURE__ */ jsx(Label, {
481
+ htmlFor: `item-setting-${field_0.name}`,
482
+ children: label_0
483
+ }), /* @__PURE__ */ jsxs(Select, {
484
+ value: value_4,
485
+ onValueChange: (newValue_1) => {
486
+ if (!requireDraft()) return;
487
+ sessionDirtyRef.current = true;
488
+ updateRepeatableSettings.mutate({
489
+ id: currentItemId,
490
+ settings: { [field_0.name]: newValue_1 }
491
+ });
492
+ },
493
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
494
+ id: `item-setting-${field_0.name}`,
495
+ children: /* @__PURE__ */ jsx(SelectValue, {})
496
+ }), /* @__PURE__ */ jsx(SelectContent, { children: field_0.enumValues?.map((enumValue_0) => /* @__PURE__ */ jsx(SelectItem, {
497
+ value: enumValue_0,
498
+ children: field_0.enumLabels?.[enumValue_0] ?? enumValue_0
499
+ }, enumValue_0)) })]
500
+ })]
501
+ }, field_0.name);
502
+ }
503
+ if (field_0.fieldType === "Boolean") {
504
+ const checked_0 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? false;
505
+ return /* @__PURE__ */ jsxs("div", {
506
+ className: "flex items-center justify-between",
507
+ children: [/* @__PURE__ */ jsx(Label, {
508
+ htmlFor: `item-setting-${field_0.name}`,
509
+ children: label_0
510
+ }), /* @__PURE__ */ jsx(Switch, {
470
511
  id: `item-setting-${field_0.name}`,
471
- children: /* @__PURE__ */ jsx(SelectValue, {})
472
- }), /* @__PURE__ */ jsx(SelectContent, { children: field_0.enumValues?.map((enumValue_0) => /* @__PURE__ */ jsx(SelectItem, {
473
- value: enumValue_0,
474
- children: field_0.enumLabels?.[enumValue_0] ?? enumValue_0
475
- }, enumValue_0)) })]
476
- })]
477
- }, field_0.name);
478
- }
479
- if (field_0.fieldType === "Boolean") {
480
- const checked_0 = itemSettingsValues[field_0.name] ?? itemSettingsSchemaProps?.[field_0.name]?.default ?? false;
481
- return /* @__PURE__ */ jsxs("div", {
482
- className: "flex items-center justify-between",
483
- children: [/* @__PURE__ */ jsx(Label, {
484
- htmlFor: `item-setting-${field_0.name}`,
485
- children: label_0
486
- }), /* @__PURE__ */ jsx(Switch, {
487
- id: `item-setting-${field_0.name}`,
488
- checked: checked_0,
489
- onCheckedChange: (newValue_2) => {
490
- sessionDirtyRef.current = true;
491
- updateRepeatableSettings.mutate({
492
- id: currentItemId,
493
- settings: { [field_0.name]: newValue_2 }
494
- });
495
- }
496
- })]
497
- }, field_0.name);
498
- }
499
- return null;
500
- })]
501
- }),
502
- isViewingAsset && assetFieldName && isMultipleAsset && /* @__PURE__ */ jsx(MultipleAssetFieldEditor, {
503
- fieldName: assetFieldName,
504
- assetType,
505
- currentData,
506
- onFieldChange: activeFieldChangeHandler
507
- }),
508
- isViewingAsset && assetFieldName && !isMultipleAsset && /* @__PURE__ */ jsx(SingleAssetFieldEditor, {
509
- fieldName: assetFieldName,
510
- assetType,
511
- currentData,
512
- onFieldChange: activeFieldChangeHandler
513
- }),
514
- !isViewingAsset && isViewingLink && linkFieldName && /* @__PURE__ */ jsx("div", {
515
- className: "px-4 py-4",
516
- children: /* @__PURE__ */ jsx(LinkFieldEditor, {
517
- fieldName: linkFieldName,
518
- linkValue: currentData[linkFieldName] ?? {
519
- type: "external",
520
- text: "",
521
- href: "",
522
- newTab: false
523
- },
524
- onSave: (fieldName_1, value_5) => {
525
- activeFieldChangeHandler(fieldName_1, value_5);
526
- }
527
- })
528
- }),
529
- !isViewingAsset && !isViewingLink && (currentItemId == null || currentItem) && /* @__PURE__ */ jsx(ItemFieldsEditor, {
530
- schema: currentSchema,
531
- data: currentData,
532
- blockId: block.id,
533
- itemId: currentItemId ?? void 0,
534
- onFieldChange: activeFieldChangeHandler,
535
- postToIframe,
536
- filesMap,
537
- itemsMap,
538
- fieldIdPrefix,
539
- autoFocusFieldName
540
- }, currentItemId ?? `block-${block.id}`),
541
- !isViewingAsset && !isViewingLink && currentItemId != null && currentItem && /* @__PURE__ */ jsxs("div", {
542
- className: "border-border flex items-center gap-1 border-t px-4 py-4",
543
- children: [
544
- canAddSibling && /* @__PURE__ */ jsxs(Button, {
545
- type: "button",
546
- variant: "ghost",
547
- size: "sm",
548
- className: "text-muted-foreground justify-start",
549
- onClick: () => addSibling({ afterPosition: currentItem.position }),
550
- children: [/* @__PURE__ */ jsx(CirclePlus, { className: "h-4 w-4" }), "Add item"]
551
- }),
552
- canRemoveCurrent && /* @__PURE__ */ jsxs(Button, {
553
- type: "button",
554
- variant: "ghost",
555
- size: "sm",
556
- className: "text-muted-foreground justify-start",
557
- onClick: () => removeCurrent(currentItemId, { onSuccess: () => previewStore.send({ type: "selectParent" }) }),
558
- children: [/* @__PURE__ */ jsx(CircleMinus, { className: "h-4 w-4" }), "Remove item"]
559
- }),
560
- /* @__PURE__ */ jsxs(Button, {
561
- type: "button",
562
- variant: "ghost",
563
- size: "sm",
564
- className: "text-muted-foreground justify-start",
565
- onClick: () => previewStore.send({ type: "selectParent" }),
566
- children: [/* @__PURE__ */ jsx(CornerLeftUp, { className: "h-4 w-4" }), "Select parent"]
512
+ checked: checked_0,
513
+ onCheckedChange: (newValue_2) => {
514
+ if (!requireDraft()) return;
515
+ sessionDirtyRef.current = true;
516
+ updateRepeatableSettings.mutate({
517
+ id: currentItemId,
518
+ settings: { [field_0.name]: newValue_2 }
519
+ });
520
+ }
521
+ })]
522
+ }, field_0.name);
523
+ }
524
+ return null;
525
+ })]
526
+ }),
527
+ isViewingAsset && assetFieldName && isMultipleAsset && /* @__PURE__ */ jsx(MultipleAssetFieldEditor, {
528
+ fieldName: assetFieldName,
529
+ assetType,
530
+ currentData,
531
+ onFieldChange: activeFieldChangeHandler
532
+ }),
533
+ isViewingAsset && assetFieldName && !isMultipleAsset && /* @__PURE__ */ jsx(SingleAssetFieldEditor, {
534
+ fieldName: assetFieldName,
535
+ assetType,
536
+ currentData,
537
+ onFieldChange: activeFieldChangeHandler
538
+ }),
539
+ !isViewingAsset && isViewingLink && linkFieldName && /* @__PURE__ */ jsx("div", {
540
+ className: "px-4 py-4",
541
+ children: /* @__PURE__ */ jsx(LinkFieldEditor, {
542
+ fieldName: linkFieldName,
543
+ linkValue: currentData[linkFieldName] ?? {
544
+ type: "external",
545
+ text: "",
546
+ href: "",
547
+ newTab: false
548
+ },
549
+ onSave: (fieldName_1, value_5) => {
550
+ activeFieldChangeHandler(fieldName_1, value_5);
551
+ }
567
552
  })
568
- ]
569
- })
570
- ] })
553
+ }),
554
+ !isViewingAsset && !isViewingLink && (currentItemId == null || currentItem) && /* @__PURE__ */ jsx(ItemFieldsEditor, {
555
+ schema: currentSchema,
556
+ data: currentData,
557
+ blockId: block.id,
558
+ itemId: currentItemId ?? void 0,
559
+ onFieldChange: activeFieldChangeHandler,
560
+ postToIframe,
561
+ filesMap,
562
+ itemsMap,
563
+ fieldIdPrefix,
564
+ autoFocusFieldName
565
+ }, currentItemId ?? `block-${block.id}`),
566
+ !isViewingAsset && !isViewingLink && currentItemId != null && currentItem && /* @__PURE__ */ jsxs("div", {
567
+ className: "border-border flex items-center gap-1 border-t px-4 py-4",
568
+ children: [
569
+ canAddSibling && /* @__PURE__ */ jsxs(Button, {
570
+ type: "button",
571
+ variant: "ghost",
572
+ size: "sm",
573
+ className: "text-muted-foreground justify-start",
574
+ onClick: () => {
575
+ if (!requireDraft()) return;
576
+ addSibling({ afterPosition: currentItem.position });
577
+ },
578
+ children: [/* @__PURE__ */ jsx(CirclePlus, { className: "h-4 w-4" }), "Add item"]
579
+ }),
580
+ canRemoveCurrent && /* @__PURE__ */ jsxs(Button, {
581
+ type: "button",
582
+ variant: "ghost",
583
+ size: "sm",
584
+ className: "text-muted-foreground justify-start",
585
+ onClick: () => {
586
+ if (!requireDraft()) return;
587
+ removeCurrent(currentItemId, { onSuccess: () => previewStore.send({ type: "selectParent" }) });
588
+ },
589
+ children: [/* @__PURE__ */ jsx(CircleMinus, { className: "h-4 w-4" }), "Remove item"]
590
+ }),
591
+ /* @__PURE__ */ jsxs(Button, {
592
+ type: "button",
593
+ variant: "ghost",
594
+ size: "sm",
595
+ className: "text-muted-foreground justify-start",
596
+ onClick: () => previewStore.send({ type: "selectParent" }),
597
+ children: [/* @__PURE__ */ jsx(CornerLeftUp, { className: "h-4 w-4" }), "Select parent"]
598
+ })
599
+ ]
600
+ })
601
+ ] })
602
+ })]
571
603
  })]
572
604
  });
573
605
  };
@@ -2,6 +2,7 @@ import { previewStore } from "../previewStore.js";
2
2
  import { useProjectSlug } from "../../../lib/auth.js";
3
3
  import { pageMutations, pageQueries, projectQueries } from "../../../lib/queries.js";
4
4
  import { cn, formatPathSegment } from "../../../lib/utils.js";
5
+ import { c } from "react/compiler-runtime";
5
6
  import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
6
7
  import { toast } from "@camox/ui/toaster";
7
8
  import { useMutation, useQuery } from "@tanstack/react-query";
@@ -10,12 +11,86 @@ import { useSelector } from "@xstate/store-react";
10
11
  import * as React from "react";
11
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
13
  import { Button } from "@camox/ui/button";
14
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@camox/ui/tooltip";
13
15
  import { Check, ChevronsUpDown, Pencil, Plus, Trash2 } from "lucide-react";
14
16
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
17
+ import { Badge } from "@camox/ui/badge";
15
18
  import { Skeleton } from "@camox/ui/skeleton";
16
19
  import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "@camox/ui/command";
17
20
 
18
21
  //#region src/features/preview/components/PagePicker.tsx
22
+ const StatusBadge = (t0) => {
23
+ const $ = c(9);
24
+ if ($[0] !== "b1d62d8bb85e72a5793231a5fd9f232264a809f20c92f8c88b773f51f15623ec") {
25
+ for (let $i = 0; $i < 9; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
26
+ $[0] = "b1d62d8bb85e72a5793231a5fd9f232264a809f20c92f8c88b773f51f15623ec";
27
+ }
28
+ const { page } = t0;
29
+ if (page.status === "draft") {
30
+ let t1;
31
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
32
+ t1 = /* @__PURE__ */ jsx(Badge, {
33
+ variant: "outline",
34
+ className: "h-4 shrink-0 px-1.5",
35
+ children: "Draft"
36
+ });
37
+ $[1] = t1;
38
+ } else t1 = $[1];
39
+ return t1;
40
+ }
41
+ if (page.status === "published") {
42
+ let t1;
43
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
44
+ t1 = /* @__PURE__ */ jsx(Badge, {
45
+ variant: "secondary",
46
+ className: "h-4 shrink-0 px-1.5",
47
+ children: "Published"
48
+ });
49
+ $[2] = t1;
50
+ } else t1 = $[2];
51
+ return t1;
52
+ }
53
+ const reason = page.modifiedReason;
54
+ let t1;
55
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
56
+ t1 = /* @__PURE__ */ jsx(Badge, {
57
+ variant: "default",
58
+ className: "h-4 shrink-0 px-1.5",
59
+ children: "Modified"
60
+ });
61
+ $[3] = t1;
62
+ } else t1 = $[3];
63
+ const badge = t1;
64
+ if (reason && (reason.reason === "layout" || reason.reason === "both")) {
65
+ const pluralized = reason.affectedPagesCount === 1 ? "page" : "pages";
66
+ const headline = reason.reason === "layout" ? `Layout ${reason.layoutHandle} has unpublished changes` : `This page and layout ${reason.layoutHandle} both have unpublished changes`;
67
+ let t2;
68
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
69
+ t2 = /* @__PURE__ */ jsx(TooltipTrigger, {
70
+ render: /* @__PURE__ */ jsx("span", { className: "shrink-0" }),
71
+ children: badge
72
+ });
73
+ $[4] = t2;
74
+ } else t2 = $[4];
75
+ let t3;
76
+ if ($[5] !== headline || $[6] !== pluralized || $[7] !== reason.affectedPagesCount) {
77
+ t3 = /* @__PURE__ */ jsxs(Tooltip, { children: [t2, /* @__PURE__ */ jsxs(TooltipContent, { children: [
78
+ headline,
79
+ " (affects ",
80
+ reason.affectedPagesCount,
81
+ " ",
82
+ pluralized,
83
+ ")"
84
+ ] })] });
85
+ $[5] = headline;
86
+ $[6] = pluralized;
87
+ $[7] = reason.affectedPagesCount;
88
+ $[8] = t3;
89
+ } else t3 = $[8];
90
+ return t3;
91
+ }
92
+ return badge;
93
+ };
19
94
  const CREATE_PAGE_VALUE = "__create_page__";
20
95
  const PagePicker = () => {
21
96
  const [open, setOpen] = React.useState(false);
@@ -113,9 +188,12 @@ const PagePicker = () => {
113
188
  children: [/* @__PURE__ */ jsx("p", {
114
189
  className: "truncate",
115
190
  children: page_1.metaTitle ?? formatPathSegment(page_1.pathSegment)
116
- }), /* @__PURE__ */ jsx("p", {
117
- className: "text-muted-foreground truncate font-mono text-xs",
118
- children: page_1.fullPath
191
+ }), /* @__PURE__ */ jsxs("div", {
192
+ className: "flex min-w-0 items-center gap-1.5",
193
+ children: [/* @__PURE__ */ jsx("p", {
194
+ className: "text-muted-foreground truncate font-mono text-xs",
195
+ children: page_1.fullPath
196
+ }), /* @__PURE__ */ jsx(StatusBadge, { page: page_1 })]
119
197
  })]
120
198
  })]
121
199
  }), /* @__PURE__ */ jsxs("div", {