camox 0.40.0 → 0.41.1

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 (108) hide show
  1. package/dist/components/PlatformLabel.js +62 -0
  2. package/dist/core/components/lexical/GradientNode.js +126 -0
  3. package/dist/core/components/lexical/InlineLexicalEditor.js +37 -56
  4. package/dist/core/components/lexical/InlineStylesPlugin.js +183 -0
  5. package/dist/core/components/lexical/SelectionBroadcaster.js +49 -46
  6. package/dist/core/components/lexical/SidebarLexicalEditor.js +197 -151
  7. package/dist/core/components/lexical/editorConfig.js +25 -7
  8. package/dist/core/components/lexical/selectTextLink.js +21 -0
  9. package/dist/core/createApp.d.ts +30 -17
  10. package/dist/core/createApp.js +14 -3
  11. package/dist/core/createBlock.d.ts +2 -2
  12. package/dist/core/createBlock.js +17 -28
  13. package/dist/core/createLayout.d.ts +48 -4
  14. package/dist/core/createLayout.js +51 -23
  15. package/dist/core/derivedRoutes.js +36 -0
  16. package/dist/core/editing/BlockEditingRuntime.js +4 -4
  17. package/dist/core/editing/CompleteBlockEditingRuntime.js +5 -5
  18. package/dist/core/editing/createEditableBlock.d.ts +9 -8
  19. package/dist/core/editing/createEditableBlock.js +265 -403
  20. package/dist/core/hooks/useIsEditable.js +4 -7
  21. package/dist/core/hooks/useRequireDraftSource.js +7 -11
  22. package/dist/core/lib/lexicalReact.d.ts +1 -26
  23. package/dist/core/lib/lexicalReact.js +46 -60
  24. package/dist/core/lib/lexicalState.js +164 -81
  25. package/dist/core/lib/modifierFormats.js +4 -15
  26. package/dist/core/lib/textStyles.d.ts +27 -0
  27. package/dist/core/lib/textStyles.js +47 -0
  28. package/dist/features/navigation/navigation.d.ts +2 -0
  29. package/dist/features/navigation/navigation.js +51 -46
  30. package/dist/features/page/DerivedPageContent.js +61 -0
  31. package/dist/features/page/PublishedPageContent.js +52 -83
  32. package/dist/features/page/SharedLayoutContent.js +66 -0
  33. package/dist/features/preview/CamoxPreview.d.ts +16 -1
  34. package/dist/features/preview/CamoxPreview.js +246 -290
  35. package/dist/features/preview/DerivedPreview.js +134 -0
  36. package/dist/features/preview/EditablePageExperience.js +25 -8
  37. package/dist/features/preview/components/AddBlockSidebar.js +43 -33
  38. package/dist/features/preview/components/AssetLightbox.js +1 -1
  39. package/dist/features/preview/components/BlockActionsPopover.js +8 -8
  40. package/dist/features/preview/components/BlockThumbnail.js +192 -0
  41. package/dist/features/preview/components/CreatePageModal.js +123 -115
  42. package/dist/features/preview/components/DerivedLayoutDialog.js +100 -0
  43. package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +5 -0
  44. package/dist/features/preview/components/DerivedLayoutSidebar.js +160 -0
  45. package/dist/features/preview/components/DerivedPageInfoSidebar.js +57 -0
  46. package/dist/features/preview/components/FieldToolbar.js +42 -20
  47. package/dist/features/preview/components/Frame.js +133 -140
  48. package/dist/features/preview/components/LeftSidebar.js +42 -38
  49. package/dist/features/preview/components/LinkFieldEditor.js +3 -4
  50. package/dist/features/preview/components/MobilePreviewDrawer.js +4 -4
  51. package/dist/features/preview/components/Overlays.js +59 -47
  52. package/dist/features/preview/components/PageEditorSidebar.js +194 -203
  53. package/dist/features/preview/components/PageInfoSidebar.js +63 -55
  54. package/dist/features/preview/components/PageNavigatorSidebar.js +34 -483
  55. package/dist/features/preview/components/PagePicker.js +205 -153
  56. package/dist/features/preview/components/PageTree.js +366 -340
  57. package/dist/features/preview/components/PreviewActivation.js +78 -0
  58. package/dist/features/preview/components/PreviewPanel.js +98 -68
  59. package/dist/features/preview/components/PreviewToolbar.js +224 -241
  60. package/dist/features/preview/components/PreviewToolbarSpacer.js +43 -0
  61. package/dist/features/preview/components/PublicationRow.js +425 -0
  62. package/dist/features/preview/components/PublishDialog.js +180 -97
  63. package/dist/features/preview/components/RightSidebar.js +19 -34
  64. package/dist/features/preview/components/previewStyles.js +8 -0
  65. package/dist/features/preview/components/useUpdateBlockPosition.js +7 -13
  66. package/dist/features/preview/previewConstants.js +8 -0
  67. package/dist/features/preview/previewStore.js +18 -39
  68. package/dist/features/preview/publication.js +71 -0
  69. package/dist/features/provider/AuthenticatedCamoxProvider.js +2 -1
  70. package/dist/features/provider/CoreCamoxProvider.js +11 -2
  71. package/dist/features/provider/LocalhostPreviewProvider.js +6 -7
  72. package/dist/features/provider/useAdminShortcuts.js +5 -4
  73. package/dist/features/routes/pageRuntime.d.ts +10 -0
  74. package/dist/features/routes/pageRuntime.js +7 -9
  75. package/dist/features/runtime/PreviewDocumentContext.js +7 -0
  76. package/dist/features/runtime/RuntimeChrome.js +149 -0
  77. package/dist/features/runtime/SharedChromeContext.js +7 -0
  78. package/dist/features/runtime/navigationRequest.js +21 -0
  79. package/dist/features/runtime/navigationTarget.js +23 -0
  80. package/dist/features/runtime/pageApp.js +196 -121
  81. package/dist/features/runtime/pageNavigation.js +79 -130
  82. package/dist/features/runtime/runtime.d.ts +11 -0
  83. package/dist/features/runtime/runtime.js +183 -47
  84. package/dist/features/runtime/studioApp.d.ts +2 -11
  85. package/dist/features/runtime/studioApp.js +14 -177
  86. package/dist/features/studio/CamoxStudio.js +27 -13
  87. package/dist/features/studio/components/Navbar.js +80 -74
  88. package/dist/features/studio/components/UserButton.js +4 -4
  89. package/dist/features/studio/studioTheme.js +25 -0
  90. package/dist/features/studio/useTheme.js +94 -109
  91. package/dist/features/vite/appGeneration.js +12 -3
  92. package/dist/features/vite/definitionsSync.js +4 -2
  93. package/dist/features/vite/layoutGeneration.js +38 -0
  94. package/dist/features/vite/runtimeDev.js +3 -1
  95. package/dist/lib/auth.js +15 -13
  96. package/dist/lib/normalized-data.js +4 -4
  97. package/dist/lib/platform.js +13 -0
  98. package/dist/lib/utils.js +90 -38
  99. package/dist/studio-overlays.css +5 -40
  100. package/dist/studio.css +1 -1
  101. package/package.json +8 -4
  102. package/skills/camox/SKILL.md +2 -0
  103. package/skills/camox/references/block-definitions.md +31 -12
  104. package/skills/camox/references/layout-definitions.md +2 -0
  105. package/dist/core/lib/modifiers.js +0 -24
  106. package/dist/features/preview/components/DraftSwitchDialog.js +0 -66
  107. package/dist/features/preview/previewQueryFns.js +0 -23
  108. package/dist/features/runtime/studioNavigation.js +0 -120
