camox 0.47.0 → 0.51.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 (117) hide show
  1. package/dist/core/createApp.d.ts +19 -6
  2. package/dist/core/createApp.js +7 -2
  3. package/dist/core/createBlock.js +54 -43
  4. package/dist/core/createCollection.d.ts +25 -7
  5. package/dist/core/createCollection.js +28 -9
  6. package/dist/core/createLayout.d.ts +98 -71
  7. package/dist/core/createLayout.js +10 -16
  8. package/dist/core/derivedRoutes.js +1 -1
  9. package/dist/core/editing/BlockEditingRuntime.d.ts +3 -2
  10. package/dist/core/editing/CompleteBlockEditingRuntime.js +2 -1
  11. package/dist/core/editing/createEditableBlock.d.ts +59 -19
  12. package/dist/core/editing/createEditableBlock.js +162 -151
  13. package/dist/core/editing/useLatestRecord.js +7 -7
  14. package/dist/core/layoutContext.d.ts +55 -0
  15. package/dist/core/layoutContext.js +66 -0
  16. package/dist/core/lib/contentType.d.ts +91 -4
  17. package/dist/core/lib/contentType.js +84 -7
  18. package/dist/core/lib/recordScope.js +19 -17
  19. package/dist/core/lib/reference.d.ts +6 -2
  20. package/dist/core/recordItem.js +55 -2
  21. package/dist/core/requestContext.d.ts +18 -0
  22. package/dist/core/structuredData.d.ts +126 -0
  23. package/dist/core/structuredData.js +40 -0
  24. package/dist/features/canvas/canvasCamera.js +1 -1
  25. package/dist/features/canvas/sidebar/CanvasInspector.js +1 -1
  26. package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +1 -1
  27. package/dist/features/content/ContentAssets.js +84 -88
  28. package/dist/features/content/ContentCollection.js +13 -7
  29. package/dist/features/content/collection-form.js +6 -2
  30. package/dist/features/content/components/MissingFromLocale.js +1 -1
  31. package/dist/features/og/pageImage.js +89 -0
  32. package/dist/features/page/CuratedPageContent.js +45 -32
  33. package/dist/features/page/DerivedPageContent.js +49 -22
  34. package/dist/features/page/SharedLayoutContent.js +26 -22
  35. package/dist/features/preview/CamoxPreview.js +41 -27
  36. package/dist/features/preview/EditablePageExperience.js +17 -9
  37. package/dist/features/preview/components/AddBlockSheet.js +1 -0
  38. package/dist/features/preview/components/BlockActionsPopover.js +19 -41
  39. package/dist/features/preview/components/FieldToolbar.js +1 -1
  40. package/dist/features/preview/components/MobilePreviewDrawer.js +6 -9
  41. package/dist/features/preview/components/PageInfo.js +244 -264
  42. package/dist/features/preview/components/PageTree.js +63 -53
  43. package/dist/features/preview/components/PreviewPanel.js +10 -64
  44. package/dist/features/preview/components/PreviewToolbar.js +1 -1
  45. package/dist/features/preview/components/RecordCard.js +50 -34
  46. package/dist/features/preview/components/RecordCombobox.js +12 -5
  47. package/dist/features/preview/components/ReferenceFieldEditor.js +4 -2
  48. package/dist/features/preview/components/ReferenceListFieldEditor.js +24 -19
  49. package/dist/features/preview/components/RepeatableItemsList.js +62 -46
  50. package/dist/features/preview/components/SelectionEditor.js +3 -2
  51. package/dist/features/preview/components/useFieldWriter.js +2 -4
  52. package/dist/features/preview/components/useRecordView.js +2 -2
  53. package/dist/features/preview/entityName.js +11 -0
  54. package/dist/features/preview/pageRecord.js +17 -18
  55. package/dist/features/preview/previewConstants.js +2 -8
  56. package/dist/features/preview/usePreviewViewportActions.js +1 -1
  57. package/dist/features/provider/AuthenticatedCamoxProvider.js +1 -1
  58. package/dist/features/provider/components/CommandPalette.js +2 -5
  59. package/dist/features/provider/signInShortcut.js +1 -2
  60. package/dist/features/provider/useAdminShortcuts.js +47 -53
  61. package/dist/features/routes/pageRuntime.d.ts +17 -2
  62. package/dist/features/routes/pageRuntime.js +61 -57
  63. package/dist/features/runtime/RuntimeChrome.js +1 -1
  64. package/dist/features/runtime/breadcrumbs.js +68 -0
  65. package/dist/features/runtime/livePages.js +3 -2
  66. package/dist/features/runtime/pageGraph.js +92 -0
  67. package/dist/features/runtime/projectLogo.js +110 -0
  68. package/dist/features/runtime/publicCache.js +83 -0
  69. package/dist/features/runtime/runtime.d.ts +24 -9
  70. package/dist/features/runtime/runtime.js +447 -130
  71. package/dist/features/runtime/runtimeRoutes.d.ts +1 -0
  72. package/dist/features/runtime/runtimeRoutes.js +1 -0
  73. package/dist/features/runtime/structuredData.js +213 -0
  74. package/dist/features/runtime/worker.d.ts +5 -1
  75. package/dist/features/runtime/worker.js +20 -3
  76. package/dist/features/settings/AnalyticsSettings.js +622 -0
  77. package/dist/features/settings/CamoxSettings.js +95 -0
  78. package/dist/features/settings/FaviconSettings.js +254 -0
  79. package/dist/features/settings/ProjectNameSettings.js +145 -0
  80. package/dist/features/settings/SettingsPart.js +73 -0
  81. package/dist/features/settings/SocialProfilesSettings.js +245 -0
  82. package/dist/features/settings/useProjectMutation.js +61 -0
  83. package/dist/features/studio/components/ChatSidebar.js +5 -5
  84. package/dist/features/studio/components/DraftsControl.js +136 -131
  85. package/dist/features/studio/components/LocalePicker.js +178 -174
  86. package/dist/features/studio/components/Navbar.js +107 -66
  87. package/dist/features/studio/components/ProjectMenu.js +85 -110
  88. package/dist/features/studio/components/ReviewDraftSheet.js +1046 -990
  89. package/dist/features/studio/components/ThreadMessages.js +2 -2
  90. package/dist/features/studio/components/UserButton.js +1 -1
  91. package/dist/features/studio/routes.js +3 -1
  92. package/dist/features/studio/showInCanvas.js +129 -0
  93. package/dist/features/vite/blockBoilerplate.js +3 -2
  94. package/dist/features/vite/definitionsSync.js +4 -5
  95. package/dist/features/vite/layoutGeneration.js +3 -2
  96. package/dist/features/vite/runtimeDev.js +32 -17
  97. package/dist/features/vite/staticAssetHeaders.js +52 -0
  98. package/dist/features/vite/vite.d.ts +1 -1
  99. package/dist/features/vite/vite.js +17 -4
  100. package/dist/lib/api-client-server.d.ts +1 -1
  101. package/dist/lib/api-client.d.ts +2 -1
  102. package/dist/lib/draft-scope.d.ts +1 -0
  103. package/dist/lib/locale-path.d.ts +3 -11
  104. package/dist/lib/locale-path.js +3 -27
  105. package/dist/lib/queries.d.ts +4 -0
  106. package/dist/lib/queries.js +8 -1
  107. package/dist/lib/shortcuts.js +29 -0
  108. package/dist/lib/studio-reads.d.ts +1 -0
  109. package/dist/lib/studio-reads.js +3 -1
  110. package/dist/lib/utils.js +1 -104
  111. package/dist/package.js +1 -1
  112. package/dist/studio.css +1 -1
  113. package/package.json +6 -4
  114. package/skills/camox/references/block-definitions.md +105 -25
  115. package/skills/camox/references/layout-definitions.md +53 -17
  116. package/skills/camox/references/routed-layouts.md +17 -14
  117. package/dist/features/studio/components/FaviconDialog.js +0 -318
