camox 0.42.0 → 0.43.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 (60) hide show
  1. package/dist/core/components/lexical/{GradientNode.js → HighlightNode.js} +34 -34
  2. package/dist/core/components/lexical/InlineLexicalEditor.js +14 -14
  3. package/dist/core/components/lexical/InlineStylesPlugin.js +6 -6
  4. package/dist/core/components/lexical/SelectionBroadcaster.js +5 -5
  5. package/dist/core/components/lexical/SidebarLexicalEditor.js +20 -20
  6. package/dist/core/components/lexical/TextLinkPopover.js +191 -208
  7. package/dist/core/components/lexical/editorConfig.js +2 -2
  8. package/dist/core/createApp.d.ts +3 -2
  9. package/dist/core/createApp.js +2 -1
  10. package/dist/core/createLayout.d.ts +16 -3
  11. package/dist/core/createLayout.js +15 -15
  12. package/dist/core/derivedRoutes.js +1 -1
  13. package/dist/core/dom.d.ts +17 -0
  14. package/dist/core/dom.js +22 -0
  15. package/dist/core/lib/lexicalReact.js +4 -4
  16. package/dist/core/lib/lexicalState.js +3 -3
  17. package/dist/core/lib/modifierFormats.js +1 -1
  18. package/dist/core/lib/textLinks.js +5 -2
  19. package/dist/core/lib/textStyles.d.ts +2 -2
  20. package/dist/core/lib/textStyles.js +6 -11
  21. package/dist/core/pageDestinations.js +37 -0
  22. package/dist/features/og/imageResponse.node.js +11 -9
  23. package/dist/features/preview/CamoxPreview.js +42 -65
  24. package/dist/features/preview/components/AddBlockSidebar.js +1 -1
  25. package/dist/features/preview/components/BlockActionsPopover.js +1 -1
  26. package/dist/features/preview/components/CreatePageModal.js +4 -4
  27. package/dist/features/preview/components/DerivedLayoutSidebar.js +45 -26
  28. package/dist/features/preview/components/DerivedPageInfoSidebar.js +38 -26
  29. package/dist/features/preview/components/FieldToolbar.js +7 -7
  30. package/dist/features/preview/components/LinkFieldEditor.js +212 -209
  31. package/dist/features/preview/components/PageEditorSidebar.js +1 -1
  32. package/dist/features/preview/components/PageInfoSidebar.js +28 -28
  33. package/dist/features/preview/components/PagePicker.js +16 -16
  34. package/dist/features/preview/components/PageTree.js +1 -1
  35. package/dist/features/preview/components/PublicationRow.js +8 -4
  36. package/dist/features/preview/components/useRepeatableItemActions.js +1 -1
  37. package/dist/features/preview/publication.js +10 -0
  38. package/dist/features/provider/CoreCamoxProvider.js +1 -1
  39. package/dist/features/runtime/runtime.js +20 -21
  40. package/dist/features/studio/components/EnvironmentMenu.js +3 -2
  41. package/dist/features/studio/components/Navbar.js +53 -80
  42. package/dist/features/vite/layoutGeneration.js +7 -3
  43. package/dist/features/vite/vite.js +5 -1
  44. package/dist/hooks/use-page-destinations.js +53 -0
  45. package/dist/lib/auth.js +11 -14
  46. package/dist/lib/server-auth-cookie.js +21 -0
  47. package/dist/studio.css +1 -1
  48. package/package.json +8 -4
  49. package/skills/camox/SKILL.md +32 -9
  50. package/skills/camox/references/block-definitions.md +84 -290
  51. package/skills/camox/references/cli-common.md +2 -4
  52. package/skills/camox/references/cli-content.md +147 -0
  53. package/skills/camox/references/cli-environments.md +1 -1
  54. package/skills/camox/references/dom-integrations.md +93 -0
  55. package/skills/camox/references/field-styling.md +50 -0
  56. package/skills/camox/references/layout-definitions.md +33 -147
  57. package/skills/camox/references/routed-layouts.md +71 -0
  58. package/skills/camoxify/SKILL.md +52 -0
  59. package/skills/camox/references/cli-blocks.md +0 -78
  60. package/skills/camox/references/cli-pages.md +0 -53
@@ -1,8 +1,8 @@
1
1
  import { useNavigate } from "../../navigation/navigation.js";
2
2
  import { useProjectSlug } from "../../../lib/auth.js";
3
+ import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
3
4
  import { blockQueries, layoutQueries, pageMutations, pageQueries, projectQueries } from "../../../lib/queries.js";
4
5
  import { cn } from "../../../lib/utils.js";
5
- import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
6
6
  import { areCommentsEnabled } from "../commentsEnabled.js";
7
7
  import { PageLocationFieldset } from "./PageLocationFieldset.js";
