camox 0.42.0 → 0.44.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 (99) hide show
  1. package/bin/camox.mjs +9 -1
  2. package/dist/core/components/lexical/{GradientNode.js → HighlightNode.js} +34 -34
  3. package/dist/core/components/lexical/InlineLexicalEditor.js +14 -14
  4. package/dist/core/components/lexical/InlineStylesPlugin.js +25 -9
  5. package/dist/core/components/lexical/SelectionBroadcaster.js +7 -7
  6. package/dist/core/components/lexical/SidebarLexicalEditor.js +24 -24
  7. package/dist/core/components/lexical/TextLinkPopover.js +191 -208
  8. package/dist/core/components/lexical/editorConfig.js +2 -2
  9. package/dist/core/createApp.d.ts +31 -4
  10. package/dist/core/createApp.js +2 -1
  11. package/dist/core/createBlock.js +24 -10
  12. package/dist/core/createLayout.d.ts +16 -3
  13. package/dist/core/createLayout.js +15 -15
  14. package/dist/core/derivedRoutes.js +1 -1
  15. package/dist/core/dom.d.ts +17 -0
  16. package/dist/core/dom.js +22 -0
  17. package/dist/core/editing/createEditableBlock.d.ts +135 -1
  18. package/dist/core/editing/createEditableBlock.js +209 -74
  19. package/dist/core/lib/contentType.d.ts +5 -0
  20. package/dist/core/lib/contentType.js +11 -0
  21. package/dist/core/lib/fieldTypes.js +1 -0
  22. package/dist/core/lib/iconTypes.d.ts +13 -0
  23. package/dist/core/lib/icons.d.ts +6 -0
  24. package/dist/core/lib/icons.js +130 -0
  25. package/dist/core/lib/lexicalReact.js +4 -4
  26. package/dist/core/lib/lexicalState.js +8 -8
  27. package/dist/core/lib/modifierFormats.js +2 -2
  28. package/dist/core/lib/textLinks.js +5 -2
  29. package/dist/core/lib/textStyles.d.ts +3 -3
  30. package/dist/core/lib/textStyles.js +9 -14
  31. package/dist/core/pageDestinations.js +37 -0
  32. package/dist/features/content/components/AssetCard.js +104 -68
  33. package/dist/features/og/imageResponse.node.js +11 -9
  34. package/dist/features/preview/CamoxPreview.js +148 -146
  35. package/dist/features/preview/commentsEnabled.js +2 -2
  36. package/dist/features/preview/components/AddBlockSidebar.js +1 -1
  37. package/dist/features/preview/components/AssetLightbox.js +17 -6
  38. package/dist/features/preview/components/AttachedComments.js +224 -87
  39. package/dist/features/preview/components/BlockActionsPopover.js +1 -1
  40. package/dist/features/preview/components/CommentTargetQuote.js +22 -21
  41. package/dist/features/preview/components/CreatePageModal.js +4 -4
  42. package/dist/features/preview/components/DerivedLayoutDialog.js +36 -21
  43. package/dist/features/preview/components/DerivedLayoutSidebar.js +45 -26
  44. package/dist/features/preview/components/DerivedPageInfoSidebar.js +38 -26
  45. package/dist/features/preview/components/FieldOverlayStyles.js +29 -9
  46. package/dist/features/preview/components/FieldToolbar.js +12 -12
  47. package/dist/features/preview/components/IconFieldEditor.js +231 -0
  48. package/dist/features/preview/components/ItemFieldsEditor.js +21 -14
  49. package/dist/features/preview/components/LinkFieldEditor.js +212 -209
  50. package/dist/features/preview/components/OverlayTracker.js +1 -1
  51. package/dist/features/preview/components/PageEditorSidebar.js +1 -1
  52. package/dist/features/preview/components/PageInfoSidebar.js +28 -28
  53. package/dist/features/preview/components/PagePicker.js +46 -46
  54. package/dist/features/preview/components/PageTree.js +1 -1
  55. package/dist/features/preview/components/PreviewPanel.js +1 -1
  56. package/dist/features/preview/components/PreviewToolbar.js +224 -225
  57. package/dist/features/preview/components/PublicationRow.js +8 -4
  58. package/dist/features/preview/components/SendFeedbackDialog.js +83 -0
  59. package/dist/features/preview/components/useRepeatableItemActions.js +1 -1
  60. package/dist/features/preview/overlayHighlights.js +54 -0
  61. package/dist/features/preview/previewCommentsStore.js +54 -34
  62. package/dist/features/preview/previewSelection.js +28 -23
  63. package/dist/features/preview/publication.js +10 -0
  64. package/dist/features/preview/usePageComments.js +39 -0
  65. package/dist/features/provider/CoreCamoxProvider.js +38 -6
  66. package/dist/features/provider/LocalhostPreviewProvider.js +81 -45
  67. package/dist/features/provider/components/CommandPalette.js +35 -84
  68. package/dist/features/runtime/pageApp.js +32 -28
  69. package/dist/features/runtime/pageServer.js +4 -1
  70. package/dist/features/runtime/runtime.d.ts +2 -0
  71. package/dist/features/runtime/runtime.js +45 -21
  72. package/dist/features/studio/components/EnvironmentMenu.js +76 -63
  73. package/dist/features/studio/components/Navbar.js +53 -80
  74. package/dist/features/vite/auth.js +1 -50
  75. package/dist/features/vite/iconGeneration.js +16 -0
  76. package/dist/features/vite/layoutGeneration.js +7 -3
  77. package/dist/features/vite/vite.d.ts +4 -2
  78. package/dist/features/vite/vite.js +24 -3
  79. package/dist/hooks/use-page-destinations.js +53 -0
  80. package/dist/lib/auth.js +40 -20
  81. package/dist/lib/preview-handoff.js +13 -0
  82. package/dist/lib/queries.js +12 -1
  83. package/dist/lib/server-auth-cookie.js +21 -0
  84. package/dist/lib/use-project-room.js +5 -2
  85. package/dist/studio-overlays.css +19 -15
  86. package/dist/studio.css +1 -1
  87. package/package.json +9 -4
  88. package/skills/camox/SKILL.md +38 -9
  89. package/skills/camox/references/block-definitions.md +107 -286
  90. package/skills/camox/references/cli-common.md +16 -4
  91. package/skills/camox/references/cli-content.md +158 -0
  92. package/skills/camox/references/cli-environments.md +1 -1
  93. package/skills/camox/references/dom-integrations.md +93 -0
  94. package/skills/camox/references/field-styling.md +50 -0
  95. package/skills/camox/references/layout-definitions.md +33 -147
  96. package/skills/camox/references/routed-layouts.md +71 -0
  97. package/skills/camoxify/SKILL.md +66 -0
  98. package/skills/camox/references/cli-blocks.md +0 -78
  99. package/skills/camox/references/cli-pages.md +0 -53