@@ -24,7 +24,7 @@ function CanvasSidebarFrame(t0) {
24
24
  }
25
25
  let t1;
26
26
  if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
27
- t1 = { width: 250 };
27
+ t1 = { width: 265 };
28
28
  $[4] = t1;
29
29
  } else t1 = $[4];
30
30
  let t2;
@@ -1,6 +1,5 @@
1
1
  import { useProjectUuid } from "../../lib/auth.js";
2
2
  import { reads, useReadQuery } from "../../lib/studio-reads.js";
3
- import { checkIfInputFocused } from "../../lib/utils.js";
4
3
  import { UploadDropZone } from "./components/UploadDropZone.js";
5
4
  import { UploadProgressDrawer } from "./components/UploadProgressDrawer.js";
6
5
  import { useFileUpload } from "../../hooks/use-file-upload.js";
@@ -10,12 +9,13 @@ import { AssetCard } from "./components/AssetCard.js";
10
9
  import { useMarqueeSelection } from "../../hooks/use-marquee-selection.js";
11
10
  import { AssetCardSkeleton } from "./components/AssetCardSkeleton.js";
12
11
  import { c } from "react/compiler-runtime";
13
- import { useEffect, useRef, useState } from "react";
12
+ import { useRef, useState } from "react";
14
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
14
  import { useMutation } from "@tanstack/react-query";
16
15
  import { Button } from "@camox/ui/button";
17
16
  import { PanelContent } from "@camox/ui/panel";
18
17
  import { ImageIcon, UploadIcon } from "lucide-react";
18
+ import { useHotkey } from "@tanstack/react-hotkeys";
19
19
  import { FloatingToolbar } from "@camox/ui/floating-toolbar";
20
20
 
21
21
  //#region src/features/content/ContentAssets.tsx