8
8
  import { PageNicknameField } from "./PageNicknameField.js";
@@ -33,9 +33,9 @@ import { Switch } from "@camox/ui/switch";
33
33
  //#region src/features/preview/components/PageInfoSidebar.tsx
34
34
  const usePageMetadataData = (pageId) => {
35
35
  const $ = c(32);
36
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
36
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
37
37
  for (let $i = 0; $i < 32; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
38
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
38
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
39
39
  }
40
40
  const projectSlug = useProjectSlug();
41
41
  const camoxApp = useCamoxApp();
@@ -91,7 +91,7 @@ const usePageMetadataData = (pageId) => {
91
91
  const { data: allLayouts } = useQuery(t7);
92
92
  let t8;
93
93
  if ($[15] !== allLayouts || $[16] !== camoxApp) {
94
- t8 = allLayouts?.filter((layout) => camoxApp.getLayoutById(layout.layoutId)?._internal.kind !== "derived");
94
+ t8 = allLayouts?.filter((layout) => camoxApp.getLayoutById(layout.layoutId)?._internal.kind === "curated");
95
95
  $[15] = allLayouts;
96
96
  $[16] = camoxApp;
97
97
  $[17] = t8;
@@ -142,9 +142,9 @@ const usePageMetadataData = (pageId) => {
142
142
  };
143
143
  const PageInfoSidebar = (t0) => {
144
144
  const $ = c(69);
145
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
145
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
146
146
  for (let $i = 0; $i < 69; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
147
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
147
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
148
148
  }
149
149
  const { pageId, scrollable: t1, aboutOnly: t2 } = t0;
150
150
  const scrollable = t1 === void 0 ? true : t1;
@@ -436,9 +436,9 @@ const PageInfoSidebar = (t0) => {
436
436
  };
437
437
  const PageNicknameSidebarEditor = (t0) => {
438
438
  const $ = c(21);
439
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
439
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
440
440
  for (let $i = 0; $i < 21; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
441
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
441
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
442
442
  }
443
443
  const { data } = t0;
444
444
  const { page } = data;
@@ -534,9 +534,9 @@ const PageNicknameSidebarEditor = (t0) => {
534
534
  };
535
535
  const PageLayoutSidebarSelect = (t0) => {
536
536
  const $ = c(29);
537
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
537
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
538
538
  for (let $i = 0; $i < 29; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
539
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
539
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
540
540
  }
541
541
  const { data } = t0;
542
542
  const { page, layouts, camoxApp } = data;
@@ -654,9 +654,9 @@ const PageLayoutSidebarSelect = (t0) => {
654
654
  };
655
655
  const PageStructureModal = (t0) => {
656
656
  const $ = c(10);
657
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
657
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
658
658
  for (let $i = 0; $i < 10; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
659
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
659
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
660
660
  }
661
661
  const { open, onOpenChange, pageId } = t0;
662
662
  const data = usePageMetadataData(pageId);
@@ -821,9 +821,9 @@ const PageStructureEditor = ({ data, includeNickname = true, includeLayout = tru
821
821
  };
822
822
  const PageSeoModal = (t0) => {
823
823
  const $ = c(8);
824
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
824
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
825
825
  for (let $i = 0; $i < 8; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
826
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
826
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
827
827
  }
828
828
  const { open, onOpenChange, pageId } = t0;
829
829
  let t1;
@@ -856,9 +856,9 @@ const PageSeoModal = (t0) => {
856
856
  };
857
857
  const PageSeoContent = (t0) => {
858
858
  const $ = c(6);
859
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
859
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
860
860
  for (let $i = 0; $i < 6; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
861
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
861
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
862
862
  }
863
863
  const { pageId } = t0;
864
864
  const { page, metaTitle, pageLayoutRecord, project } = usePageMetadataData(pageId);
@@ -883,9 +883,9 @@ const PageSeoContent = (t0) => {
883
883
  };
884
884
  const PageSeoEditor = (t0) => {
885
885
  const $ = c(43);
886
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
886
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
887
887
  for (let $i = 0; $i < 43; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
888
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
888
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
889
889
  }
890
890
  const { page, metaTitle, layoutId, projectName } = t0;
891
891
  let t1;
@@ -1053,9 +1053,9 @@ const PageSeoEditor = (t0) => {
1053
1053
  };
1054
1054
  const PageMarkdownModal = (t0) => {
1055
1055
  const $ = c(10);
1056
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
1056
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
1057
1057
  for (let $i = 0; $i < 10; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1058
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
1058
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
1059
1059
  }
1060
1060
  const { open, onOpenChange, pageId, metaTitle, metaDescription } = t0;
1061
1061
  let t1;
@@ -1094,9 +1094,9 @@ const PageMarkdownModal = (t0) => {
1094
1094
  };
1095
1095
  const PageMarkdownContent = (t0) => {
1096
1096
  const $ = c(6);
1097
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
1097
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
1098
1098
  for (let $i = 0; $i < 6; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1099
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
1099
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
1100
1100
  }
1101
1101
  const { pageId } = t0;
1102
1102
  const { page, metaTitle } = usePageMetadataData(pageId);
@@ -1129,9 +1129,9 @@ function truncateText(text, maxLen) {
1129
1129
  }
1130
1130
  const SearchEnginePreview = (t0) => {
1131
1131
  const $ = c(17);
1132
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
1132
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
1133
1133
  for (let $i = 0; $i < 17; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1134
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
1134
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
1135
1135
  }
1136
1136
  const { page, metaTitle, metaDescription } = t0;
1137
1137
  const url = `${typeof window !== "undefined" ? window.location.origin : ""}${page.fullPath}`;
@@ -1214,9 +1214,9 @@ const SearchEnginePreview = (t0) => {
1214
1214
  };
1215
1215
  const SocialPreviewSection = (t0) => {
1216
1216
  const $ = c(60);
1217
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
1217
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
1218
1218
  for (let $i = 0; $i < 60; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1219
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
1219
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
1220
1220
  }
1221
1221
  const { page, metaTitle, metaDescription, layoutId, projectName } = t0;
1222
1222
  const pageMetaTitle = page.metaTitle ?? page.pathSegment;
@@ -1488,9 +1488,9 @@ const SocialPreviewSection = (t0) => {
1488
1488
  };
1489
1489
  const PageMarkdownPreview = (t0) => {
1490
1490
  const $ = c(9);
1491
- if ($[0] !== "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639") {
1491
+ if ($[0] !== "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4") {
1492
1492
  for (let $i = 0; $i < 9; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
1493
- $[0] = "f14881fbdeca138d8eb32185f2d3259957dcb91e0de821aeac5cc1ede49b5639";
1493
+ $[0] = "dedb426819c8cdba3ad2706e62e75231e53c7e782fd82aca9849a8543054a0e4";
1494
1494
  }
1495
1495
  const { pageId, metaTitle, metaDescription } = t0;
1496
1496
  let t1;
@@ -1,8 +1,8 @@
1
1
  import { useLocation, useNavigate } from "../../navigation/navigation.js";
2
2
  import { useProjectSlug } from "../../../lib/auth.js";
3
+ import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
3
4
  import { layoutQueries, pageMutations, pageQueries, projectQueries } from "../../../lib/queries.js";
4
5
  import { cn } from "../../../lib/utils.js";
5
- import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
6
6
  import { previewStore } from "../previewStore.js";
7
7
  import { matchDerivedLayout, routeSegments } from "../../../core/derivedRoutes.js";
8
8
  import { DerivedLayoutDialog } from "./DerivedLayoutDialog.js";
@@ -23,7 +23,7 @@ const CREATE_PAGE_VALUE = "__create_page__";
23
23
  const DERIVED_LAYOUT_PREFIX = "__derived_layout__:";
24
24
  const PagePicker = () => {
25
25
  const [open, setOpen] = React.useState(false);
26
- const derivedLayouts = useCamoxApp().getLayouts().filter((layout) => layout._internal.kind === "derived");
26
+ const derivedLayouts = useCamoxApp().getLayouts().filter((layout) => layout._internal.kind !== "curated");
27
27
  const [layoutToPreview, setLayoutToPreview] = React.useState(null);
28
28
  const [pageToDelete, setPageToDelete] = React.useState(null);
29
29
  const projectSlug = useProjectSlug();
@@ -183,42 +183,42 @@ const PagePicker = () => {
183
183
  })]
184
184
  })]
185
185
  }, page_1.id)) }),
186
- derivedLayouts.length > 0 && /* @__PURE__ */ jsx(CommandGroup, {
187
- heading: "Derived pages",
188
- children: derivedLayouts.map((layout_1) => /* @__PURE__ */ jsx(CommandItem, {
189
- value: `${DERIVED_LAYOUT_PREFIX}${layout_1._internal.id}`,
190
- keywords: [layout_1._internal.title],
186
+ ["singleton", "derived"].map((kind) => /* @__PURE__ */ jsx(CommandGroup, {
187
+ heading: kind === "singleton" ? "Singleton pages" : "Derived pages",
188
+ children: derivedLayouts.filter((layout_1) => layout_1._internal.kind === kind).map((layout_2) => /* @__PURE__ */ jsx(CommandItem, {
189
+ value: `${DERIVED_LAYOUT_PREFIX}${layout_2._internal.id}`,
190
+ keywords: [layout_2._internal.title],
191
191
  hideCheck: true,
192
192
  onSelect: () => {
193
193
  closePopover();
194
- const segments = routeSegments(layout_1._internal.id);
194
+ const segments = routeSegments(layout_2._internal.id);
195
195
  if (!segments.some((segment) => segment.startsWith("$"))) {
196
196
  navigate({ to: `/${segments.map(encodeURIComponent).join("/")}` });
197
197
  return;
198
198
  }
199
- setLayoutToPreview(layout_1);
199
+ setLayoutToPreview(layout_2);
200
200
  },
201
201
  children: /* @__PURE__ */ jsxs("div", {
202
202
  className: "flex min-w-0 flex-1 items-start gap-2",
203
- children: [/* @__PURE__ */ jsx(Check, { className: cn("size-4 mt-0.5 shrink-0", currentDerived?.layout._internal.id !== layout_1._internal.id && "invisible") }), /* @__PURE__ */ jsxs("div", {
203
+ children: [/* @__PURE__ */ jsx(Check, { className: cn("size-4 mt-0.5 shrink-0", currentDerived?.layout._internal.id !== layout_2._internal.id && "invisible") }), /* @__PURE__ */ jsxs("div", {
204
204
  className: "flex min-w-0 flex-col",
205
205
  children: [/* @__PURE__ */ jsxs("div", {
206
206
  className: "flex min-w-0 items-center gap-1.5",
207
207
  children: [/* @__PURE__ */ jsx("p", {
208
208
  className: "truncate",
209
- children: layout_1._internal.title
210
- }), layoutStatusById.has(layout_1._internal.id) && /* @__PURE__ */ jsx(PageStatusBadge, {
209
+ children: layout_2._internal.title
210
+ }), layoutStatusById.has(layout_2._internal.id) && /* @__PURE__ */ jsx(PageStatusBadge, {
211
211
  size: "sm",
212
- status: layoutStatusById.get(layout_1._internal.id)
212
+ status: layoutStatusById.get(layout_2._internal.id)
213
213
  })]
214
214
  }), /* @__PURE__ */ jsxs("p", {
215
215
  className: "text-muted-foreground truncate font-mono text-xs",
216
- children: ["/", routeSegments(layout_1._internal.id).join("/")]
216
+ children: ["/", routeSegments(layout_2._internal.id).join("/")]
217
217
  })]
218
218
  })]
219
219
  })
220
- }, layout_1._internal.id))
221
- })
220
+ }, layout_2._internal.id))
221
+ }, kind))
222
222
  ]
223
223
  }),
224
224
  /* @__PURE__ */ jsx(CommandSeparator, {}),
@@ -1,6 +1,6 @@
1
1
  import { usePageBlocks } from "../../../lib/normalized-data.js";
2
- import { cn } from "../../../lib/utils.js";
3
2
  import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
3
+ import { cn } from "../../../lib/utils.js";
4
4
  import { previewStore, selectPreviewSource } from "../previewStore.js";
5
5
  import { useRequireDraftSource } from "../../../core/hooks/useRequireDraftSource.js";
6
6
  import { useUpdateBlockPosition } from "./useUpdateBlockPosition.js";
@@ -19,9 +19,9 @@ import { ButtonGroup } from "@camox/ui/button-group";
19
19
  //#region src/features/preview/components/PublicationRow.tsx
20
20
  function PublicationRow(t0) {
21
21
  const $ = c(4);
22
- if ($[0] !== "523b5f62d51c452de4fc84846c3eb3bd79ec8b996291e394294cbc9a83e20848") {
22
+ if ($[0] !== "e9c63697d20e2e70a2c2f753bfe924624780cd53f172ee49c5b02a56c0ac0967") {
23
23
  for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
24
- $[0] = "523b5f62d51c452de4fc84846c3eb3bd79ec8b996291e394294cbc9a83e20848";
24
+ $[0] = "e9c63697d20e2e70a2c2f753bfe924624780cd53f172ee49c5b02a56c0ac0967";
25
25
  }
26
26
  const { target } = t0;
27
27
  const identity = target ? `${target.kind}:${target.kind === "page" ? target.page.id : target.layout.id}` : "loading";
@@ -36,9 +36,9 @@ function PublicationRow(t0) {
36
36
  }
37
37
  function PublicationControls(t0) {
38
38
  const $ = c(78);
39
- if ($[0] !== "523b5f62d51c452de4fc84846c3eb3bd79ec8b996291e394294cbc9a83e20848") {
39
+ if ($[0] !== "e9c63697d20e2e70a2c2f753bfe924624780cd53f172ee49c5b02a56c0ac0967") {
40
40
  for (let $i = 0; $i < 78; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
41
- $[0] = "523b5f62d51c452de4fc84846c3eb3bd79ec8b996291e394294cbc9a83e20848";
41
+ $[0] = "e9c63697d20e2e70a2c2f753bfe924624780cd53f172ee49c5b02a56c0ac0967";
42
42
  }
43
43
  const { target } = t0;
44
44
  const source = useSelector(previewStore, selectPreviewSource);
@@ -309,6 +309,10 @@ function PublicationControls(t0) {
309
309
  "The draft for ",
310
310
  target?.kind === "page" && target.page.fullPath,
311
311
  " will be reset to its published version. Shared layout changes are not discarded. This does not change what visitors see."
312
+ ] }) : target?.kind === "layout" && target.layout.kind === "singleton" ? /* @__PURE__ */ jsxs(Fragment, { children: [
313
+ "This removes the published editable blocks from ",
314
+ target.name,
315
+ ". The code-owned page, its URL, application content, and draft blocks stay available."
312
316
  ] }) : target?.kind === "layout" ? /* @__PURE__ */ jsxs(Fragment, { children: [
313
317
  "This removes the shared before and after blocks from every live page using",
314
318
  " ",
@@ -1,5 +1,5 @@
1
- import { repeatableItemMutations } from "../../../lib/queries.js";
2
1
  import "../../provider/components/CamoxAppContext.js";
2
+ import { repeatableItemMutations } from "../../../lib/queries.js";
3
3
  import { previewStore } from "../previewStore.js";
4
4
  import { useRequireDraftSource } from "../../../core/hooks/useRequireDraftSource.js";
5
5
  import { c } from "react/compiler-runtime";
@@ -5,6 +5,16 @@
5
5
  * not a separate publishing UI or a sequence of independent block requests.
6
6
  */
7
7
  function buildPublicationPlan(target) {
8
+ if (target.kind === "layout" && target.layout.kind === "singleton") return {
9
+ title: "Publish page content",
10
+ description: `Publishes the editable blocks on ${target.name}. The route and application data are controlled by code, not publishing.`,
11
+ items: [{
12
+ key: `layout:${target.layout.id}`,
13
+ label: `${target.name} blocks`,
14
+ impact: "Updates this page’s before and after blocks. Synced block changes also apply wherever those blocks are used.",
15
+ optional: false
16
+ }]
17
+ };
8
18
  if (target.kind === "layout") return {
9
19
  title: target.layout.status === "modified" ? "Publish changes" : "Publish layout",
10
20
  description: `Publishes ${target.name} on every page using this layout. Generated content and external data are not published by this action.`,
@@ -1,7 +1,7 @@
1
1
  import { AuthContext, createCamoxAuthClient, useProcessOtt } from "../../lib/auth.js";
2
2
  import { initApiClient } from "../../lib/api-client.js";
3
- import { projectQueries } from "../../lib/queries.js";
4
3
  import { CamoxAppProvider } from "./components/CamoxAppContext.js";
4
+ import { projectQueries } from "../../lib/queries.js";
5
5
  import { useQueryClient } from "@tanstack/react-query";
6
6
  import * as React from "react";
7
7
  import { jsx } from "react/jsx-runtime";
@@ -1,3 +1,5 @@
1
+ import { buildClearServerAuthCookieHeader, getServerAuthCookieHeader } from "../../lib/server-auth-cookie.js";
2
+ import { getPageDestinations } from "../../core/pageDestinations.js";
1
3
  import { PLATFORM_SCRIPT } from "../../lib/platform.js";
2
4
  import { STUDIO_THEME_SCRIPT } from "../studio/studioTheme.js";
3
5
  import { matchDerivedLayout } from "../../core/derivedRoutes.js";
@@ -9,20 +11,6 @@ import { createHead, renderSSRHead } from "@unhead/react/server";
9
11
  const DEFAULT_RUNTIME_BASE_PATH = "";
10
12
  const RUNTIME_HEALTH_PATH = "/_camox/health";
11
13
  const RUNTIME_REGISTRY_PATH = "/_camox/registry";
12
- const SERVER_AUTH_COOKIE_NAME = "camox_auth_cookie";
13
- function buildClearServerAuthCookieHeader() {
14
- return `${SERVER_AUTH_COOKIE_NAME}=; Path=/; SameSite=Lax; Max-Age=0`;
15
- }
16
- function getServerAuthCookieHeader(headers) {
17
- const authCookie = (headers.get("Cookie") ?? headers.get("cookie") ?? "").split(";").map((part) => part.trim()).find((part) => part.startsWith(`${SERVER_AUTH_COOKIE_NAME}=`));
18
- if (!authCookie) return "";
19
- const value = authCookie.slice(18);
20
- try {
21
- return decodeURIComponent(value);
22
- } catch {
23
- return "";
24
- }
25
- }
26
14
  function normalizeRuntimeBasePath(basePath) {
27
15
  if (basePath === void 0) return DEFAULT_RUNTIME_BASE_PATH;
28
16
  if (!basePath || basePath === "/") return "";
@@ -101,10 +89,16 @@ async function createSitemapResponse(request, options) {
101
89
  if (!options.apiUrl || !options.projectSlug) return Response.json({ message: "Camox sitemap is not configured." }, { status: 500 });
102
90
  const api = createServerApiClient(options.apiUrl, options.environmentName);
103
91
  const origin = new URL(request.url).origin;
104
- const entries = (await api.pages.listBySlug({ projectSlug: options.projectSlug })).map((page) => ` <url>
105
- <loc>${escapeXml(`${origin}${withRuntimeBasePath(page.fullPath, options.runtimeBasePath)}`)}</loc>
106
- <lastmod>${escapeXml(new Date(page.updatedAt).toISOString())}</lastmod>
107
- </url>`).join("\n");
92
+ const pages = await api.pages.listBySlug({ projectSlug: options.projectSlug });
93
+ const destinations = getPageDestinations(pages, (await options.getCamoxApp?.())?.getLayouts() ?? []);
94
+ const updatedAtById = new Map(pages.map((page) => [page.id, page.updatedAt]));
95
+ const entries = destinations.map((page) => {
96
+ const updatedAt = page.kind === "curated" ? updatedAtById.get(page.pageId) : void 0;
97
+ const lastmod = updatedAt == null ? "" : `\n <lastmod>${escapeXml(new Date(updatedAt).toISOString())}</lastmod>`;
98
+ return ` <url>
99
+ <loc>${escapeXml(`${origin}${withRuntimeBasePath(page.fullPath, options.runtimeBasePath)}`)}</loc>${lastmod}
100
+ </url>`;
101
+ }).join("\n");
108
102
  return new Response(`<?xml version="1.0" encoding="UTF-8"?>
109
103
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
110
104
  ${entries}
@@ -214,6 +208,8 @@ async function createPageHtmlResponse({ options, pathname, request }) {
214
208
  pathname,
215
209
  request
216
210
  });
211
+ const singleton = await createDerivedResponse(options, pathname, request, false, true);
212
+ if (singleton) return singleton;
217
213
  const queryClient = new QueryClient();
218
214
  try {
219
215
  const result = await loadCamoxPageForRequest({
@@ -277,13 +273,14 @@ ${renderClientEntryScript(options.pageClientEntryUrl)}
277
273
  throw error;
278
274
  }
279
275
  }
280
- async function createDerivedResponse(options, pathname, request, dataOnly = false) {
276
+ async function createDerivedResponse(options, pathname, request, dataOnly = false, singletonOnly = false) {
281
277
  const app = await options.getCamoxApp?.();
282
278
  const match = app && matchDerivedLayout(app.getLayouts(), pathname);
283
279
  if (!match || !dataOnly && !options.renderPage) return null;
280
+ if (singletonOnly && match.layout._internal.kind !== "singleton") return null;
284
281
  try {
285
- const data = await match.layout._internal.loader({ params: match.params });
286
- const serializedData = JSON.parse(JSON.stringify(data));
282
+ const data = await match.layout._internal.loader?.({ params: match.params });
283
+ const serializedData = data === void 0 ? void 0 : JSON.parse(JSON.stringify(data));
287
284
  const queryClient = new QueryClient();
288
285
  const authCookieHeader = getServerAuthCookieHeader(request.headers);
289
286
  let source = authCookieHeader ? "draft" : "live";
@@ -404,6 +401,8 @@ ${renderClientEntryScript(options.studioClientEntryUrl)}
404
401
  }
405
402
  async function createPageDataResponse({ options, pathname, request }) {
406
403
  if (!options.apiUrl || !options.projectSlug) return Response.json({ message: "Camox page loader is not configured." }, { status: 500 });
404
+ const singleton = await createDerivedResponse(options, pathname, request, true, true);
405
+ if (singleton) return singleton;
407
406
  const queryClient = new QueryClient();
408
407
  try {
409
408
  const result = await loadCamoxPageForRequest({
@@ -30,15 +30,16 @@ function formatReason(reason, direction) {
30
30
  case "block-definition-missing-in-source": return `Block type "${reason.blockId}" exists in ${targetLabel} but not in ${sourceLabel}.`;
31
31
  case "block-definition-missing-in-target": return `Block type "${reason.blockId}" exists in ${sourceLabel} but not in ${targetLabel}. ${remedy}`;
32
32
  case "block-definition-schema-mismatch": return `Block type "${reason.blockId}" has a different "${reason.field}" between envs. Sync block definitions and try again.`;
33
+ case "layout-kind-mismatch": return `Layout "${reason.layoutId}" has a different kind between environments. Sync layout definitions and try again.`;
33
34
  case "layout-missing-in-source": return `Layout "${reason.layoutId}" exists in ${targetLabel} but not in ${sourceLabel}.`;
34
35
  case "layout-missing-in-target": return `Layout "${reason.layoutId}" exists in ${sourceLabel} but not in ${targetLabel}. ${remedy}`;
35
36
  }
36
37
  }
37
38
  const EnvironmentMenu = () => {
38
39
  const $ = c(84);
39
- if ($[0] !== "42128d7498b871de7c9ecaaeb2e1b0e858fc9e8ce0ed44b99ccfaf471a4c55ca") {
40
+ if ($[0] !== "5eec2ee2385ce7d4417564ed3e8ad71e1d521ff3c557dea4a4ca42288fb12d99") {
40
41
  for (let $i = 0; $i < 84; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
41
- $[0] = "42128d7498b871de7c9ecaaeb2e1b0e858fc9e8ce0ed44b99ccfaf471a4c55ca";
42
+ $[0] = "5eec2ee2385ce7d4417564ed3e8ad71e1d521ff3c557dea4a4ca42288fb12d99";
42
43
  }
43
44
  const [open, setOpen] = React.useState(false);
44
45
  const [pendingDirection, setPendingDirection] = React.useState(null);
@@ -1,7 +1,6 @@
1
1
  import { Link as Link$1, useLocation, useNavigate } from "../../navigation/navigation.js";
2
2
  import { actionsStore } from "../../provider/actionsStore.js";
3
- import { useProjectSlug } from "../../../lib/auth.js";
4
- import { pageQueries, projectQueries } from "../../../lib/queries.js";
3
+ import { usePageDestinations } from "../../../hooks/use-page-destinations.js";
5
4
  import { PlatformLabel } from "../../../components/PlatformLabel.js";
6
5
  import { cn } from "../../../lib/utils.js";
7
6
  import { STUDIO_CONTENT_PATH } from "../routes.js";
@@ -10,7 +9,6 @@ import { EnvironmentMenu } from "./EnvironmentMenu.js";
10
9
  import { ProjectMenu } from "./ProjectMenu.js";
11
10
  import { UserButton } from "./UserButton.js";
12
11
  import { c } from "react/compiler-runtime";
13
- import { useQuery } from "@tanstack/react-query";
14
12
  import * as React from "react";
15
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
14
  import { Kbd } from "@camox/ui/kbd";
@@ -36,97 +34,72 @@ const links = [{
36
34
  ]
37
35
  }];
38
36
  const Navbar = (t0) => {
39
- const $ = c(20);
40
- if ($[0] !== "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d") {
41
- for (let $i = 0; $i < 20; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
42
- $[0] = "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d";
37
+ const $ = c(13);
38
+ if ($[0] !== "9182f23e40dc6d439cec2c533b4f9436ee59f885213d6d7a79d434c7aa8349c6") {
39
+ for (let $i = 0; $i < 13; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
40
+ $[0] = "9182f23e40dc6d439cec2c533b4f9436ee59f885213d6d7a79d434c7aa8349c6";
43
41
  }
44
42
  const { isPreview: t1 } = t0;
45
43
  const isPreview = t1 === void 0 ? false : t1;
46
- const projectSlug = useProjectSlug();
47
- let t2;
48
- if ($[1] !== projectSlug) {
49
- t2 = projectQueries.getBySlug(projectSlug);
50
- $[1] = projectSlug;
51
- $[2] = t2;
52
- } else t2 = $[2];
53
- const { data: project } = useQuery(t2);
54
- let t3;
55
- if ($[3] !== project?.id) {
56
- t3 = pageQueries.list(project?.id ?? 0);
57
- $[3] = project?.id;
58
- $[4] = t3;
59
- } else t3 = $[4];
60
- const t4 = !!project;
61
- let t5;
62
- if ($[5] !== t3 || $[6] !== t4) {
63
- t5 = {
64
- ...t3,
65
- enabled: t4
66
- };
67
- $[5] = t3;
68
- $[6] = t4;
69
- $[7] = t5;
70
- } else t5 = $[7];
71
- const { data: pages } = useQuery(t5);
44
+ const pages = usePageDestinations();
72
45
  const { pathname } = useLocation();
73
- let t6;
74
- if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
75
- t6 = /* @__PURE__ */ jsxs("div", {
46
+ let t2;
47
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
48
+ t2 = /* @__PURE__ */ jsxs("div", {
76
49
  className: "flew-row flex gap-1",
77
50
  children: [/* @__PURE__ */ jsx(ProjectMenu, {}), /* @__PURE__ */ jsx(EnvironmentMenu, {})]
78
51
  });
79
- $[8] = t6;
80
- } else t6 = $[8];
81
- let t7;
82
- if ($[9] !== isPreview || $[10] !== pages || $[11] !== pathname) {
83
- t7 = links.map((link, index) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link$1, {
52
+ $[1] = t2;
53
+ } else t2 = $[1];
54
+ let t3;
55
+ if ($[2] !== isPreview || $[3] !== pages || $[4] !== pathname) {
56
+ t3 = links.map((link, index) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link$1, {
84
57
  to: link.to,
85
58
  className: cn("flex gap-2 items-center rounded-md px-4 py-2 text-sm font-medium", "hover:bg-accent hover:text-accent-foreground outline-none transition-[color,box-shadow] focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-1", (isPreview || pages?.some((page) => page.fullPath === pathname)) && index === 0 ? "bg-accent hover:bg-accent text-accent-foreground" : "text-muted-foreground"),
86
59
  activeProps: { className: "bg-accent hover:bg-accent text-accent-foreground!" },
87
60
  children: link.children
88
61
  }) }, link.to));
89
- $[9] = isPreview;
90
- $[10] = pages;
91
- $[11] = pathname;
92
- $[12] = t7;
93
- } else t7 = $[12];
94
- let t8;
95
- if ($[13] !== t7) {
96
- t8 = /* @__PURE__ */ jsxs("div", {
62
+ $[2] = isPreview;
63
+ $[3] = pages;
64
+ $[4] = pathname;
65
+ $[5] = t3;
66
+ } else t3 = $[5];
67
+ let t4;
68
+ if ($[6] !== t3) {
69
+ t4 = /* @__PURE__ */ jsxs("div", {
97
70
  className: "flex flex-row gap-2",
98
- children: [t6, /* @__PURE__ */ jsx("ul", {
71
+ children: [t2, /* @__PURE__ */ jsx("ul", {
99
72
  className: "flex items-center gap-1",
100
- children: t7
73
+ children: t3
101
74
  })]
102
75
  });
103
- $[13] = t7;
104
- $[14] = t8;
105
- } else t8 = $[14];
106
- let t10;
107
- let t9;
108
- if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
109
- t9 = /* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4" });
110
- t10 = /* @__PURE__ */ jsx("span", {
76
+ $[6] = t3;
77
+ $[7] = t4;
78
+ } else t4 = $[7];
79
+ let t5;
80
+ let t6;
81
+ if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
82
+ t5 = /* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4" });
83
+ t6 = /* @__PURE__ */ jsx("span", {
111
84
  className: "text-muted-foreground",
112
85
  children: "Quick find"
113
86
  });
114
- $[15] = t10;
115
- $[16] = t9;
87
+ $[8] = t5;
88
+ $[9] = t6;
116
89
  } else {
117
- t10 = $[15];
118
- t9 = $[16];
90
+ t5 = $[8];
91
+ t6 = $[9];
119
92
  }
120
- let t11;
121
- if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
122
- t11 = /* @__PURE__ */ jsxs("div", {
93
+ let t7;
94
+ if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
95
+ t7 = /* @__PURE__ */ jsxs("div", {
123
96
  className: "flex items-center gap-2",
124
97
  children: [/* @__PURE__ */ jsxs(Button, {
125
98
  variant: "outline",
126
99
  onClick: _temp,
127
100
  children: [
128
- t9,
129
- t10,
101
+ t5,
102
+ t6,
130
103
  /* @__PURE__ */ jsx(Kbd, {
131
104
  className: "ml-4",
132
105
  children: /* @__PURE__ */ jsx(PlatformLabel, {
@@ -137,24 +110,24 @@ const Navbar = (t0) => {
137
110
  ]
138
111
  }), /* @__PURE__ */ jsx(UserButton, {})]
139
112
  });
140
- $[17] = t11;
141
- } else t11 = $[17];
142
- let t12;
143
- if ($[18] !== t8) {
144
- t12 = /* @__PURE__ */ jsxs("nav", {
113
+ $[10] = t7;
114
+ } else t7 = $[10];
115
+ let t8;
116
+ if ($[11] !== t4) {
117
+ t8 = /* @__PURE__ */ jsxs("nav", {
145
118
  className: "bg-background relative flex items-center justify-between gap-4 border-b-2 px-2 py-2",
146
- children: [t8, t11]
119
+ children: [t4, t7]
147
120
  });
148
- $[18] = t8;
149
- $[19] = t12;
150
- } else t12 = $[19];
151
- return t12;
121
+ $[11] = t4;
122
+ $[12] = t8;
123
+ } else t8 = $[12];
124
+ return t8;
152
125
  };
153
126
  function useNavbarActions() {
154
127
  const $ = c(4);
155
- if ($[0] !== "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d") {
128
+ if ($[0] !== "9182f23e40dc6d439cec2c533b4f9436ee59f885213d6d7a79d434c7aa8349c6") {
156
129
  for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
157
- $[0] = "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d";
130
+ $[0] = "9182f23e40dc6d439cec2c533b4f9436ee59f885213d6d7a79d434c7aa8349c6";
158
131
  }
159
132
  const navigate = useNavigate();
160
133
  let t0;