@@ -5,15 +5,14 @@ import { c } from "react/compiler-runtime";
5
5
  import * as React from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { useSelector } from "@xstate/store-react";
8
- import { Kbd } from "@camox/ui/kbd";
9
8
  import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandShortcut } from "@camox/ui/command";
10
9
 
11
10
  //#region src/features/provider/components/CommandPalette.tsx
12
11
  function CommandPalette() {
13
- const $ = c(43);
14
- if ($[0] !== "911725dec1e42161265ff27436f9ae9c6c82392d2b8fce53f3eb80dfb29cb6f0") {
15
- for (let $i = 0; $i < 43; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
16
- $[0] = "911725dec1e42161265ff27436f9ae9c6c82392d2b8fce53f3eb80dfb29cb6f0";
12
+ const $ = c(37);
13
+ if ($[0] !== "9972e9f574379197af85e2183aa9808df309da92f446247a1c5412a1062393cb") {
14
+ for (let $i = 0; $i < 37; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
15
+ $[0] = "9972e9f574379197af85e2183aa9808df309da92f446247a1c5412a1062393cb";
17
16
  }
18
17
  const isOpen = useSelector(studioStore, _temp);
19
18
  const [search, setSearch] = React.useState("");
@@ -163,64 +162,18 @@ function CommandPalette() {
163
162
  } else t8 = $[25];
164
163
  let t9;
165
164
  if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
166
- t9 = /* @__PURE__ */ jsxs("span", {
167
- className: "flex items-center gap-1.5",
168
- children: [
169
- /* @__PURE__ */ jsx(Kbd, { children: "↑" }),
170
- /* @__PURE__ */ jsx(Kbd, { children: "↓" }),
171
- " to navigate"
172
- ]
165
+ t9 = /* @__PURE__ */ jsx(CommandEmpty, {
166
+ className: "text-muted-foreground py-8",
167
+ children: "No results found."
173
168
  });
174
169
  $[26] = t9;
175
170
  } else t9 = $[26];
176
171
  let t10;
177
- if ($[27] === Symbol.for("react.memo_cache_sentinel")) {
178
- t10 = /* @__PURE__ */ jsxs("span", {
179
- className: "flex items-center gap-1.5",
180
- children: [/* @__PURE__ */ jsx(Kbd, { children: "↵" }), " to select"]
181
- });
182
- $[27] = t10;
183
- } else t10 = $[27];
184
- let t11;
185
- if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
186
- t11 = /* @__PURE__ */ jsx(Kbd, { children: "Esc" });
187
- $[28] = t11;
188
- } else t11 = $[28];
189
- const t12 = page ? "to go back" : "to close";
190
- let t13;
191
- if ($[29] !== t12) {
192
- t13 = /* @__PURE__ */ jsxs("div", {
193
- className: "text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-2 border-b px-5 pb-3 text-xs",
194
- children: [
195
- t9,
196
- t10,
197
- /* @__PURE__ */ jsxs("span", {
198
- className: "flex items-center gap-1.5",
199
- children: [
200
- t11,
201
- " ",
202
- t12
203
- ]
204
- })
205
- ]
206
- });
207
- $[29] = t12;
208
- $[30] = t13;
209
- } else t13 = $[30];
210
- let t14;
211
- if ($[31] === Symbol.for("react.memo_cache_sentinel")) {
212
- t14 = /* @__PURE__ */ jsx(CommandEmpty, {
213
- className: "text-muted-foreground py-8",
214
- children: "No results found."
215
- });
216
- $[31] = t14;
217
- } else t14 = $[31];
218
- let t15;
219
- if ($[32] !== groupedActions || $[33] !== handleSelect) {
220
- t15 = /* @__PURE__ */ jsxs(CommandList, {
172
+ if ($[27] !== groupedActions || $[28] !== handleSelect) {
173
+ t10 = /* @__PURE__ */ jsxs(CommandList, {
221
174
  className: "max-h-[min(24rem,45dvh)] min-h-0 scroll-py-1 px-2 pt-1 pb-2",
222
- children: [t14, Array.from(groupedActions.entries()).map((t16) => {
223
- const [groupLabel_0, groupActions] = t16;
175
+ children: [t9, Array.from(groupedActions.entries()).map((t11) => {
176
+ const [groupLabel_0, groupActions] = t11;
224
177
  return /* @__PURE__ */ jsx(CommandGroup, {
225
178
  heading: groupLabel_0,
226
179
  className: "mb-2 p-0 last:mb-0 **:[[cmdk-group-heading]]:px-3 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:text-[11px] **:[[cmdk-group-heading]]:tracking-widest **:[[cmdk-group-heading]]:uppercase",
@@ -234,44 +187,42 @@ function CommandPalette() {
234
187
  }, groupLabel_0);
235
188
  })]
236
189
  });
237
- $[32] = groupedActions;
238
- $[33] = handleSelect;
239
- $[34] = t15;
240
- } else t15 = $[34];
241
- let t16;
242
- if ($[35] !== t13 || $[36] !== t15 || $[37] !== t8 || $[38] !== value) {
243
- t16 = /* @__PURE__ */ jsxs(Command, {
190
+ $[27] = groupedActions;
191
+ $[28] = handleSelect;
192
+ $[29] = t10;
193
+ } else t10 = $[29];
194
+ let t11;
195
+ if ($[30] !== t10 || $[31] !== t8 || $[32] !== value) {
196
+ t11 = /* @__PURE__ */ jsxs(Command, {
244
197
  value,
245
198
  onValueChange: setValue,
246
199
  className: "h-auto max-h-[80dvh] p-0",
247
200
  children: [
248
201
  t6,
249
202
  t8,
250
- t13,
251
- t15
203
+ t10
252
204
  ]
253
205
  });
254
- $[35] = t13;
255
- $[36] = t15;
256
- $[37] = t8;
257
- $[38] = value;
258
- $[39] = t16;
259
- } else t16 = $[39];
260
- let t17;
261
- if ($[40] !== isOpen || $[41] !== t16) {
262
- t17 = /* @__PURE__ */ jsx(CommandDialog, {
206
+ $[30] = t10;
207
+ $[31] = t8;
208
+ $[32] = value;
209
+ $[33] = t11;
210
+ } else t11 = $[33];
211
+ let t12;
212
+ if ($[34] !== isOpen || $[35] !== t11) {
213
+ t12 = /* @__PURE__ */ jsx(CommandDialog, {
263
214
  open: isOpen,
264
215
  onOpenChange: handleOpenChange,
265
216
  className: "top-[12vh] sm:max-w-xl",
266
217
  description: "Navigate your site and switch settings quickly.",
267
218
  showCloseButton: true,
268
- children: t16
219
+ children: t11
269
220
  });
270
- $[40] = isOpen;
271
- $[41] = t16;
272
- $[42] = t17;
273
- } else t17 = $[42];
274
- return t17;
221
+ $[34] = isOpen;
222
+ $[35] = t11;
223
+ $[36] = t12;
224
+ } else t12 = $[36];
225
+ return t12;
275
226
  }
276
227
  function _temp3(state_1) {
277
228
  return state_1.context.commandPalettePages;
@@ -284,9 +235,9 @@ function _temp(state) {
284
235
  }
285
236
  function useCommandPaletteActions() {
286
237
  const $ = c(2);
287
- if ($[0] !== "911725dec1e42161265ff27436f9ae9c6c82392d2b8fce53f3eb80dfb29cb6f0") {
238
+ if ($[0] !== "9972e9f574379197af85e2183aa9808df309da92f446247a1c5412a1062393cb") {
288
239
  for (let $i = 0; $i < 2; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
289
- $[0] = "911725dec1e42161265ff27436f9ae9c6c82392d2b8fce53f3eb80dfb29cb6f0";
240
+ $[0] = "9972e9f574379197af85e2183aa9808df309da92f446247a1c5412a1062393cb";
290
241
  }
291
242
  let t0;
292
243
  if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
@@ -14,15 +14,15 @@ import { jsx } from "react/jsx-runtime";
14
14
 
15
15
  //#region src/features/runtime/pageApp.tsx
16
16
  function PageApp(t0) {
17
- const $ = c(19);
18
- if ($[0] !== "9929b5234f34e8dc8642da9eb491a90e9a57f3ab875ddfc1a186f42f4452f2f5") {
19
- for (let $i = 0; $i < 19; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
20
- $[0] = "9929b5234f34e8dc8642da9eb491a90e9a57f3ab875ddfc1a186f42f4452f2f5";
17
+ const $ = c(20);
18
+ if ($[0] !== "cc0d298a8943efca3ffb5e7cf2f8f814ebecbf27dec68e2a3129c1bb95c18943") {
19
+ for (let $i = 0; $i < 20; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
20
+ $[0] = "cc0d298a8943efca3ffb5e7cf2f8f814ebecbf27dec68e2a3129c1bb95c18943";
21
21
  }
22
22
  const { camoxApp, input, queryClient } = t0;
23
23
  const t1 = input.dehydratedState;
24
24
  let t2;
25
- if ($[1] !== camoxApp || $[2] !== input.apiUrl || $[3] !== input.authenticationUrl || $[4] !== input.environmentName || $[5] !== input.presentation || $[6] !== input.projectSlug || $[7] !== queryClient) {
25
+ if ($[1] !== camoxApp || $[2] !== input.apiUrl || $[3] !== input.authenticationUrl || $[4] !== input.environmentName || $[5] !== input.presentation || $[6] !== input.previewHandoff || $[7] !== input.projectSlug || $[8] !== queryClient) {
26
26
  t2 = (currentInput) => /* @__PURE__ */ jsx(CoreCamoxProvider, {
27
27
  camoxApp,
28
28
  authenticationUrl: input.authenticationUrl,
@@ -33,7 +33,10 @@ function PageApp(t0) {
33
33
  initialProject: currentInput.project,
34
34
  children: /* @__PURE__ */ jsx(PreviewDocumentContext.Provider, {
35
35
  value: currentInput.previewDocument,
36
- children: /* @__PURE__ */ jsx(PageExperience, {
36
+ children: input.previewHandoff ? /* @__PURE__ */ jsx("div", {
37
+ role: "status",
38
+ children: "Signing in to draft preview…"
39
+ }) : /* @__PURE__ */ jsx(PageExperience, {
37
40
  camoxApp,
38
41
  input: currentInput,
39
42
  queryClient,
@@ -46,42 +49,43 @@ function PageApp(t0) {
46
49
  $[3] = input.authenticationUrl;
47
50
  $[4] = input.environmentName;
48
51
  $[5] = input.presentation;
49
- $[6] = input.projectSlug;
50
- $[7] = queryClient;
51
- $[8] = t2;
52
- } else t2 = $[8];
52
+ $[6] = input.previewHandoff;
53
+ $[7] = input.projectSlug;
54
+ $[8] = queryClient;
55
+ $[9] = t2;
56
+ } else t2 = $[9];
53
57
  let t3;
54
- if ($[9] !== input || $[10] !== queryClient || $[11] !== t2) {
58
+ if ($[10] !== input || $[11] !== queryClient || $[12] !== t2) {
55
59
  t3 = /* @__PURE__ */ jsx(PageNavigationProvider, {
56
60
  initialInput: input,
57
61
  queryClient,
58
62
  children: t2
59
63
  });
60
- $[9] = input;
61
- $[10] = queryClient;
62
- $[11] = t2;
63
- $[12] = t3;
64
- } else t3 = $[12];
64
+ $[10] = input;
65
+ $[11] = queryClient;
66
+ $[12] = t2;
67
+ $[13] = t3;
68
+ } else t3 = $[13];
65
69
  let t4;
66
- if ($[13] !== t1 || $[14] !== t3) {
70
+ if ($[14] !== t1 || $[15] !== t3) {
67
71
  t4 = /* @__PURE__ */ jsx(HydrationBoundary, {
68
72
  state: t1,
69
73
  children: t3
70
74
  });
71
- $[13] = t1;
72
- $[14] = t3;
73
- $[15] = t4;
74
- } else t4 = $[15];
75
+ $[14] = t1;
76
+ $[15] = t3;
77
+ $[16] = t4;
78
+ } else t4 = $[16];
75
79
  let t5;
76
- if ($[16] !== queryClient || $[17] !== t4) {
80
+ if ($[17] !== queryClient || $[18] !== t4) {
77
81
  t5 = /* @__PURE__ */ jsx(QueryClientProvider, {
78
82
  client: queryClient,
79
83
  children: t4
80
84
  });
81
- $[16] = queryClient;
82
- $[17] = t4;
83
- $[18] = t5;
84
- } else t5 = $[18];
85
+ $[17] = queryClient;
86
+ $[18] = t4;
87
+ $[19] = t5;
88
+ } else t5 = $[19];
85
89
  return t5;
86
90
  }
87
91
  const LazyEditablePageExperience = React.lazy(() => import("../preview/EditablePageExperience.js").then((module) => ({ default: module.EditablePageExperience })));
@@ -101,9 +105,9 @@ var EditingActivationBoundary = class extends React.Component {
101
105
  const subscribeToHydration = () => () => {};
102
106
  function PageExperience(t0) {
103
107
  const $ = c(39);
104
- if ($[0] !== "9929b5234f34e8dc8642da9eb491a90e9a57f3ab875ddfc1a186f42f4452f2f5") {
108
+ if ($[0] !== "cc0d298a8943efca3ffb5e7cf2f8f814ebecbf27dec68e2a3129c1bb95c18943") {
105
109
  for (let $i = 0; $i < 39; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
106
- $[0] = "9929b5234f34e8dc8642da9eb491a90e9a57f3ab875ddfc1a186f42f4452f2f5";
110
+ $[0] = "cc0d298a8943efca3ffb5e7cf2f8f814ebecbf27dec68e2a3129c1bb95c18943";
107
111
  }
108
112
  const { camoxApp, input, queryClient, studioDocument } = t0;
109
113
  const { isAuthenticated, isLoading } = useAuthState();
@@ -1,5 +1,6 @@
1
+ import { prefetchIcons } from "../../core/lib/icons.js";
1
2
  import { PageApp } from "./pageApp.js";
2
- import { QueryClient, hydrate } from "@tanstack/react-query";
3
+ import { QueryClient, dehydrate, hydrate } from "@tanstack/react-query";
3
4
  import { jsx } from "react/jsx-runtime";
4
5
  import { renderToString } from "react-dom/server";
5
6
 
@@ -7,6 +8,8 @@ import { renderToString } from "react-dom/server";
7
8
  async function renderPageWithApp(input) {
8
9
  const queryClient = new QueryClient();
9
10
  hydrate(queryClient, input.dehydratedState);
11
+ await prefetchIcons(queryClient, input.dehydratedState, input.camoxApp.getSerializableDefinitions());
12
+ Object.assign(input.dehydratedState, dehydrate(queryClient));
10
13
  return renderToString(/* @__PURE__ */ jsx(PageApp, {
11
14
  camoxApp: input.camoxApp,
12
15
  input,
@@ -17,6 +17,8 @@ interface LayoutIdentity {
17
17
  version: string;
18
18
  }
19
19
  interface PageRenderInput {
20
+ /** Authenticate before looking up a page that may exist only in draft. */
21
+ previewHandoff?: boolean;
20
22
  previewDocument?: string;
21
23
  routeKind?: "studio" | "studio-content" | "studio-nested";
22
24
  project?: Awaited<ReturnType<ReturnType<typeof createServerApiClient>["projects"]["getBySlug"]>>;
@@ -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,33 @@ async function createPageHtmlResponse({ options, pathname, request }) {
214
208
  pathname,
215
209
  request
216
210
  });
211
+ const requestUrl = new URL(request.url);
212
+ if (requestUrl.searchParams.has("camox-preview") && requestUrl.searchParams.has("ott")) {
213
+ const input = {
214
+ previewHandoff: true,
215
+ presentation: "public",
216
+ source: "live",
217
+ apiUrl: options.apiUrl,
218
+ authenticationUrl: options.authenticationUrl,
219
+ environmentName: options.environmentName,
220
+ projectSlug: options.projectSlug,
221
+ href: requestUrl.href,
222
+ pathname,
223
+ runtimeBasePath: normalizeRuntimeBasePath(options.runtimeBasePath),
224
+ head: {},
225
+ loaderData: null,
226
+ layoutIdentity: null,
227
+ dehydratedState: dehydrate(new QueryClient())
228
+ };
229
+ const html = await options.renderPage(input);
230
+ return new Response(`<!doctype html><html><head><meta name="referrer" content="no-referrer"><title>Camox draft preview</title><script id="__CAMOX_DATA__" type="application/json">${serializeJsonForHtml(input)}<\/script></head><body><div id="root">${html}</div>${renderClientEntryScript(options.pageClientEntryUrl)}</body></html>`, { headers: {
231
+ "Content-Type": "text/html; charset=utf-8",
232
+ "Cache-Control": "private, no-store",
233
+ "Referrer-Policy": "no-referrer"
234
+ } });
235
+ }
236
+ const singleton = await createDerivedResponse(options, pathname, request, false, true);
237
+ if (singleton) return singleton;
217
238
  const queryClient = new QueryClient();
218
239
  try {
219
240
  const result = await loadCamoxPageForRequest({
@@ -277,13 +298,14 @@ ${renderClientEntryScript(options.pageClientEntryUrl)}
277
298
  throw error;
278
299
  }
279
300
  }
280
- async function createDerivedResponse(options, pathname, request, dataOnly = false) {
301
+ async function createDerivedResponse(options, pathname, request, dataOnly = false, singletonOnly = false) {
281
302
  const app = await options.getCamoxApp?.();
282
303
  const match = app && matchDerivedLayout(app.getLayouts(), pathname);
283
304
  if (!match || !dataOnly && !options.renderPage) return null;
305
+ if (singletonOnly && match.layout._internal.kind !== "singleton") return null;
284
306
  try {
285
- const data = await match.layout._internal.loader({ params: match.params });
286
- const serializedData = JSON.parse(JSON.stringify(data));
307
+ const data = await match.layout._internal.loader?.({ params: match.params });
308
+ const serializedData = data === void 0 ? void 0 : JSON.parse(JSON.stringify(data));
287
309
  const queryClient = new QueryClient();
288
310
  const authCookieHeader = getServerAuthCookieHeader(request.headers);
289
311
  let source = authCookieHeader ? "draft" : "live";
@@ -404,6 +426,8 @@ ${renderClientEntryScript(options.studioClientEntryUrl)}
404
426
  }
405
427
  async function createPageDataResponse({ options, pathname, request }) {
406
428
  if (!options.apiUrl || !options.projectSlug) return Response.json({ message: "Camox page loader is not configured." }, { status: 500 });
429
+ const singleton = await createDerivedResponse(options, pathname, request, true, true);
430
+ if (singleton) return singleton;
407
431
  const queryClient = new QueryClient();
408
432
  try {
409
433
  const result = await loadCamoxPageForRequest({
@@ -1,5 +1,6 @@
1
1
  import { AuthContext } from "../../../lib/auth.js";
2
2
  import { environmentMutations, environmentQueries, projectQueries } from "../../../lib/queries.js";
3
+ import { areCommentsEnabled } from "../../preview/commentsEnabled.js";
3
4
  import { c } from "react/compiler-runtime";
4
5
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
6
  import * as React from "react";
@@ -30,15 +31,16 @@ function formatReason(reason, direction) {
30
31
  case "block-definition-missing-in-source": return `Block type "${reason.blockId}" exists in ${targetLabel} but not in ${sourceLabel}.`;
31
32
  case "block-definition-missing-in-target": return `Block type "${reason.blockId}" exists in ${sourceLabel} but not in ${targetLabel}. ${remedy}`;
32
33
  case "block-definition-schema-mismatch": return `Block type "${reason.blockId}" has a different "${reason.field}" between envs. Sync block definitions and try again.`;
34
+ case "layout-kind-mismatch": return `Layout "${reason.layoutId}" has a different kind between environments. Sync layout definitions and try again.`;
33
35
  case "layout-missing-in-source": return `Layout "${reason.layoutId}" exists in ${targetLabel} but not in ${sourceLabel}.`;
34
36
  case "layout-missing-in-target": return `Layout "${reason.layoutId}" exists in ${sourceLabel} but not in ${targetLabel}. ${remedy}`;
35
37
  }
36
38
  }
37
39
  const EnvironmentMenu = () => {
38
- const $ = c(84);
39
- if ($[0] !== "42128d7498b871de7c9ecaaeb2e1b0e858fc9e8ce0ed44b99ccfaf471a4c55ca") {
40
- for (let $i = 0; $i < 84; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
41
- $[0] = "42128d7498b871de7c9ecaaeb2e1b0e858fc9e8ce0ed44b99ccfaf471a4c55ca";
40
+ const $ = c(87);
41
+ if ($[0] !== "a19764fc2cadba3ca96828ce487913043214419dfe0474e5a0ab5e6e8d2d6177") {
42
+ for (let $i = 0; $i < 87; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
43
+ $[0] = "a19764fc2cadba3ca96828ce487913043214419dfe0474e5a0ab5e6e8d2d6177";
42
44
  }
43
45
  const [open, setOpen] = React.useState(false);
44
46
  const [pendingDirection, setPendingDirection] = React.useState(null);
@@ -301,7 +303,7 @@ const EnvironmentMenu = () => {
301
303
  } else t25 = $[55];
302
304
  let t26;
303
305
  if ($[56] !== environmentName || $[57] !== pendingDirection) {
304
- t26 = /* @__PURE__ */ jsx(AlertDialogDescription, { children: pendingDirection === "push" ? /* @__PURE__ */ jsxs(Fragment, { children: [
306
+ t26 = pendingDirection === "push" ? /* @__PURE__ */ jsxs(Fragment, { children: [
305
307
  "This will replace all content in",
306
308
  " ",
307
309
  /* @__PURE__ */ jsx("span", {
@@ -328,84 +330,95 @@ const EnvironmentMenu = () => {
328
330
  children: "production"
329
331
  }),
330
332
  ". This action cannot be undone."
331
- ] }) });
333
+ ] });
332
334
  $[56] = environmentName;
333
335
  $[57] = pendingDirection;
334
336
  $[58] = t26;
335
337
  } else t26 = $[58];
336
338
  let t27;
337
- if ($[59] !== t25 || $[60] !== t26) {
338
- t27 = /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [t25, t26] });
339
- $[59] = t25;
340
- $[60] = t26;
341
- $[61] = t27;
342
- } else t27 = $[61];
339
+ if ($[59] === Symbol.for("react.memo_cache_sentinel")) {
340
+ t27 = areCommentsEnabled() && " Feedback comments in the destination will also be replaced.";
341
+ $[59] = t27;
342
+ } else t27 = $[59];
343
343
  let t28;
344
- if ($[62] !== replicate.isPending) {
345
- t28 = /* @__PURE__ */ jsx(AlertDialogCancel, {
344
+ if ($[60] !== t26) {
345
+ t28 = /* @__PURE__ */ jsxs(AlertDialogDescription, { children: [t26, t27] });
346
+ $[60] = t26;
347
+ $[61] = t28;
348
+ } else t28 = $[61];
349
+ let t29;
350
+ if ($[62] !== t25 || $[63] !== t28) {
351
+ t29 = /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [t25, t28] });
352
+ $[62] = t25;
353
+ $[63] = t28;
354
+ $[64] = t29;
355
+ } else t29 = $[64];
356
+ let t30;
357
+ if ($[65] !== replicate.isPending) {
358
+ t30 = /* @__PURE__ */ jsx(AlertDialogCancel, {
346
359
  variant: "outline",
347
360
  size: "default",
348
361
  disabled: replicate.isPending,
349
362
  children: "Cancel"
350
363
  });
351
- $[62] = replicate.isPending;
352
- $[63] = t28;
353
- } else t28 = $[63];
354
- let t29;
355
- if ($[64] !== replicate.isPending) {
356
- t29 = replicate.isPending ? /* @__PURE__ */ jsx(Spinner, {}) : null;
357
- $[64] = replicate.isPending;
358
- $[65] = t29;
359
- } else t29 = $[65];
360
- const t30 = pendingDirection === "push" ? "Push" : "Pull";
364
+ $[65] = replicate.isPending;
365
+ $[66] = t30;
366
+ } else t30 = $[66];
361
367
  let t31;
362
- if ($[66] !== handleConfirm || $[67] !== replicate.isPending || $[68] !== t29 || $[69] !== t30) {
363
- t31 = /* @__PURE__ */ jsxs(AlertDialogAction, {
368
+ if ($[67] !== replicate.isPending) {
369
+ t31 = replicate.isPending ? /* @__PURE__ */ jsx(Spinner, {}) : null;
370
+ $[67] = replicate.isPending;
371
+ $[68] = t31;
372
+ } else t31 = $[68];
373
+ const t32 = pendingDirection === "push" ? "Push" : "Pull";
374
+ let t33;
375
+ if ($[69] !== handleConfirm || $[70] !== replicate.isPending || $[71] !== t31 || $[72] !== t32) {
376
+ t33 = /* @__PURE__ */ jsxs(AlertDialogAction, {
364
377
  variant: "destructive",
365
378
  onClick: handleConfirm,
366
379
  disabled: replicate.isPending,
367
- children: [t29, t30]
380
+ children: [t31, t32]
368
381
  });
369
- $[66] = handleConfirm;
370
- $[67] = replicate.isPending;
371
- $[68] = t29;
372
- $[69] = t30;
373
- $[70] = t31;
374
- } else t31 = $[70];
375
- let t32;
376
- if ($[71] !== t28 || $[72] !== t31) {
377
- t32 = /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [t28, t31] });
378
- $[71] = t28;
379
- $[72] = t31;
380
- $[73] = t32;
381
- } else t32 = $[73];
382
- let t33;
383
- if ($[74] !== t27 || $[75] !== t32) {
384
- t33 = /* @__PURE__ */ jsxs(AlertDialogContent, { children: [t27, t32] });
385
- $[74] = t27;
386
- $[75] = t32;
387
- $[76] = t33;
388
- } else t33 = $[76];
382
+ $[69] = handleConfirm;
383
+ $[70] = replicate.isPending;
384
+ $[71] = t31;
385
+ $[72] = t32;
386
+ $[73] = t33;
387
+ } else t33 = $[73];
389
388
  let t34;
390
- if ($[77] !== t22 || $[78] !== t23 || $[79] !== t33) {
391
- t34 = /* @__PURE__ */ jsx(AlertDialog, {
389
+ if ($[74] !== t30 || $[75] !== t33) {
390
+ t34 = /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [t30, t33] });
391
+ $[74] = t30;
392
+ $[75] = t33;
393
+ $[76] = t34;
394
+ } else t34 = $[76];
395
+ let t35;
396
+ if ($[77] !== t29 || $[78] !== t34) {
397
+ t35 = /* @__PURE__ */ jsxs(AlertDialogContent, { children: [t29, t34] });
398
+ $[77] = t29;
399
+ $[78] = t34;
400
+ $[79] = t35;
401
+ } else t35 = $[79];
402
+ let t36;
403
+ if ($[80] !== t22 || $[81] !== t23 || $[82] !== t35) {
404
+ t36 = /* @__PURE__ */ jsx(AlertDialog, {
392
405
  open: t22,
393
406
  onOpenChange: t23,
394
- children: t33
407
+ children: t35
395
408
  });
396
- $[77] = t22;
397
- $[78] = t23;
398
- $[79] = t33;
399
- $[80] = t34;
400
- } else t34 = $[80];
401
- let t35;
402
- if ($[81] !== t21 || $[82] !== t34) {
403
- t35 = /* @__PURE__ */ jsxs(Fragment, { children: [t21, t34] });
404
- $[81] = t21;
405
- $[82] = t34;
406
- $[83] = t35;
407
- } else t35 = $[83];
408
- return t35;
409
+ $[80] = t22;
410
+ $[81] = t23;
411
+ $[82] = t35;
412
+ $[83] = t36;
413
+ } else t36 = $[83];
414
+ let t37;
415
+ if ($[84] !== t21 || $[85] !== t36) {
416
+ t37 = /* @__PURE__ */ jsxs(Fragment, { children: [t21, t36] });
417
+ $[84] = t21;
418
+ $[85] = t36;
419
+ $[86] = t37;
420
+ } else t37 = $[86];
421
+ return t37;
409
422
  };
410
423
 
411
424
  //#endregion