@@ -74,31 +74,27 @@ const ContentAssets = () => {
74
74
  $[10] = t8;
75
75
  } else t8 = $[10];
76
76
  const { selectionRect, didDragRef, handlers } = useMarqueeSelection(containerRef, t8);
77
- let t10;
78
77
  let t9;
79
- if ($[11] !== lightboxFileId) {
80
- t9 = () => {
81
- const handleKeyDown = (event) => {
82
- if (event.key !== "Escape") return;
83
- if (lightboxFileId !== null) return;
84
- if (checkIfInputFocused()) return;
85
- setSelectedIds(_temp);
86
- };
87
- document.addEventListener("keydown", handleKeyDown);
88
- return () => document.removeEventListener("keydown", handleKeyDown);
78
+ if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
79
+ t9 = () => setSelectedIds(_temp);
80
+ $[11] = t9;
81
+ } else t9 = $[11];
82
+ const t10 = lightboxFileId === null;
83
+ let t11;
84
+ if ($[12] !== t10) {
85
+ t11 = {
86
+ enabled: t10,
87
+ ignoreInputs: true,
88
+ preventDefault: false,
89
+ stopPropagation: false
89
90
  };
90
- t10 = [lightboxFileId];
91
- $[11] = lightboxFileId;
92
91
  $[12] = t10;
93
- $[13] = t9;
94
- } else {
95
- t10 = $[12];
96
- t9 = $[13];
97
- }
98
- useEffect(t9, t10);
99
- let t11;
92
+ $[13] = t11;
93
+ } else t11 = $[13];
94
+ useHotkey("Escape", t9, t11);
95
+ let t12;
100
96
  if ($[14] !== didDragRef) {
101
- t11 = () => {
97
+ t12 = () => {
102
98
  if (didDragRef.current) {
103
99
  didDragRef.current = false;
104
100
  return;
@@ -106,20 +102,20 @@ const ContentAssets = () => {
106
102
  setSelectedIds(/* @__PURE__ */ new Set());
107
103
  };
108
104
  $[14] = didDragRef;
109
- $[15] = t11;
110
- } else t11 = $[15];
111
- let t12;
105
+ $[15] = t12;
106
+ } else t12 = $[15];
107
+ let t13;
112
108
  if ($[16] !== files) {
113
- t12 = files === void 0 && /* @__PURE__ */ jsx("div", {
109
+ t13 = files === void 0 && /* @__PURE__ */ jsx("div", {
114
110
  className: "grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-4",
115
111
  children: Array.from({ length: 12 }, _temp2)
116
112
  });
117
113
  $[16] = files;
118
- $[17] = t12;
119
- } else t12 = $[17];
120
- let t13;
114
+ $[17] = t13;
115
+ } else t13 = $[17];
116
+ let t14;
121
117
  if ($[18] !== files?.length || $[19] !== uploadFiles) {
122
- t13 = files?.length === 0 && /* @__PURE__ */ jsx("div", {
118
+ t14 = files?.length === 0 && /* @__PURE__ */ jsx("div", {
123
119
  className: "flex h-full items-center justify-center",
124
120
  children: /* @__PURE__ */ jsxs("div", {
125
121
  className: "flex max-w-sm flex-col items-center gap-2 text-center",
@@ -150,9 +146,9 @@ const ContentAssets = () => {
150
146
  type: "file",
151
147
  className: "sr-only",
152
148
  multiple: true,
153
- onChange: (event_0) => {
154
- if (event_0.target.files) uploadFiles(event_0.target.files);
155
- event_0.target.value = "";
149
+ onChange: (event) => {
150
+ if (event.target.files) uploadFiles(event.target.files);
151
+ event.target.value = "";
156
152
  }
157
153
  })
158
154
  ]
@@ -162,11 +158,11 @@ const ContentAssets = () => {
162
158
  });
163
159
  $[18] = files?.length;
164
160
  $[19] = uploadFiles;
165
- $[20] = t13;
166
- } else t13 = $[20];
167
- let t14;
161
+ $[20] = t14;
162
+ } else t14 = $[20];
163
+ let t15;
168
164
  if ($[21] !== files || $[22] !== selectedIds) {
169
- t14 = files && files.length > 0 && /* @__PURE__ */ jsx("div", {
165
+ t15 = files && files.length > 0 && /* @__PURE__ */ jsx("div", {
170
166
  className: "grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-4",
171
167
  children: files.map((file) => /* @__PURE__ */ jsx(AssetCard, {
172
168
  file,
@@ -184,11 +180,11 @@ const ContentAssets = () => {
184
180
  });
185
181
  $[21] = files;
186
182
  $[22] = selectedIds;
187
- $[23] = t14;
188
- } else t14 = $[23];
189
- let t15;
183
+ $[23] = t15;
184
+ } else t15 = $[23];
185
+ let t16;
190
186
  if ($[24] !== selectionRect) {
191
- t15 = selectionRect && /* @__PURE__ */ jsx("div", {
187
+ t16 = selectionRect && /* @__PURE__ */ jsx("div", {
192
188
  className: "border-primary bg-primary/10 pointer-events-none absolute z-50 border",
193
189
  style: {
194
190
  left: selectionRect.left,
@@ -198,61 +194,61 @@ const ContentAssets = () => {
198
194
  }
199
195
  });
200
196
  $[24] = selectionRect;
201
- $[25] = t15;
202
- } else t15 = $[25];
203
- let t16;
204
- if ($[26] !== handlers.onPointerDown || $[27] !== handlers.onPointerMove || $[28] !== handlers.onPointerUp || $[29] !== t11 || $[30] !== t12 || $[31] !== t13 || $[32] !== t14 || $[33] !== t15) {
205
- t16 = /* @__PURE__ */ jsxs(PanelContent, {
197
+ $[25] = t16;
198
+ } else t16 = $[25];
199
+ let t17;
200
+ if ($[26] !== handlers.onPointerDown || $[27] !== handlers.onPointerMove || $[28] !== handlers.onPointerUp || $[29] !== t12 || $[30] !== t13 || $[31] !== t14 || $[32] !== t15 || $[33] !== t16) {
201
+ t17 = /* @__PURE__ */ jsxs(PanelContent, {
206
202
  ref: containerRef,
207
203
  className: "relative p-4 select-none",
208
- onClick: t11,
204
+ onClick: t12,
209
205
  onPointerDown: handlers.onPointerDown,
210
206
  onPointerMove: handlers.onPointerMove,
211
207
  onPointerUp: handlers.onPointerUp,
212
208
  children: [
213
- t12,
214
209
  t13,
215
210
  t14,
216
- t15
211
+ t15,
212
+ t16
217
213
  ]
218
214
  });
219
215
  $[26] = handlers.onPointerDown;
220
216
  $[27] = handlers.onPointerMove;
221
217
  $[28] = handlers.onPointerUp;
222
- $[29] = t11;
223
- $[30] = t12;
224
- $[31] = t13;
225
- $[32] = t14;
226
- $[33] = t15;
227
- $[34] = t16;
228
- } else t16 = $[34];
229
- let t17;
230
- if ($[35] !== t16 || $[36] !== uploadFiles) {
231
- t17 = /* @__PURE__ */ jsx("div", {
218
+ $[29] = t12;
219
+ $[30] = t13;
220
+ $[31] = t14;
221
+ $[32] = t15;
222
+ $[33] = t16;
223
+ $[34] = t17;
224
+ } else t17 = $[34];
225
+ let t18;
226
+ if ($[35] !== t17 || $[36] !== uploadFiles) {
227
+ t18 = /* @__PURE__ */ jsx("div", {
232
228
  className: "flex min-h-0 min-w-0 flex-1 flex-col",
233
229
  children: /* @__PURE__ */ jsx(UploadDropZone, {
234
230
  onDrop: uploadFiles,
235
231
  className: "flex min-h-0 flex-1 flex-col",
236
- children: t16
232
+ children: t17
237
233
  })
238
234
  });
239
- $[35] = t16;
235
+ $[35] = t17;
240
236
  $[36] = uploadFiles;
241
- $[37] = t17;
242
- } else t17 = $[37];
243
- let t18;
237
+ $[37] = t18;
238
+ } else t18 = $[37];
239
+ let t19;
244
240
  if ($[38] !== clearAll || $[39] !== uploads) {
245
- t18 = /* @__PURE__ */ jsx(UploadProgressDrawer, {
241
+ t19 = /* @__PURE__ */ jsx(UploadProgressDrawer, {
246
242
  uploads,
247
243
  onClose: clearAll
248
244
  });
249
245
  $[38] = clearAll;
250
246
  $[39] = uploads;
251
- $[40] = t18;
252
- } else t18 = $[40];
253
- let t19;
247
+ $[40] = t19;
248
+ } else t19 = $[40];
249
+ let t20;
254
250
  if ($[41] !== deleteFiles || $[42] !== selectedIds) {
255
- t19 = selectedIds.size > 0 && /* @__PURE__ */ jsxs(FloatingToolbar, {
251
+ t20 = selectedIds.size > 0 && /* @__PURE__ */ jsxs(FloatingToolbar, {
256
252
  className: "bottom-4 min-w-xs justify-between gap-4 pl-3",
257
253
  children: [/* @__PURE__ */ jsxs("span", {
258
254
  className: "text-muted-foreground",
@@ -274,11 +270,11 @@ const ContentAssets = () => {
274
270
  });
275
271
  $[41] = deleteFiles;
276
272
  $[42] = selectedIds;
277
- $[43] = t19;
278
- } else t19 = $[43];
279
- let t20;
273
+ $[43] = t20;
274
+ } else t20 = $[43];
275
+ let t21;
280
276
  if ($[44] !== lightboxFileId) {
281
- t20 = lightboxFileId && /* @__PURE__ */ jsx(AssetLightbox, {
277
+ t21 = lightboxFileId && /* @__PURE__ */ jsx(AssetLightbox, {
282
278
  open: true,
283
279
  onOpenChange: (open) => {
284
280
  if (!open) setLightboxFileId(null);
@@ -286,23 +282,23 @@ const ContentAssets = () => {
286
282
  fileId: lightboxFileId
287
283
  });
288
284
  $[44] = lightboxFileId;
289
- $[45] = t20;
290
- } else t20 = $[45];
291
- let t21;
292
- if ($[46] !== t17 || $[47] !== t18 || $[48] !== t19 || $[49] !== t20) {
293
- t21 = /* @__PURE__ */ jsxs(Fragment, { children: [
294
- t17,
285
+ $[45] = t21;
286
+ } else t21 = $[45];
287
+ let t22;
288
+ if ($[46] !== t18 || $[47] !== t19 || $[48] !== t20 || $[49] !== t21) {
289
+ t22 = /* @__PURE__ */ jsxs(Fragment, { children: [
295
290
  t18,
296
291
  t19,
297
- t20
292
+ t20,
293
+ t21
298
294
  ] });
299
- $[46] = t17;
300
- $[47] = t18;
301
- $[48] = t19;
302
- $[49] = t20;
303
- $[50] = t21;
304
- } else t21 = $[50];
305
- return t21;
295
+ $[46] = t18;
296
+ $[47] = t19;
297
+ $[48] = t20;
298
+ $[49] = t21;
299
+ $[50] = t22;
300
+ } else t22 = $[50];
301
+ return t22;
306
302
  };
307
303
  function _temp(prev) {
308
304
  return prev.size === 0 ? prev : /* @__PURE__ */ new Set();
@@ -177,9 +177,15 @@ const ContentCollection = (t0) => {
177
177
  itemId: record.id
178
178
  }),
179
179
  className: "focus-visible:bg-accent flex min-w-0 flex-1 items-center gap-2 px-4 py-3 text-sm",
180
- children: [/* @__PURE__ */ jsx("span", {
181
- className: "truncate",
182
- children: record.label || "Untitled item"
180
+ children: [/* @__PURE__ */ jsxs("span", {
181
+ className: "flex min-w-0 flex-col items-start text-left",
182
+ children: [/* @__PURE__ */ jsx("span", {
183
+ className: "max-w-full truncate",
184
+ children: record.title || "Untitled item"
185
+ }), record.description && /* @__PURE__ */ jsx("span", {
186
+ className: "text-muted-foreground max-w-full truncate text-xs",
187
+ children: record.description
188
+ })]
183
189
  }), record.duplicatePageValue && /* @__PURE__ */ jsx("span", {
184
190
  className: "text-destructive shrink-0 text-xs",
185
191
  title: "Another item's page has the same URL. Change one of them before publishing.",
@@ -189,7 +195,7 @@ const ContentCollection = (t0) => {
189
195
  projectUuid,
190
196
  collectionId: collection.collectionId,
191
197
  id: record.id,
192
- label: record.label,
198
+ label: record.title,
193
199
  compact: true
194
200
  })]
195
201
  }, record.id)), /* @__PURE__ */ jsx("li", {
@@ -244,9 +250,9 @@ const ContentCollection = (t0) => {
244
250
  return t14;
245
251
  };
246
252
  const fieldLabel = (name, schema) => schema.title ?? name.replace(/([A-Z])/g, " $1").replace(/^./, (letter) => letter.toUpperCase());
247
- function CollectionItemForm({ contentSchema, label, projectUuid, collectionId, record, initialContent, onSaved }) {
253
+ function CollectionItemForm({ contentSchema, toTitle, projectUuid, collectionId, record, initialContent, onSaved }) {
248
254
  const studioCache = useStudioCache();
249
- const fields = collectionFormFields(contentSchema, label);
255
+ const fields = collectionFormFields(contentSchema, toTitle);
250
256
  const [savedRecord, setSavedRecord] = useState(record);
251
257
  const [error, setError] = useState(null);
252
258
  const create = useMutation(collectionMutations.create());
@@ -502,7 +508,7 @@ const ContentCollectionItemEditor = (t0) => {
502
508
  if ($[20] !== collectionId || $[21] !== definition || $[22] !== initialContent || $[23] !== itemId || $[24] !== onSaved || $[25] !== projectUuid || $[26] !== recordQuery) {
503
509
  t9 = definition && (!itemId || recordQuery.data) && /* @__PURE__ */ jsx(CollectionItemForm, {
504
510
  contentSchema: definition.contentSchema,
505
- label: definition.label,
511
+ toTitle: definition.toTitle,
506
512
  projectUuid,
507
513
  collectionId,
508
514
  record: itemId ? recordQuery.data : void 0,
@@ -1,8 +1,12 @@
1
1
  import { isLexicalState, lexicalStateToPlainText } from "../../core/lib/lexicalState.js";
2
+ import { titleField } from "../../core/recordItem.js";
2
3
 
3
4
  //#region src/features/content/collection-form.ts
4
- function collectionFormFields(contentSchema, label) {
5
- return Object.entries(contentSchema.properties).sort(([a], [b]) => Number(b === label) - Number(a === label));
5
+ /** The form's fields, the one the record's title starts with first. */
6
+ function collectionFormFields(contentSchema, toTitle) {
7
+ const schema = contentSchema;
8
+ const first = titleField(toTitle);
9
+ return Object.entries(schema.properties).sort(([a], [b]) => Number(b === first) - Number(a === first));
6
10
  }
7
11
  function collectionFormDefaults(fields, content = {}) {
8
12
  return Object.fromEntries(fields.map(([name, schema]) => {
@@ -144,7 +144,7 @@ function missingGroups(missing, collections) {
144
144
  entity: "record",
145
145
  id: record.id,
146
146
  collectionId: record.collectionId,
147
- name: record.label || "Untitled item"
147
+ name: record.title || "Untitled item"
148
148
  }))
149
149
  });
150
150
  }
@@ -0,0 +1,89 @@
1
+ import { c } from "react/compiler-runtime";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+
4
+ //#region src/features/og/pageImage.tsx
5
+ /** The size every page image renders at, which the page head states. */
6
+ const PAGE_IMAGE_SIZE = {
7
+ width: 1200,
8
+ height: 630
9
+ };
10
+ /**
11
+ * A page's image, chosen in order: the editor's custom image, else `/og` for the page, which
12
+ * renders its layout's `toImage` or Camox's default image. `/og` names the page by its path,
13
+ * with its locale's prefix.
14
+ */
15
+ function pageImageUrl({ origin, path, customImageUrl }) {
16
+ return customImageUrl || `${origin}/og?${new URLSearchParams({ path }).toString()}`;
17
+ }
18
+ /** Camox's default image, for a page whose layout has no `toImage`: its title and project. */
19
+ function DefaultPageImage(t0) {
20
+ const $ = c(9);
21
+ const { page, project } = t0;
22
+ let t1;
23
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
24
+ t1 = {
25
+ display: "flex",
26
+ flexDirection: "column",
27
+ justifyContent: "center",
28
+ width: "100%",
29
+ height: "100%",
30
+ backgroundColor: "#09090b",
31
+ padding: "60px 80px"
32
+ };
33
+ $[0] = t1;
34
+ } else t1 = $[0];
35
+ let t2;
36
+ if ($[1] !== project.name) {
37
+ t2 = project.name && /* @__PURE__ */ jsx("div", {
38
+ style: {
39
+ fontSize: 24,
40
+ color: "#a1a1aa",
41
+ marginBottom: 24,
42
+ textTransform: "uppercase",
43
+ letterSpacing: "0.1em"
44
+ },
45
+ children: project.name
46
+ });
47
+ $[1] = project.name;
48
+ $[2] = t2;
49
+ } else t2 = $[2];
50
+ let t3;
51
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
52
+ t3 = {
53
+ fontSize: 64,
54
+ fontWeight: 700,
55
+ color: "#fafafa",
56
+ lineHeight: 1.2
57
+ };
58
+ $[3] = t3;
59
+ } else t3 = $[3];
60
+ let t4;
61
+ if ($[4] !== page.title) {
62
+ t4 = /* @__PURE__ */ jsx("div", {
63
+ style: t3,
64
+ children: page.title
65
+ });
66
+ $[4] = page.title;
67
+ $[5] = t4;
68
+ } else t4 = $[5];
69
+ let t5;
70
+ if ($[6] !== t2 || $[7] !== t4) {
71
+ t5 = /* @__PURE__ */ jsxs("div", {
72
+ style: t1,
73
+ children: [t2, t4]
74
+ });
75
+ $[6] = t2;
76
+ $[7] = t4;
77
+ $[8] = t5;
78
+ } else t5 = $[8];
79
+ return t5;
80
+ }
81
+ /** Renders a page image as a PNG response. Server only. */
82
+ async function renderPageImage(element) {
83
+ if (import.meta.env?.SSR === false) throw new Error("OG images can only be rendered on the server.");
84
+ const { ImageResponse } = await import("camox/_internal/imageResponse");
85
+ return new ImageResponse(element, PAGE_IMAGE_SIZE);
86
+ }
87
+
88
+ //#endregion
89
+ export { DefaultPageImage, PAGE_IMAGE_SIZE, pageImageUrl, renderPageImage };
@@ -1,8 +1,10 @@
1
1
  import { useProjectUuid } from "../../lib/auth.js";
2
+ import { useViewLocale } from "../../lib/view-locale-context.js";
2
3
  import { useCamoxApp } from "../provider/components/CamoxAppContext.js";
3
4
  import { NormalizedDataProvider, usePageBlocks } from "../../lib/normalized-data.js";
4
5
  import { reads, useRead } from "../../lib/studio-reads.js";
5
6
  import { BlockErrorBoundary } from "../preview/components/BlockErrorBoundary.js";
7
+ import { curatedLayoutContext } from "../../core/layoutContext.js";
6
8
  import { useLocalePath } from "../../lib/view-locale.js";
7
9
  import { SharedLayoutContent } from "./SharedLayoutContent.js";
8
10
  import { c } from "react/compiler-runtime";
@@ -84,7 +86,7 @@ function PageBlock(t0) {
84
86
  }
85
87
  /** A curated page as visitors see it, without editing. */
86
88
  function CuratedPageContent() {
87
- const $ = c(19);
89
+ const $ = c(23);
88
90
  const pathname = useLocalePath();
89
91
  const projectUuid = useProjectUuid();
90
92
  const camoxApp = useCamoxApp();
@@ -108,49 +110,60 @@ function CuratedPageContent() {
108
110
  $[5] = t1;
109
111
  } else t1 = $[5];
110
112
  const layout = t1;
113
+ const { locale } = useViewLocale();
111
114
  let t2;
112
- if ($[6] !== pageBlocks) {
113
- t2 = pageBlocks.map(_temp);
114
- $[6] = pageBlocks;
115
- $[7] = t2;
116
- } else t2 = $[7];
117
- const pageBlocksContent = t2;
115
+ if ($[6] !== locale || $[7] !== pageData) {
116
+ t2 = curatedLayoutContext(pageData, locale);
117
+ $[6] = locale;
118
+ $[7] = pageData;
119
+ $[8] = t2;
120
+ } else t2 = $[8];
121
+ const context = t2;
122
+ let t3;
123
+ if ($[9] !== pageBlocks) {
124
+ t3 = pageBlocks.map(_temp);
125
+ $[9] = pageBlocks;
126
+ $[10] = t3;
127
+ } else t3 = $[10];
128
+ const pageBlocksContent = t3;
118
129
  if (!layout) {
119
- let t3;
120
- if ($[8] !== pageBlocksContent) {
121
- t3 = /* @__PURE__ */ jsx("main", {
130
+ let t4;
131
+ if ($[11] !== pageBlocksContent) {
132
+ t4 = /* @__PURE__ */ jsx("main", {
122
133
  className: "flex min-h-screen flex-col",
123
134
  children: pageBlocksContent
124
135
  });
125
- $[8] = pageBlocksContent;
126
- $[9] = t3;
127
- } else t3 = $[9];
128
- return t3;
136
+ $[11] = pageBlocksContent;
137
+ $[12] = t4;
138
+ } else t4 = $[12];
139
+ return t4;
129
140
  }
130
- let t3;
131
- if ($[10] !== afterBlocks || $[11] !== beforeBlocks) {
132
- t3 = [...beforeBlocks, ...afterBlocks];
133
- $[10] = afterBlocks;
134
- $[11] = beforeBlocks;
135
- $[12] = t3;
136
- } else t3 = $[12];
137
141
  let t4;
138
- if ($[13] !== layout || $[14] !== layoutFiles || $[15] !== layoutItems || $[16] !== pageBlocksContent || $[17] !== t3) {
139
- t4 = /* @__PURE__ */ jsx(SharedLayoutContent, {
142
+ if ($[13] !== afterBlocks || $[14] !== beforeBlocks) {
143
+ t4 = [...beforeBlocks, ...afterBlocks];
144
+ $[13] = afterBlocks;
145
+ $[14] = beforeBlocks;
146
+ $[15] = t4;
147
+ } else t4 = $[15];
148
+ let t5;
149
+ if ($[16] !== context || $[17] !== layout || $[18] !== layoutFiles || $[19] !== layoutItems || $[20] !== pageBlocksContent || $[21] !== t4) {
150
+ t5 = /* @__PURE__ */ jsx(SharedLayoutContent, {
140
151
  layout,
141
- blocks: t3,
152
+ context,
153
+ blocks: t4,
142
154
  files: layoutFiles,
143
155
  repeatableItems: layoutItems,
144
156
  children: pageBlocksContent
145
157
  });
146
- $[13] = layout;
147
- $[14] = layoutFiles;
148
- $[15] = layoutItems;
149
- $[16] = pageBlocksContent;
150
- $[17] = t3;
151
- $[18] = t4;
152
- } else t4 = $[18];
153
- return t4;
158
+ $[16] = context;
159
+ $[17] = layout;
160
+ $[18] = layoutFiles;
161
+ $[19] = layoutItems;
162
+ $[20] = pageBlocksContent;
163
+ $[21] = t4;
164
+ $[22] = t5;
165
+ } else t5 = $[22];
166
+ return t5;
154
167
  }
155
168
  function _temp(block) {
156
169
  return /* @__PURE__ */ jsx(BlockErrorBoundary, {
@@ -1,4 +1,7 @@
1
+ import { useViewLocale } from "../../lib/view-locale-context.js";
1
2
  import { usePageBlocks } from "../../lib/normalized-data.js";
3
+ import { asCollectionItem, recordPageValues } from "../../core/recordItem.js";
4
+ import { buildLayoutContext } from "../../core/layoutContext.js";
2
5
  import { SharedLayoutContent } from "./SharedLayoutContent.js";
3
6
  import { c } from "react/compiler-runtime";
4
7
  import { jsx } from "react/jsx-runtime";
@@ -6,7 +9,7 @@ import { jsx } from "react/jsx-runtime";
6
9
  //#region src/features/page/DerivedPageContent.tsx
7
10
  const EMPTY_PAGE = { blockIds: [] };
8
11
  function DerivedPageContent(t0) {
9
- const $ = c(14);
12
+ const $ = c(18);
10
13
  const { camoxApp, derived } = t0;
11
14
  let t1;
12
15
  if ($[0] !== derived.layout) {
@@ -18,38 +21,62 @@ function DerivedPageContent(t0) {
18
21
  $[1] = t1;
19
22
  } else t1 = $[1];
20
23
  const { beforeBlocks, afterBlocks, layoutFiles, layoutItems } = usePageBlocks(t1);
24
+ const { locale } = useViewLocale();
25
+ let layout;
26
+ let result;
21
27
  let t2;
22
- if ($[2] !== camoxApp || $[3] !== derived.layoutId) {
23
- t2 = camoxApp.getLayoutById(derived.layoutId);
28
+ if ($[2] !== camoxApp || $[3] !== derived || $[4] !== locale) {
29
+ layout = camoxApp.getLayoutById(derived.layoutId);
30
+ if (!layout) throw new Error(`Unknown derived layout: ${derived.layoutId}`);
31
+ const { page, result: t3 } = derived;
32
+ result = t3;
33
+ const record = asCollectionItem(result);
34
+ const collection = record && camoxApp.getCollectionById(record.collectionId);
35
+ t2 = buildLayoutContext(layout._internal, {
36
+ path: page?.path ?? "",
37
+ locale: locale.tag,
38
+ projectName: page?.projectName ?? "",
39
+ data: result?.data,
40
+ params: page?.params,
41
+ record: record && collection ? recordPageValues(collection, record) : void 0
42
+ });
24
43
  $[2] = camoxApp;
25
- $[3] = derived.layoutId;
26
- $[4] = t2;
27
- } else t2 = $[4];
28
- const layout = t2;
29
- if (!layout) throw new Error(`Unknown derived layout: ${derived.layoutId}`);
44
+ $[3] = derived;
45
+ $[4] = locale;
46
+ $[5] = layout;
47
+ $[6] = result;
48
+ $[7] = t2;
49
+ } else {
50
+ layout = $[5];
51
+ result = $[6];
52
+ t2 = $[7];
53
+ }
54
+ const context = t2;
30
55
  let t3;
31
- if ($[5] !== afterBlocks || $[6] !== beforeBlocks) {
56
+ if ($[8] !== afterBlocks || $[9] !== beforeBlocks) {
32
57
  t3 = [...beforeBlocks, ...afterBlocks];
33
- $[5] = afterBlocks;
34
- $[6] = beforeBlocks;
35
- $[7] = t3;
36
- } else t3 = $[7];
58
+ $[8] = afterBlocks;
59
+ $[9] = beforeBlocks;
60
+ $[10] = t3;
61
+ } else t3 = $[10];
37
62
  let t4;
38
- if ($[8] !== derived.result || $[9] !== layout || $[10] !== layoutFiles || $[11] !== layoutItems || $[12] !== t3) {
63
+ if ($[11] !== context || $[12] !== layout || $[13] !== layoutFiles || $[14] !== layoutItems || $[15] !== result || $[16] !== t3) {
39
64
  t4 = /* @__PURE__ */ jsx(SharedLayoutContent, {
40
65
  layout,
41
- result: derived.result,
66
+ context,
67
+ result,
42
68
  blocks: t3,
43
69
  files: layoutFiles,
44
70
  repeatableItems: layoutItems
45
71
  });
46
- $[8] = derived.result;
47
- $[9] = layout;
48
- $[10] = layoutFiles;
49
- $[11] = layoutItems;
50
- $[12] = t3;
51
- $[13] = t4;
52
- } else t4 = $[13];
72
+ $[11] = context;
73
+ $[12] = layout;
74
+ $[13] = layoutFiles;
75
+ $[14] = layoutItems;
76
+ $[15] = result;
77
+ $[16] = t3;
78
+ $[17] = t4;
79
+ } else t4 = $[17];
53
80
  return t4;
54
81
  }
55
82