@@ -1,4 +1,7 @@
1
- import { buildCamoxPageHead, createMarkdownResponse, createServerApiClient, isNotFoundError, loadCamoxPageForRequest } from "../routes/pageRuntime.js";
1
+ import { PLATFORM_SCRIPT } from "../../lib/platform.js";
2
+ import { STUDIO_THEME_SCRIPT } from "../studio/studioTheme.js";
3
+ import { matchDerivedLayout } from "../../core/derivedRoutes.js";
4
+ import { buildCamoxPageHead, createMarkdownResponse, createServerApiClient, isAuthSessionError, isNotFoundError, loadCamoxPageForRequest, seedBlockCaches } from "../routes/pageRuntime.js";
2
5
  import { QueryClient, dehydrate } from "@tanstack/react-query";
3
6
  import { createHead, renderSSRHead } from "@unhead/react/server";
4
7
 
@@ -40,8 +43,7 @@ function normalizePagePath(path) {
40
43
  if (!path) return "/";
41
44
  return path.startsWith("/") ? path : `/${path}`;
42
45
  }
43
- function getLayoutIdentity(data, source) {
44
- const layout = data.page.layout;
46
+ function getLayoutIdentity(layout, source) {
45
47
  if (!layout) return null;
46
48
  const version = source === "live" ? String(layout.livePublishedCheckpointId ?? "none") : String(layout.contentUpdatedAt ?? layout.updatedAt);
47
49
  return {
@@ -173,10 +175,15 @@ function createPageHeadInput(head) {
173
175
  link: pageLinks
174
176
  };
175
177
  }
176
- function renderHead(document, pageHead, stylesheetUrl) {
178
+ function renderHead(document, pageHead, stylesheetUrl, studioStylesheetUrl) {
177
179
  const head = createHead();
178
180
  head.push(createDefaultHeadInput(stylesheetUrl));
179
181
  head.push(document);
182
+ if (studioStylesheetUrl) head.push({ link: [{
183
+ rel: "stylesheet",
184
+ href: studioStylesheetUrl,
185
+ "data-camox-studio": "true"
186
+ }] });
180
187
  head.push(pageHead);
181
188
  return renderSSRHead(head);
182
189
  }
@@ -187,6 +194,20 @@ function renderClientEntryScript(src) {
187
194
  if (!src) return "";
188
195
  return `<script type="module" src="${escapeXml(src)}"><\/script>`;
189
196
  }
197
+ async function preparePreviewDocument(input, options, document, content) {
198
+ const siteHead = renderHead(document, createPageHeadInput(input.head ?? {}), options.stylesheetUrl);
199
+ const href = new URL(withRuntimeBasePath(input.pathname, options.runtimeBasePath), input.href).href;
200
+ const siteHtml = content ?? (options.renderPage ? await options.renderPage({
201
+ ...input,
202
+ href,
203
+ presentation: "public",
204
+ previewDocument: void 0
205
+ }) : "");
206
+ input.previewDocument = `<!doctype html><html${siteHead.htmlAttrs}><head><base href="${escapeXml(href)}">${siteHead.headTags}</head><body${siteHead.bodyAttrs}>${siteHead.bodyTagsOpen}<div id="root" data-camox-preview-root>${siteHtml}</div>${siteHead.bodyTags}</body></html>`;
207
+ }
208
+ function renderStudioThemeScript(enabled) {
209
+ return enabled ? `<script data-camox-studio>${STUDIO_THEME_SCRIPT}${PLATFORM_SCRIPT}<\/script>` : "";
210
+ }
190
211
  async function createPageHtmlResponse({ options, pathname, request }) {
191
212
  if (!options.apiUrl || !options.authenticationUrl || !options.projectSlug || !options.renderPage) return createPageDataResponse({
192
213
  options,
@@ -204,31 +225,39 @@ async function createPageHtmlResponse({ options, pathname, request }) {
204
225
  projectSlug: options.projectSlug,
205
226
  queryClient
206
227
  });
207
- const headers = new Headers({ "Content-Type": "text/html; charset=utf-8" });
228
+ const headers = new Headers({
229
+ "Content-Type": "text/html; charset=utf-8",
230
+ Vary: "Cookie"
231
+ });
232
+ if (getServerAuthCookieHeader(request.headers)) headers.set("Cache-Control", "private, no-store");
208
233
  if (result.shouldClearAuthCookie) headers.append("Set-Cookie", buildClearServerAuthCookieHeader());
209
234
  const camoxApp = await options.getCamoxApp?.();
210
235
  const document = await options.getDocument?.() ?? {};
211
236
  const head = camoxApp ? buildCamoxPageHead(camoxApp, result.data) : {};
212
237
  const dehydratedState = dehydrate(queryClient);
213
238
  const pageRenderInput = {
239
+ project: result.source === "draft" ? await createServerApiClient(options.apiUrl, options.environmentName, { authCookieHeader: getServerAuthCookieHeader(request.headers) }).projects.getBySlug({ slug: options.projectSlug }) : void 0,
240
+ presentation: result.source === "draft" ? "studio" : "public",
214
241
  apiUrl: options.apiUrl,
215
242
  authenticationUrl: options.authenticationUrl,
216
243
  dehydratedState,
217
244
  environmentName: options.environmentName,
218
245
  head,
219
246
  href: new URL(request.url).href,
220
- layoutIdentity: getLayoutIdentity(result.data, result.source),
247
+ layoutIdentity: getLayoutIdentity(result.data.page.layout, result.source),
221
248
  loaderData: result.data,
222
249
  pathname,
223
250
  projectSlug: options.projectSlug,
224
251
  runtimeBasePath: normalizeRuntimeBasePath(options.runtimeBasePath),
225
252
  source: result.source
226
253
  };
254
+ await preparePreviewDocument(pageRenderInput, options, document, result.source === "live" ? "" : void 0);
227
255
  const appHtml = await options.renderPage(pageRenderInput);
228
- const renderedHead = renderHead(document, createPageHeadInput(head), options.stylesheetUrl);
256
+ const renderedHead = renderHead(result.source === "draft" ? {} : document, createPageHeadInput(head), result.source === "draft" ? void 0 : options.stylesheetUrl, result.source === "draft" ? options.studioStylesheetUrl : void 0);
229
257
  return new Response(`<!doctype html>
230
258
  <html${renderedHead.htmlAttrs}>
231
259
  <head>
260
+ ${renderStudioThemeScript(result.source === "draft")}
232
261
  ${renderedHead.headTags}
233
262
  <script id="__CAMOX_DATA__" type="application/json">${serializeJsonForHtml(pageRenderInput)}<\/script>
234
263
  </head>
@@ -240,18 +269,102 @@ ${renderClientEntryScript(options.pageClientEntryUrl)}
240
269
  </body>
241
270
  </html>`, { headers });
242
271
  } catch (error) {
243
- if (isNotFoundError(error)) return new Response("No Camox page on this path", { status: 404 });
272
+ if (isNotFoundError(error)) {
273
+ const derived = await createDerivedResponse(options, pathname, request);
274
+ if (derived) return derived;
275
+ return new Response("No Camox page on this path", { status: 404 });
276
+ }
277
+ throw error;
278
+ }
279
+ }
280
+ async function createDerivedResponse(options, pathname, request, dataOnly = false) {
281
+ const app = await options.getCamoxApp?.();
282
+ const match = app && matchDerivedLayout(app.getLayouts(), pathname);
283
+ if (!match || !dataOnly && !options.renderPage) return null;
284
+ try {
285
+ const data = await match.layout._internal.loader({ params: match.params });
286
+ const serializedData = JSON.parse(JSON.stringify(data));
287
+ const queryClient = new QueryClient();
288
+ const authCookieHeader = getServerAuthCookieHeader(request.headers);
289
+ let source = authCookieHeader ? "draft" : "live";
290
+ let clearAuthCookie = false;
291
+ const loadLayout = (source) => createServerApiClient(options.apiUrl, options.environmentName, { authCookieHeader: source === "draft" ? authCookieHeader : void 0 }).layouts.get({
292
+ projectSlug: options.projectSlug,
293
+ layoutId: match.layout._internal.id,
294
+ source
295
+ });
296
+ const shared = await loadLayout(source).catch(async (error) => {
297
+ if (source !== "draft" || !isAuthSessionError(error)) throw error;
298
+ source = "live";
299
+ clearAuthCookie = true;
300
+ return loadLayout("live");
301
+ });
302
+ seedBlockCaches(queryClient, shared, source);
303
+ const input = {
304
+ project: source === "draft" ? await createServerApiClient(options.apiUrl, options.environmentName, { authCookieHeader }).projects.getBySlug({ slug: options.projectSlug }) : void 0,
305
+ presentation: source === "draft" ? "studio" : "public",
306
+ apiUrl: options.apiUrl,
307
+ authenticationUrl: options.authenticationUrl,
308
+ projectSlug: options.projectSlug,
309
+ environmentName: options.environmentName,
310
+ dehydratedState: dehydrate(queryClient),
311
+ head: { meta: [{ title: match.layout._internal.title }] },
312
+ href: request.url,
313
+ layoutIdentity: getLayoutIdentity(shared.layout, source),
314
+ loaderData: null,
315
+ pathname,
316
+ runtimeBasePath: normalizeRuntimeBasePath(options.runtimeBasePath),
317
+ source,
318
+ derived: {
319
+ layoutId: match.layout._internal.id,
320
+ data: serializedData,
321
+ layout: shared.layout
322
+ }
323
+ };
324
+ const document = await options.getDocument?.() ?? {};
325
+ await preparePreviewDocument(input, options, document, source === "live" && !dataOnly ? "" : void 0);
326
+ if (dataOnly) return Response.json(input, { headers: {
327
+ "Cache-Control": "private, no-store",
328
+ Vary: "Cookie",
329
+ ...clearAuthCookie ? { "Set-Cookie": buildClearServerAuthCookieHeader() } : {}
330
+ } });
331
+ const html = await options.renderPage(input);
332
+ const head = renderHead(source === "draft" ? {} : document, { title: match.layout._internal.title }, source === "draft" ? void 0 : options.stylesheetUrl, source === "draft" ? options.studioStylesheetUrl : void 0);
333
+ return new Response(`<!doctype html><html${head.htmlAttrs}><head>${renderStudioThemeScript(source === "draft")}${head.headTags}<script id="__CAMOX_DATA__" type="application/json">${serializeJsonForHtml(input)}<\/script></head><body${head.bodyAttrs}>${head.bodyTagsOpen}<div id="root">${html}</div>${head.bodyTags}${renderClientEntryScript(options.pageClientEntryUrl)}</body></html>`, { headers: {
334
+ "Content-Type": "text/html; charset=utf-8",
335
+ Vary: "Cookie",
336
+ ...authCookieHeader ? { "Cache-Control": "private, no-store" } : {},
337
+ ...clearAuthCookie ? { "Set-Cookie": buildClearServerAuthCookieHeader() } : {}
338
+ } });
339
+ } catch (error) {
340
+ if (isNotFoundError(error)) return new Response("Page not found", { status: 404 });
244
341
  throw error;
245
342
  }
246
343
  }
247
- async function createStudioHtmlResponse({ match, options, request }) {
248
- if (!options.apiUrl || !options.authenticationUrl || !options.projectSlug || !options.renderStudio) return Response.json({
344
+ async function createStudioHtmlResponse({ match, options, request, dataOnly = false }) {
345
+ if (!options.apiUrl || !options.authenticationUrl || !options.projectSlug || !dataOnly && !options.renderStudio) return Response.json({
249
346
  message: "Camox Studio renderer is not configured.",
250
347
  ...match
251
348
  }, { status: 500 });
252
349
  const queryClient = new QueryClient();
253
- const document = await options.getDocument?.() ?? {};
350
+ const authCookieHeader = getServerAuthCookieHeader(request.headers);
351
+ let project;
352
+ let presentation = "public";
353
+ let clearAuthCookie = false;
354
+ if (authCookieHeader) try {
355
+ project = await createServerApiClient(options.apiUrl, options.environmentName, { authCookieHeader }).projects.getBySlug({ slug: options.projectSlug });
356
+ presentation = "studio";
357
+ } catch (error) {
358
+ if (!isAuthSessionError(error)) throw error;
359
+ clearAuthCookie = true;
360
+ }
254
361
  const studioRenderInput = {
362
+ project,
363
+ source: "live",
364
+ head: { meta: [{ title: "Camox Studio" }] },
365
+ layoutIdentity: null,
366
+ loaderData: null,
367
+ presentation,
255
368
  apiUrl: options.apiUrl,
256
369
  authenticationUrl: options.authenticationUrl,
257
370
  dehydratedState: dehydrate(queryClient),
@@ -262,11 +375,17 @@ async function createStudioHtmlResponse({ match, options, request }) {
262
375
  routeKind: match.kind,
263
376
  runtimeBasePath: normalizeRuntimeBasePath(options.runtimeBasePath)
264
377
  };
378
+ if (dataOnly) return Response.json(studioRenderInput, { headers: {
379
+ "Cache-Control": "private, no-store",
380
+ Vary: "Cookie",
381
+ ...clearAuthCookie ? { "Set-Cookie": buildClearServerAuthCookieHeader() } : {}
382
+ } });
265
383
  const appHtml = await options.renderStudio(studioRenderInput);
266
- const renderedHead = renderHead(document, {}, options.stylesheetUrl);
384
+ const renderedHead = renderHead({}, { title: "Camox Studio" }, void 0, presentation === "studio" ? options.studioStylesheetUrl : void 0);
267
385
  return new Response(`<!doctype html>
268
386
  <html${renderedHead.htmlAttrs}>
269
387
  <head>
388
+ ${renderStudioThemeScript(presentation === "studio")}
270
389
  ${renderedHead.headTags}
271
390
  <script id="__CAMOX_DATA__" type="application/json">${serializeJsonForHtml(studioRenderInput)}<\/script>
272
391
  </head>
@@ -276,7 +395,12 @@ ${renderedHead.bodyTagsOpen}
276
395
  ${renderedHead.bodyTags}
277
396
  ${renderClientEntryScript(options.studioClientEntryUrl)}
278
397
  </body>
279
- </html>`, { headers: { "Content-Type": "text/html; charset=utf-8" } });
398
+ </html>`, { headers: {
399
+ "Content-Type": "text/html; charset=utf-8",
400
+ "Cache-Control": "private, no-store",
401
+ Vary: "Cookie",
402
+ ...clearAuthCookie ? { "Set-Cookie": buildClearServerAuthCookieHeader() } : {}
403
+ } });
280
404
  }
281
405
  async function createPageDataResponse({ options, pathname, request }) {
282
406
  if (!options.apiUrl || !options.projectSlug) return Response.json({ message: "Camox page loader is not configured." }, { status: 500 });
@@ -291,43 +415,44 @@ async function createPageDataResponse({ options, pathname, request }) {
291
415
  projectSlug: options.projectSlug,
292
416
  queryClient
293
417
  });
294
- const headers = new Headers({ "Content-Type": "application/json; charset=utf-8" });
418
+ const headers = new Headers({
419
+ "Content-Type": "application/json; charset=utf-8",
420
+ "Cache-Control": "private, no-store",
421
+ Vary: "Cookie"
422
+ });
295
423
  if (result.shouldClearAuthCookie) headers.append("Set-Cookie", buildClearServerAuthCookieHeader());
296
424
  const camoxApp = await options.getCamoxApp?.();
297
425
  const head = camoxApp ? buildCamoxPageHead(camoxApp, result.data) : null;
298
426
  const dehydratedState = dehydrate(queryClient);
299
- const { page, layout, projectName, project } = result.data.page;
300
- return new Response(`${JSON.stringify({
301
- kind: "page",
427
+ const project = result.source === "draft" ? await createServerApiClient(options.apiUrl, options.environmentName, { authCookieHeader: getServerAuthCookieHeader(request.headers) }).projects.getBySlug({ slug: options.projectSlug }) : void 0;
428
+ const input = {
429
+ apiUrl: options.apiUrl,
430
+ authenticationUrl: options.authenticationUrl ?? "",
431
+ environmentName: options.environmentName,
432
+ projectSlug: options.projectSlug,
433
+ runtimeBasePath: normalizeRuntimeBasePath(options.runtimeBasePath),
434
+ href: request.url,
435
+ presentation: result.source === "draft" ? "studio" : "public",
436
+ source: result.source,
302
437
  pathname,
303
- page: {
304
- id: page.id,
305
- fullPath: page.fullPath,
306
- metaTitle: page.metaTitle,
307
- metaDescription: page.metaDescription
308
- },
309
- layout: layout ? {
310
- id: layout.id,
311
- layoutId: layout.layoutId
312
- } : null,
313
- layoutIdentity: getLayoutIdentity(result.data, result.source),
314
- project: {
315
- id: project.id,
316
- name: projectName
317
- },
318
- loaderData: {
319
- faviconUrl: result.data.faviconUrl,
320
- origin: result.data.origin
321
- },
438
+ layoutIdentity: getLayoutIdentity(result.data.page.layout, result.source),
439
+ project,
440
+ loaderData: result.data,
322
441
  head,
323
442
  dehydratedState
324
- }, null, 2)}\n`, { headers });
443
+ };
444
+ await preparePreviewDocument(input, options, await options.getDocument?.() ?? {});
445
+ return Response.json(input, { headers });
325
446
  } catch (error) {
326
- if (isNotFoundError(error)) return Response.json({
327
- kind: "page",
328
- message: "No Camox page on this path",
329
- pathname
330
- }, { status: 404 });
447
+ if (isNotFoundError(error)) {
448
+ const derived = await createDerivedResponse(options, pathname, request, true);
449
+ if (derived) return derived;
450
+ return Response.json({
451
+ kind: "page",
452
+ message: "No Camox page on this path",
453
+ pathname
454
+ }, { status: 404 });
455
+ }
331
456
  throw error;
332
457
  }
333
458
  }
@@ -346,11 +471,22 @@ async function handleCamoxRequest(request, options = {}) {
346
471
  }
347
472
  if (match.kind === "og") return createOgResponse(request, options);
348
473
  if (match.kind === "sitemap") return createSitemapResponse(request, options);
349
- if (match.kind === "data") return createPageDataResponse({
350
- options,
351
- pathname: normalizePagePath(new URL(request.url).searchParams.get("path")),
352
- request
353
- });
474
+ if (match.kind === "data") {
475
+ const targetPathname = normalizePagePath(new URL(request.url).searchParams.get("path"));
476
+ const targetMatch = matchRuntimeRoute(targetPathname);
477
+ if (targetMatch.kind.startsWith("studio")) return createStudioHtmlResponse({
478
+ match: targetMatch,
479
+ options,
480
+ request,
481
+ dataOnly: true
482
+ });
483
+ if (targetMatch.kind !== "page") return new Response("Not found", { status: 404 });
484
+ return createPageDataResponse({
485
+ options,
486
+ pathname: targetPathname,
487
+ request
488
+ });
489
+ }
354
490
  if (match.kind === "studio" || match.kind === "studio-content" || match.kind === "studio-nested") return createStudioHtmlResponse({
355
491
  match,
356
492
  options,
@@ -1,15 +1,6 @@
1
+ import { PageRenderInput } from "./runtime.js";
1
2
  import { QueryClient } from "@tanstack/react-query";
2
3
  //#region src/features/runtime/studioApp.d.ts
3
- interface StudioRenderInput {
4
- apiUrl: string;
5
- authenticationUrl: string;
6
- dehydratedState: unknown;
7
- environmentName?: string;
8
- href: string;
9
- pathname: string;
10
- projectSlug: string;
11
- routeKind: "studio" | "studio-content" | "studio-nested";
12
- runtimeBasePath: string;
13
- }
4
+ type StudioRenderInput = PageRenderInput;
14
5
  //#endregion
15
6
  export { StudioRenderInput };
@@ -1,184 +1,21 @@
1
- import { useSignInRedirect } from "../../lib/auth.js";
2
- import { STUDIO_CONTENT_PATH } from "../studio/routes.js";
3
- import { CamoxContent } from "../content/CamoxContent.js";
4
- import { AuthGate } from "../../components/AuthGate.js";
5
- import { AuthenticatedCamoxProvider } from "../provider/AuthenticatedCamoxProvider.js";
6
- import { CoreCamoxProvider } from "../provider/CoreCamoxProvider.js";
7
- import { CamoxStudio } from "../studio/CamoxStudio.js";
8
- import { CompleteBlockEditingRuntimeProvider } from "../../core/editing/CompleteBlockEditingRuntime.js";
9
- import { StudioNavigationProvider } from "./studioNavigation.js";
1
+ import { PageApp } from "./pageApp.js";
10
2
  import { c } from "react/compiler-runtime";
11
- import { HydrationBoundary, QueryClientProvider } from "@tanstack/react-query";
12
- import * as React from "react";
13
- import { Fragment, jsx } from "react/jsx-runtime";
3
+ import { jsx } from "react/jsx-runtime";
14
4
 
15
5
  //#region src/features/runtime/studioApp.tsx
16
- function StudioRouteContent(t0) {
17
- const $ = c(4);
18
- if ($[0] !== "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17") {
19
- for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
20
- $[0] = "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17";
6
+ function StudioApp(props) {
7
+ const $ = c(3);
8
+ if ($[0] !== "67c2b6f45da5572f3c7b8c4d68491571a2d455e4cd7067c0380e91fc49b09d93") {
9
+ for (let $i = 0; $i < 3; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
10
+ $[0] = "67c2b6f45da5572f3c7b8c4d68491571a2d455e4cd7067c0380e91fc49b09d93";
21
11
  }
22
- const { input } = t0;
23
- if (input.pathname === STUDIO_CONTENT_PATH) {
24
- let t1;
25
- if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
26
- t1 = /* @__PURE__ */ jsx(CamoxContent, {});
27
- $[1] = t1;
28
- } else t1 = $[1];
29
- return t1;
30
- }
31
- if (input.routeKind === "studio-nested") {
32
- let t1;
33
- if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
34
- t1 = /* @__PURE__ */ jsx("div", {
35
- className: "text-muted-foreground p-6 text-sm",
36
- children: "Studio page not found"
37
- });
38
- $[2] = t1;
39
- } else t1 = $[2];
40
- return t1;
41
- }
42
- let t1;
43
- if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
44
- t1 = /* @__PURE__ */ jsx("div", {
45
- className: "text-muted-foreground p-6 text-sm",
46
- children: "Loading Studio…"
47
- });
48
- $[3] = t1;
49
- } else t1 = $[3];
50
- return t1;
51
- }
52
- function StudioAuthentication(t0) {
53
- const $ = c(6);
54
- if ($[0] !== "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17") {
55
- for (let $i = 0; $i < 6; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
56
- $[0] = "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17";
57
- }
58
- const { children } = t0;
59
- const signInRedirect = useSignInRedirect(_temp);
60
- let t1;
61
- let t2;
62
- if ($[1] !== signInRedirect) {
63
- t1 = () => signInRedirect();
64
- t2 = [signInRedirect];
65
- $[1] = signInRedirect;
66
- $[2] = t1;
67
- $[3] = t2;
68
- } else {
69
- t1 = $[2];
70
- t2 = $[3];
71
- }
72
- React.useEffect(t1, t2);
73
- let t3;
74
- if ($[4] !== children) {
75
- t3 = /* @__PURE__ */ jsx(Fragment, { children });
76
- $[4] = children;
77
- $[5] = t3;
78
- } else t3 = $[5];
79
- return t3;
80
- }
81
- function _temp() {
82
- if (typeof window === "undefined") return;
83
- return new URL("/", window.location.href).href;
84
- }
85
- function StudioAccessGate(t0) {
86
- const $ = c(6);
87
- if ($[0] !== "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17") {
88
- for (let $i = 0; $i < 6; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
89
- $[0] = "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17";
90
- }
91
- const { children } = t0;
92
- let t1;
93
- if ($[1] !== children) {
94
- t1 = /* @__PURE__ */ jsx(AuthenticatedCamoxProvider, { children });
95
- $[1] = children;
96
- $[2] = t1;
97
- } else t1 = $[2];
98
- let t2;
99
- if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
100
- t2 = /* @__PURE__ */ jsx(StudioAuthentication, { children: null });
101
- $[3] = t2;
102
- } else t2 = $[3];
103
- let t3;
104
- if ($[4] !== t1) {
105
- t3 = /* @__PURE__ */ jsx(AuthGate, {
106
- authenticated: t1,
107
- loading: null,
108
- unauthenticated: t2
109
- });
110
- $[4] = t1;
111
- $[5] = t3;
112
- } else t3 = $[5];
113
- return t3;
114
- }
115
- function StudioApp(t0) {
116
- const $ = c(21);
117
- if ($[0] !== "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17") {
118
- for (let $i = 0; $i < 21; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
119
- $[0] = "4c9c648e70d7360e13c0b3d8d0f0633d0ab4263e359229c8802810bae1f42f17";
120
- }
121
- const { camoxApp, input, queryClient } = t0;
122
- const t1 = input.dehydratedState;
123
- let t2;
124
- if ($[1] !== input) {
125
- t2 = /* @__PURE__ */ jsx(StudioAccessGate, { children: /* @__PURE__ */ jsx(CompleteBlockEditingRuntimeProvider, { children: /* @__PURE__ */ jsx(CamoxStudio, { children: /* @__PURE__ */ jsx(StudioRouteContent, { input }) }) }) });
126
- $[1] = input;
127
- $[2] = t2;
128
- } else t2 = $[2];
129
- let t3;
130
- if ($[3] !== camoxApp || $[4] !== input.apiUrl || $[5] !== input.authenticationUrl || $[6] !== input.environmentName || $[7] !== input.projectSlug || $[8] !== t2) {
131
- t3 = /* @__PURE__ */ jsx(CoreCamoxProvider, {
132
- camoxApp,
133
- authenticationUrl: input.authenticationUrl,
134
- apiUrl: input.apiUrl,
135
- projectSlug: input.projectSlug,
136
- environmentName: input.environmentName,
137
- children: t2
138
- });
139
- $[3] = camoxApp;
140
- $[4] = input.apiUrl;
141
- $[5] = input.authenticationUrl;
142
- $[6] = input.environmentName;
143
- $[7] = input.projectSlug;
144
- $[8] = t2;
145
- $[9] = t3;
146
- } else t3 = $[9];
147
- let t4;
148
- if ($[10] !== input.href || $[11] !== input.pathname || $[12] !== input.runtimeBasePath || $[13] !== t3) {
149
- t4 = /* @__PURE__ */ jsx(StudioNavigationProvider, {
150
- href: input.href,
151
- pathname: input.pathname,
152
- runtimeBasePath: input.runtimeBasePath,
153
- children: t3
154
- });
155
- $[10] = input.href;
156
- $[11] = input.pathname;
157
- $[12] = input.runtimeBasePath;
158
- $[13] = t3;
159
- $[14] = t4;
160
- } else t4 = $[14];
161
- let t5;
162
- if ($[15] !== t1 || $[16] !== t4) {
163
- t5 = /* @__PURE__ */ jsx(HydrationBoundary, {
164
- state: t1,
165
- children: t4
166
- });
167
- $[15] = t1;
168
- $[16] = t4;
169
- $[17] = t5;
170
- } else t5 = $[17];
171
- let t6;
172
- if ($[18] !== queryClient || $[19] !== t5) {
173
- t6 = /* @__PURE__ */ jsx(QueryClientProvider, {
174
- client: queryClient,
175
- children: t5
176
- });
177
- $[18] = queryClient;
178
- $[19] = t5;
179
- $[20] = t6;
180
- } else t6 = $[20];
181
- return t6;
12
+ let t0;
13
+ if ($[1] !== props) {
14
+ t0 = /* @__PURE__ */ jsx(PageApp, { ...props });
15
+ $[1] = props;
16
+ $[2] = t0;
17
+ } else t0 = $[2];
18
+ return t0;
182
19
  }
183
20
 
184
21
  //#endregion
@@ -1,21 +1,23 @@
1
1
  import { Navigate, useLocation } from "../navigation/navigation.js";
2
2
  import { useAuthState, useSignInRedirect } from "../../lib/auth.js";
3
+ import { SharedChromeContext } from "../runtime/SharedChromeContext.js";
3
4
  import { STUDIO_BASE_PATH } from "./routes.js";
4
5
  import { trackClientEvent } from "../../lib/telemetry-client.js";
5
6
  import { Navbar } from "./components/Navbar.js";
6
7
  import { c } from "react/compiler-runtime";
7
8
  import * as React from "react";
8
- import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
10
 
10
11
  //#region src/features/studio/CamoxStudio.tsx
11
12
  const CamoxStudio = (t0) => {
12
- const $ = c(14);
13
- if ($[0] !== "869273f9f42924824ee2b9e5a0066bb2be142208aab9426269ff2984ede38347") {
14
- for (let $i = 0; $i < 14; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
15
- $[0] = "869273f9f42924824ee2b9e5a0066bb2be142208aab9426269ff2984ede38347";
13
+ const $ = c(16);
14
+ if ($[0] !== "424ccf9eb3e0b68f3206047ad39569798182b6392a92ca98a7e9d6cf3cc9ff22") {
15
+ for (let $i = 0; $i < 16; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
16
+ $[0] = "424ccf9eb3e0b68f3206047ad39569798182b6392a92ca98a7e9d6cf3cc9ff22";
16
17
  }
17
18
  const { children } = t0;
18
19
  const { isAuthenticated, isLoading: isLoadingAuth } = useAuthState();
20
+ const sharedChrome = React.useContext(SharedChromeContext);
19
21
  const { pathname } = useLocation();
20
22
  const signInRedirect = useSignInRedirect();
21
23
  let t1;
@@ -62,25 +64,37 @@ const CamoxStudio = (t0) => {
62
64
  if (pathname === STUDIO_BASE_PATH) {
63
65
  let t5;
64
66
  if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
65
- t5 = /* @__PURE__ */ jsx(Navigate, { to: "/" });
67
+ t5 = /* @__PURE__ */ jsx(Navigate, {
68
+ to: "/",
69
+ replace: true
70
+ });
66
71
  $[10] = t5;
67
72
  } else t5 = $[10];
68
73
  return t5;
69
74
  }
75
+ if (sharedChrome) {
76
+ let t5;
77
+ if ($[11] !== children) {
78
+ t5 = /* @__PURE__ */ jsx(Fragment, { children });
79
+ $[11] = children;
80
+ $[12] = t5;
81
+ } else t5 = $[12];
82
+ return t5;
83
+ }
70
84
  let t5;
71
- if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
85
+ if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
72
86
  t5 = /* @__PURE__ */ jsx(Navbar, {});
73
- $[11] = t5;
74
- } else t5 = $[11];
87
+ $[13] = t5;
88
+ } else t5 = $[13];
75
89
  let t6;
76
- if ($[12] !== children) {
90
+ if ($[14] !== children) {
77
91
  t6 = /* @__PURE__ */ jsxs("div", {
78
92
  className: "bg-background flex h-screen flex-col overflow-hidden",
79
93
  children: [t5, children]
80
94
  });
81
- $[12] = children;
82
- $[13] = t6;
83
- } else t6 = $[13];
95
+ $[14] = children;
96
+ $[15] = t6;
97
+ } else t6 = $[15];
84
98
  return t6;
85
99
  };
86
100