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,7 +1,12 @@
1
- import { useAuthState } from "../../lib/auth.js";
1
+ import { useAuthState, useSignInRedirect } from "../../lib/auth.js";
2
+ import { PreviewDocumentContext } from "./PreviewDocumentContext.js";
3
+ import { PreviewActivation } from "../preview/components/PreviewActivation.js";
4
+ import { Frame } from "../preview/components/Frame.js";
2
5
  import { CoreCamoxProvider, isLocalhostPreview } from "../provider/CoreCamoxProvider.js";
6
+ import { DerivedPageContent } from "../page/DerivedPageContent.js";
3
7
  import { PublishedPageExperience } from "../page/PublishedPageExperience.js";
4
8
  import { PageNavigationProvider } from "./pageNavigation.js";
9
+ import { RuntimeChrome } from "./RuntimeChrome.js";
5
10
  import { c } from "react/compiler-runtime";
6
11
  import { HydrationBoundary, QueryClientProvider } from "@tanstack/react-query";
7
12
  import * as React from "react";
@@ -9,76 +14,75 @@ import { jsx } from "react/jsx-runtime";
9
14
 
10
15
  //#region src/features/runtime/pageApp.tsx
11
16
  function PageApp(t0) {
12
- const $ = c(22);
13
- if ($[0] !== "41b609b6b6e9223ab6f76c85adfea25789d9d089bc02ca12ea1dd789dd8294d1") {
14
- for (let $i = 0; $i < 22; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
15
- $[0] = "41b609b6b6e9223ab6f76c85adfea25789d9d089bc02ca12ea1dd789dd8294d1";
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";
16
21
  }
17
22
  const { camoxApp, input, queryClient } = t0;
18
23
  const t1 = input.dehydratedState;
19
24
  let t2;
20
- if ($[1] !== camoxApp || $[2] !== input || $[3] !== queryClient) {
21
- t2 = /* @__PURE__ */ jsx(PageExperience, {
22
- camoxApp,
23
- input,
24
- queryClient
25
- });
26
- $[1] = camoxApp;
27
- $[2] = input;
28
- $[3] = queryClient;
29
- $[4] = t2;
30
- } else t2 = $[4];
31
- let t3;
32
- if ($[5] !== camoxApp || $[6] !== input.apiUrl || $[7] !== input.authenticationUrl || $[8] !== input.environmentName || $[9] !== input.projectSlug || $[10] !== t2) {
33
- t3 = /* @__PURE__ */ jsx(CoreCamoxProvider, {
25
+ if ($[1] !== camoxApp || $[2] !== input.apiUrl || $[3] !== input.authenticationUrl || $[4] !== input.environmentName || $[5] !== input.presentation || $[6] !== input.projectSlug || $[7] !== queryClient) {
26
+ t2 = (currentInput) => /* @__PURE__ */ jsx(CoreCamoxProvider, {
34
27
  camoxApp,
35
28
  authenticationUrl: input.authenticationUrl,
36
29
  apiUrl: input.apiUrl,
37
30
  projectSlug: input.projectSlug,
38
31
  environmentName: input.environmentName,
39
- children: t2
32
+ initialAuthenticated: input.presentation === "studio",
33
+ initialProject: currentInput.project,
34
+ children: /* @__PURE__ */ jsx(PreviewDocumentContext.Provider, {
35
+ value: currentInput.previewDocument,
36
+ children: /* @__PURE__ */ jsx(PageExperience, {
37
+ camoxApp,
38
+ input: currentInput,
39
+ queryClient,
40
+ studioDocument: input.presentation === "studio"
41
+ })
42
+ })
40
43
  });
41
- $[5] = camoxApp;
42
- $[6] = input.apiUrl;
43
- $[7] = input.authenticationUrl;
44
- $[8] = input.environmentName;
45
- $[9] = input.projectSlug;
46
- $[10] = t2;
47
- $[11] = t3;
48
- } else t3 = $[11];
49
- let t4;
50
- if ($[12] !== input || $[13] !== queryClient || $[14] !== t3) {
51
- t4 = /* @__PURE__ */ jsx(PageNavigationProvider, {
44
+ $[1] = camoxApp;
45
+ $[2] = input.apiUrl;
46
+ $[3] = input.authenticationUrl;
47
+ $[4] = input.environmentName;
48
+ $[5] = input.presentation;
49
+ $[6] = input.projectSlug;
50
+ $[7] = queryClient;
51
+ $[8] = t2;
52
+ } else t2 = $[8];
53
+ let t3;
54
+ if ($[9] !== input || $[10] !== queryClient || $[11] !== t2) {
55
+ t3 = /* @__PURE__ */ jsx(PageNavigationProvider, {
52
56
  initialInput: input,
53
57
  queryClient,
58
+ children: t2
59
+ });
60
+ $[9] = input;
61
+ $[10] = queryClient;
62
+ $[11] = t2;
63
+ $[12] = t3;
64
+ } else t3 = $[12];
65
+ let t4;
66
+ if ($[13] !== t1 || $[14] !== t3) {
67
+ t4 = /* @__PURE__ */ jsx(HydrationBoundary, {
68
+ state: t1,
54
69
  children: t3
55
70
  });
56
- $[12] = input;
57
- $[13] = queryClient;
71
+ $[13] = t1;
58
72
  $[14] = t3;
59
73
  $[15] = t4;
60
74
  } else t4 = $[15];
61
75
  let t5;
62
- if ($[16] !== t1 || $[17] !== t4) {
63
- t5 = /* @__PURE__ */ jsx(HydrationBoundary, {
64
- state: t1,
76
+ if ($[16] !== queryClient || $[17] !== t4) {
77
+ t5 = /* @__PURE__ */ jsx(QueryClientProvider, {
78
+ client: queryClient,
65
79
  children: t4
66
80
  });
67
- $[16] = t1;
81
+ $[16] = queryClient;
68
82
  $[17] = t4;
69
83
  $[18] = t5;
70
84
  } else t5 = $[18];
71
- let t6;
72
- if ($[19] !== queryClient || $[20] !== t5) {
73
- t6 = /* @__PURE__ */ jsx(QueryClientProvider, {
74
- client: queryClient,
75
- children: t5
76
- });
77
- $[19] = queryClient;
78
- $[20] = t5;
79
- $[21] = t6;
80
- } else t6 = $[21];
81
- return t6;
85
+ return t5;
82
86
  }
83
87
  const LazyEditablePageExperience = React.lazy(() => import("../preview/EditablePageExperience.js").then((module) => ({ default: module.EditablePageExperience })));
84
88
  const LazyLocalhostPreviewProvider = React.lazy(() => import("../provider/LocalhostPreviewProvider.js").then((module) => ({ default: module.LocalhostPreviewProvider })));
@@ -88,100 +92,171 @@ var EditingActivationBoundary = class extends React.Component {
88
92
  return { failed: true };
89
93
  }
90
94
  componentDidCatch(error) {
91
- console.error("Camox editing runtime failed to load; keeping the published page visible.", error);
95
+ console.error("Camox editing runtime failed to load.", error);
92
96
  }
93
97
  render() {
94
- if (this.state.failed) return this.props.fallback;
95
- return this.props.children;
98
+ return this.state.failed ? this.props.fallback : this.props.children;
96
99
  }
97
100
  };
101
+ const subscribeToHydration = () => () => {};
98
102
  function PageExperience(t0) {
99
- const $ = c(21);
100
- if ($[0] !== "41b609b6b6e9223ab6f76c85adfea25789d9d089bc02ca12ea1dd789dd8294d1") {
101
- for (let $i = 0; $i < 21; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
102
- $[0] = "41b609b6b6e9223ab6f76c85adfea25789d9d089bc02ca12ea1dd789dd8294d1";
103
+ const $ = c(39);
104
+ if ($[0] !== "9929b5234f34e8dc8642da9eb491a90e9a57f3ab875ddfc1a186f42f4452f2f5") {
105
+ for (let $i = 0; $i < 39; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
106
+ $[0] = "9929b5234f34e8dc8642da9eb491a90e9a57f3ab875ddfc1a186f42f4452f2f5";
103
107
  }
104
- const { camoxApp, input, queryClient } = t0;
105
- const { isAuthenticated } = useAuthState();
106
- const [hasOtt] = React.useState(_temp);
107
- const local = isLocalhostPreview();
108
- const shouldActivateEditing = isAuthenticated || input.source === "draft" || hasOtt;
108
+ const { camoxApp, input, queryClient, studioDocument } = t0;
109
+ const { isAuthenticated, isLoading } = useAuthState();
110
+ const hasHydrated = React.useSyncExternalStore(subscribeToHydration, _temp, _temp2);
111
+ const signIn = useSignInRedirect();
109
112
  let t1;
110
- if ($[1] !== input.source) {
111
- t1 = /* @__PURE__ */ jsx(PublishedPageExperience, { source: input.source });
112
- $[1] = input.source;
113
- $[2] = t1;
114
- } else t1 = $[2];
115
- const published = t1;
116
- if (shouldActivateEditing) {
117
- let t2;
118
- if ($[3] !== camoxApp || $[4] !== input || $[5] !== queryClient) {
119
- t2 = /* @__PURE__ */ jsx(LazyEditablePageExperience, {
113
+ let t2;
114
+ if ($[1] !== input.routeKind || $[2] !== isAuthenticated || $[3] !== isLoading || $[4] !== signIn) {
115
+ t1 = () => {
116
+ if (input.routeKind && !isAuthenticated && !isLoading) signIn();
117
+ };
118
+ t2 = [
119
+ input.routeKind,
120
+ isAuthenticated,
121
+ isLoading,
122
+ signIn
123
+ ];
124
+ $[1] = input.routeKind;
125
+ $[2] = isAuthenticated;
126
+ $[3] = isLoading;
127
+ $[4] = signIn;
128
+ $[5] = t1;
129
+ $[6] = t2;
130
+ } else {
131
+ t1 = $[5];
132
+ t2 = $[6];
133
+ }
134
+ React.useEffect(t1, t2);
135
+ let t3;
136
+ if ($[7] !== camoxApp || $[8] !== input.derived || $[9] !== input.routeKind || $[10] !== input.source) {
137
+ t3 = input.routeKind ? /* @__PURE__ */ jsx("div", {
138
+ className: "text-muted-foreground p-6 text-sm",
139
+ children: "Loading Studio…"
140
+ }) : input.derived ? /* @__PURE__ */ jsx(DerivedPageContent, {
141
+ camoxApp,
142
+ derived: input.derived,
143
+ source: input.source
144
+ }) : /* @__PURE__ */ jsx(PublishedPageExperience, { source: input.source });
145
+ $[7] = camoxApp;
146
+ $[8] = input.derived;
147
+ $[9] = input.routeKind;
148
+ $[10] = input.source;
149
+ $[11] = t3;
150
+ } else t3 = $[11];
151
+ const published = t3;
152
+ let t4;
153
+ if ($[12] !== published) {
154
+ t4 = /* @__PURE__ */ jsx(Frame, { children: published });
155
+ $[12] = published;
156
+ $[13] = t4;
157
+ } else t4 = $[13];
158
+ const isolatedPage = t4;
159
+ const fallback = input.routeKind ? published : isolatedPage;
160
+ if (isAuthenticated) {
161
+ let t5;
162
+ if ($[14] !== camoxApp || $[15] !== hasHydrated || $[16] !== input || $[17] !== queryClient) {
163
+ t5 = hasHydrated ? /* @__PURE__ */ jsx(LazyEditablePageExperience, {
120
164
  camoxApp,
121
165
  input,
122
166
  queryClient
167
+ }) : null;
168
+ $[14] = camoxApp;
169
+ $[15] = hasHydrated;
170
+ $[16] = input;
171
+ $[17] = queryClient;
172
+ $[18] = t5;
173
+ } else t5 = $[18];
174
+ const editor = t5;
175
+ let t6;
176
+ if ($[19] !== editor || $[20] !== fallback || $[21] !== input.previewDocument || $[22] !== input.routeKind || $[23] !== published) {
177
+ t6 = input.routeKind ? /* @__PURE__ */ jsx(React.Suspense, {
178
+ fallback,
179
+ children: editor ?? fallback
180
+ }) : /* @__PURE__ */ jsx(PreviewActivation, {
181
+ fallback: /* @__PURE__ */ jsx(Frame, {
182
+ serverOnly: !!input.previewDocument,
183
+ children: published
184
+ }),
185
+ children: editor
123
186
  });
124
- $[3] = camoxApp;
125
- $[4] = input;
126
- $[5] = queryClient;
127
- $[6] = t2;
128
- } else t2 = $[6];
129
- let t3;
130
- if ($[7] !== published || $[8] !== t2) {
131
- t3 = /* @__PURE__ */ jsx(React.Suspense, {
132
- fallback: published,
133
- children: t2
187
+ $[19] = editor;
188
+ $[20] = fallback;
189
+ $[21] = input.previewDocument;
190
+ $[22] = input.routeKind;
191
+ $[23] = published;
192
+ $[24] = t6;
193
+ } else t6 = $[24];
194
+ let t7;
195
+ if ($[25] !== fallback || $[26] !== t6) {
196
+ t7 = /* @__PURE__ */ jsx(EditingActivationBoundary, {
197
+ fallback,
198
+ children: t6
134
199
  });
135
- $[7] = published;
136
- $[8] = t2;
137
- $[9] = t3;
138
- } else t3 = $[9];
139
- let t4;
140
- if ($[10] !== published || $[11] !== t3) {
141
- t4 = /* @__PURE__ */ jsx(EditingActivationBoundary, {
142
- fallback: published,
143
- children: t3
200
+ $[25] = fallback;
201
+ $[26] = t6;
202
+ $[27] = t7;
203
+ } else t7 = $[27];
204
+ let t8;
205
+ if ($[28] !== input || $[29] !== t7) {
206
+ t8 = /* @__PURE__ */ jsx(RuntimeChrome, {
207
+ input,
208
+ children: t7
144
209
  });
145
- $[10] = published;
146
- $[11] = t3;
147
- $[12] = t4;
148
- } else t4 = $[12];
149
- return t4;
210
+ $[28] = input;
211
+ $[29] = t7;
212
+ $[30] = t8;
213
+ } else t8 = $[30];
214
+ return t8;
150
215
  }
151
- if (local) {
152
- let t2;
153
- if ($[13] !== published) {
154
- t2 = /* @__PURE__ */ jsx(LazyLocalhostPreviewProvider, { children: published });
155
- $[13] = published;
156
- $[14] = t2;
157
- } else t2 = $[14];
158
- let t3;
159
- if ($[15] !== published || $[16] !== t2) {
160
- t3 = /* @__PURE__ */ jsx(React.Suspense, {
216
+ if (input.routeKind) return null;
217
+ if (hasHydrated && isLocalhostPreview()) {
218
+ let t5;
219
+ if ($[31] !== published) {
220
+ t5 = /* @__PURE__ */ jsx(LazyLocalhostPreviewProvider, { children: published });
221
+ $[31] = published;
222
+ $[32] = t5;
223
+ } else t5 = $[32];
224
+ let t6;
225
+ if ($[33] !== published || $[34] !== t5) {
226
+ t6 = /* @__PURE__ */ jsx(React.Suspense, {
161
227
  fallback: published,
162
- children: t2
228
+ children: t5
163
229
  });
164
- $[15] = published;
165
- $[16] = t2;
166
- $[17] = t3;
167
- } else t3 = $[17];
168
- let t4;
169
- if ($[18] !== published || $[19] !== t3) {
170
- t4 = /* @__PURE__ */ jsx(EditingActivationBoundary, {
171
- fallback: published,
172
- children: t3
230
+ $[33] = published;
231
+ $[34] = t5;
232
+ $[35] = t6;
233
+ } else t6 = $[35];
234
+ return t6;
235
+ }
236
+ if (studioDocument) {
237
+ let t5;
238
+ if ($[36] === Symbol.for("react.memo_cache_sentinel")) {
239
+ t5 = { height: "100vh" };
240
+ $[36] = t5;
241
+ } else t5 = $[36];
242
+ let t6;
243
+ if ($[37] !== isolatedPage) {
244
+ t6 = /* @__PURE__ */ jsx("div", {
245
+ style: t5,
246
+ children: isolatedPage
173
247
  });
174
- $[18] = published;
175
- $[19] = t3;
176
- $[20] = t4;
177
- } else t4 = $[20];
178
- return t4;
248
+ $[37] = isolatedPage;
249
+ $[38] = t6;
250
+ } else t6 = $[38];
251
+ return t6;
179
252
  }
180
253
  return published;
181
254
  }
255
+ function _temp2() {
256
+ return false;
257
+ }
182
258
  function _temp() {
183
- if (typeof window === "undefined") return false;
184
- return new URL(window.location.href).searchParams.has("ott");
259
+ return true;
185
260
  }
186
261
 
187
262
  //#endregion
@@ -1,168 +1,117 @@
1
1
  import { NavigationProvider } from "../navigation/navigation.js";
2
2
  import { createPageHeadInput } from "./runtime.js";
3
+ import { createNavigationRequestTracker } from "./navigationRequest.js";
4
+ import { getNavigationTarget, runtimePath } from "./navigationTarget.js";
3
5
  import { hydrate } from "@tanstack/react-query";
4
6
  import * as React from "react";
5
7
  import { jsx } from "react/jsx-runtime";
6
8
  import { createHead, renderDOMHead } from "@unhead/react/client";
7
9
 
8
10
  //#region src/features/runtime/pageNavigation.tsx
9
- function normalizeRuntimeBasePath(basePath) {
10
- if (!basePath || basePath === "/") return "";
11
- return basePath.startsWith("/") ? basePath.replace(/\/+$/, "") : `/${basePath.replace(/\/+$/, "")}`;
12
- }
13
- function normalizePagePath(pathname, runtimeBasePath) {
14
- const basePath = normalizeRuntimeBasePath(runtimeBasePath);
15
- if (!basePath) return pathname || "/";
16
- if (pathname === basePath) return "/";
17
- if (pathname.startsWith(`${basePath}/`)) return pathname.slice(basePath.length) || "/";
18
- return pathname || "/";
19
- }
20
- function toRuntimePathname(pagePathname, runtimeBasePath) {
21
- const basePath = normalizeRuntimeBasePath(runtimeBasePath);
22
- const normalizedPathname = pagePathname.startsWith("/") ? pagePathname : `/${pagePathname}`;
23
- if (!basePath) return normalizedPathname;
24
- if (normalizedPathname === "/") return `${basePath}/`;
25
- if (normalizedPathname.startsWith(`${basePath}/`)) return normalizedPathname;
26
- return `${basePath}${normalizedPathname}`;
27
- }
28
- function getRuntimeLocation(runtimeBasePath) {
29
- return {
30
- hash: window.location.hash,
31
- href: window.location.href,
32
- pathname: normalizePagePath(window.location.pathname, runtimeBasePath),
33
- search: window.location.search
34
- };
35
- }
36
- function isReservedPagePath(pathname) {
37
- return pathname.startsWith("/_camox/") || pathname === "/_camox" || pathname === "/camox" || pathname.startsWith("/camox/") || pathname === "/og" || pathname === "/sitemap.xml";
38
- }
39
- function getClientNavigationTarget(to, runtimeBasePath) {
40
- const url = new URL(to, window.location.href);
41
- if (url.origin !== window.location.origin) return null;
42
- const pagePathname = normalizePagePath(url.pathname, runtimeBasePath);
43
- if (isReservedPagePath(pagePathname)) return null;
44
- url.pathname = toRuntimePathname(pagePathname, runtimeBasePath);
45
- return url;
46
- }
47
- async function fetchPageData(pagePathname, runtimeBasePath) {
48
- const dataUrl = new URL(toRuntimePathname("/_camox/data", runtimeBasePath), window.location.origin);
49
- dataUrl.searchParams.set("path", pagePathname);
50
- const response = await fetch(dataUrl, {
51
- headers: { Accept: "application/json" },
52
- credentials: "same-origin"
53
- });
54
- if (!response.ok) throw new Error(`Page data request failed: ${response.status}`);
55
- return await response.json();
56
- }
57
- function isPageHead(head) {
58
- return !!head && typeof head === "object";
59
- }
60
- function updateHead(head, manager) {
61
- if (!isPageHead(head)) return;
62
- if (!manager.removedServerPageHead) {
63
- document.querySelectorAll("[data-camox-page-head]").forEach((element) => element.remove());
64
- manager.removedServerPageHead = true;
65
- }
66
- manager.entry?.dispose();
67
- manager.entry = manager.head.push(createPageHeadInput(head));
68
- renderDOMHead(manager.head);
69
- }
70
- function hasSameLayoutIdentity(current, next) {
71
- return !!current && !!next && current.id === next.id && current.layoutId === next.layoutId && current.version === next.version;
72
- }
73
- function isLayoutBlockQuery(query, layoutBlockIds) {
74
- if (!query || typeof query !== "object") return false;
75
- const queryKey = query.queryKey;
76
- return Array.isArray(queryKey) && queryKey[0] === "camox" && queryKey[1] === "blocks" && queryKey[2] === "get" && typeof queryKey[3] === "number" && layoutBlockIds.has(queryKey[3]);
77
- }
78
- function preserveStableLayoutBlockCaches(dehydratedState, currentLayout, nextLayout) {
79
- if (!hasSameLayoutIdentity(currentLayout, nextLayout)) return dehydratedState;
80
- if (!dehydratedState || typeof dehydratedState !== "object") return dehydratedState;
81
- if (!Array.isArray(dehydratedState.queries)) return dehydratedState;
82
- const layoutBlockIds = new Set(currentLayout?.blockIds ?? []);
11
+ function preserveStableLayoutBlockCaches(dehydratedState, current, next) {
12
+ if (!current || !next || current.id !== next.id || current.layoutId !== next.layoutId || current.version !== next.version) return dehydratedState;
13
+ if (!dehydratedState || typeof dehydratedState !== "object" || !("queries" in dehydratedState) || !Array.isArray(dehydratedState.queries)) return dehydratedState;
14
+ const ids = new Set(current.blockIds);
83
15
  return {
84
16
  ...dehydratedState,
85
- queries: dehydratedState.queries.filter((query) => !isLayoutBlockQuery(query, layoutBlockIds))
17
+ queries: dehydratedState.queries.filter((query) => {
18
+ const key = query.queryKey;
19
+ return !(key?.[0] === "camox" && key[1] === "blocks" && key[2] === "get" && typeof key[3] === "number" && ids.has(key[3]));
20
+ })
86
21
  };
87
22
  }
88
23
  function PageNavigationProvider({ children, initialInput, queryClient }) {
89
- const headManagerRef = React.useRef(null);
90
- if (!headManagerRef.current) headManagerRef.current = {
91
- head: createHead(),
92
- removedServerPageHead: false
93
- };
94
- const headManager = headManagerRef.current;
95
- const currentLayoutIdentityRef = React.useRef(initialInput.layoutIdentity);
96
- const navigate = React.useCallback(async ({ replace, to }) => {
97
- const target = getClientNavigationTarget(to, initialInput.runtimeBasePath);
24
+ const [input, setInput] = React.useState(initialInput);
25
+ const current = React.useRef(initialInput);
26
+ const [requests] = React.useState(createNavigationRequestTracker);
27
+ const headManager = React.useRef(null);
28
+ const navigate = React.useCallback(async ({ to, replace, pop = false }) => {
29
+ const request = requests.begin();
30
+ const target = getNavigationTarget(to, window.location.href, initialInput.runtimeBasePath);
98
31
  if (!target) {
99
- window.location.assign(to);
32
+ if (replace) window.location.replace(to);
33
+ else window.location.assign(to);
100
34
  return;
101
35
  }
102
- const pagePathname = normalizePagePath(target.pathname, initialInput.runtimeBasePath);
103
36
  try {
104
- const data = await fetchPageData(pagePathname, initialInput.runtimeBasePath);
105
- if (data.dehydratedState) hydrate(queryClient, preserveStableLayoutBlockCaches(data.dehydratedState, currentLayoutIdentityRef.current, data.layoutIdentity));
106
- currentLayoutIdentityRef.current = data.layoutIdentity ?? null;
107
- updateHead(data.head, headManager);
37
+ let next = current.current;
38
+ if (target.pathname !== next.pathname || pop) {
39
+ const url = new URL(runtimePath("/_camox/data", initialInput.runtimeBasePath), window.location.origin);
40
+ url.searchParams.set("path", target.pathname);
41
+ const response = await fetch(url, {
42
+ credentials: "same-origin",
43
+ headers: { Accept: "application/json" },
44
+ signal: request.signal
45
+ });
46
+ if (!response.ok) throw new Error(`Navigation failed: ${response.status}`);
47
+ next = await response.json();
48
+ if (next.pathname !== target.pathname || !next.dehydratedState) throw new Error("Invalid navigation response");
49
+ }
50
+ if (!request.isCurrent()) return;
51
+ hydrate(queryClient, preserveStableLayoutBlockCaches(next.dehydratedState, current.current.layoutIdentity, next.layoutIdentity));
52
+ next = {
53
+ ...next,
54
+ href: target.url.href
55
+ };
56
+ current.current = next;
57
+ if (!pop) if (replace) window.history.replaceState(null, "", target.url.href);
58
+ else window.history.pushState(null, "", target.url.href);
59
+ if (!headManager.current) {
60
+ document.querySelectorAll("[data-camox-page-head]").forEach((element) => element.remove());
61
+ headManager.current = { head: createHead() };
62
+ }
63
+ const manager = headManager.current;
64
+ manager.entry?.dispose();
65
+ manager.entry = manager.head.push(createPageHeadInput(next.head ?? {}));
66
+ renderDOMHead(manager.head);
67
+ React.startTransition(() => setInput(next));
68
+ if (!pop && !replace && !target.url.hash) window.scrollTo({ top: 0 });
108
69
  } catch {
109
- window.location.assign(target.href);
110
- return;
70
+ if (!request.isCurrent()) return;
71
+ if (pop) window.location.reload();
72
+ else if (replace) window.location.replace(target.url.href);
73
+ else window.location.assign(target.url.href);
111
74
  }
112
- if (replace) window.history.replaceState(null, "", target.href);
113
- else {
114
- window.history.pushState(null, "", target.href);
115
- window.scrollTo({ top: 0 });
116
- }
117
- window.dispatchEvent(new Event("camox:navigation"));
118
75
  }, [
119
- headManager,
120
76
  initialInput.runtimeBasePath,
121
- queryClient
77
+ queryClient,
78
+ requests
122
79
  ]);
123
80
  React.useEffect(() => {
124
81
  const onClick = (event) => {
125
- if (event.defaultPrevented) return;
126
- if (event.button !== 0) return;
127
- if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) return;
82
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
128
83
  const anchor = event.target?.closest("a");
129
- if (!anchor) return;
130
- if (anchor.hasAttribute("download")) return;
131
- if (anchor.target && anchor.target !== "_self") return;
132
- const target_0 = getClientNavigationTarget(anchor.href, initialInput.runtimeBasePath);
133
- if (!target_0) return;
84
+ if (!anchor || anchor.hasAttribute("download") || anchor.target && anchor.target !== "_self") return;
85
+ if (!getNavigationTarget(anchor.href, window.location.href, initialInput.runtimeBasePath)) return;
134
86
  event.preventDefault();
135
- navigate({ to: target_0.href });
87
+ navigate({ to: anchor.href });
136
88
  };
89
+ const onPopState = () => void navigate({
90
+ to: window.location.href,
91
+ pop: true
92
+ });
137
93
  document.addEventListener("click", onClick);
138
- return () => document.removeEventListener("click", onClick);
139
- }, [navigate]);
140
- React.useEffect(() => {
141
- const onPopState = () => {
142
- fetchPageData(normalizePagePath(window.location.pathname, initialInput.runtimeBasePath), initialInput.runtimeBasePath).then((data_0) => {
143
- if (data_0.dehydratedState) hydrate(queryClient, preserveStableLayoutBlockCaches(data_0.dehydratedState, currentLayoutIdentityRef.current, data_0.layoutIdentity));
144
- currentLayoutIdentityRef.current = data_0.layoutIdentity ?? null;
145
- updateHead(data_0.head, headManager);
146
- window.dispatchEvent(new Event("camox:navigation"));
147
- }).catch(() => window.location.reload());
148
- };
149
94
  window.addEventListener("popstate", onPopState);
150
- return () => window.removeEventListener("popstate", onPopState);
95
+ return () => {
96
+ document.removeEventListener("click", onClick);
97
+ window.removeEventListener("popstate", onPopState);
98
+ requests.cancel();
99
+ };
151
100
  }, [
152
- headManager,
101
+ navigate,
153
102
  initialInput.runtimeBasePath,
154
- queryClient
103
+ requests
155
104
  ]);
105
+ const url_0 = new URL(input.href);
156
106
  return /* @__PURE__ */ jsx(NavigationProvider, {
157
- getLocation: React.useCallback(() => getRuntimeLocation(initialInput.runtimeBasePath), [initialInput.runtimeBasePath]),
158
- initialLocation: {
159
- hash: new URL(initialInput.href).hash,
160
- href: initialInput.href,
161
- pathname: initialInput.pathname,
162
- search: new URL(initialInput.href).search
107
+ location: {
108
+ href: input.href,
109
+ pathname: input.pathname,
110
+ hash: url_0.hash,
111
+ search: url_0.search
163
112
  },
164
113
  navigate,
165
- children
114
+ children: children(input)
166
115
  });
167
116
  }
168
117
 
@@ -1,5 +1,6 @@
1
1
  import { CamoxApp } from "../../core/createApp.js";
2
2
  import { CamoxDocument } from "../../core/defineDocument.js";
3
+ import { createServerApiClient } from "../routes/pageRuntime.js";
3
4
  import { StudioRenderInput } from "./studioApp.js";
4
5
  import { UseHeadInput } from "unhead/types";
5
6
 
@@ -16,6 +17,15 @@ interface LayoutIdentity {
16
17
  version: string;
17
18
  }
18
19
  interface PageRenderInput {
20
+ previewDocument?: string;
21
+ routeKind?: "studio" | "studio-content" | "studio-nested";
22
+ project?: Awaited<ReturnType<ReturnType<typeof createServerApiClient>["projects"]["getBySlug"]>>;
23
+ presentation?: "public" | "studio";
24
+ derived?: {
25
+ layoutId: string;
26
+ data: unknown;
27
+ layout: Awaited<ReturnType<ReturnType<typeof createServerApiClient>["layouts"]["get"]>>["layout"];
28
+ };
19
29
  apiUrl: string;
20
30
  authenticationUrl: string;
21
31
  dehydratedState: unknown;
@@ -42,6 +52,7 @@ interface RuntimeOptions {
42
52
  renderStudio?: (input: StudioRenderInput) => Promise<string>;
43
53
  runtimeBasePath?: string;
44
54
  stylesheetUrl?: string;
55
+ studioStylesheetUrl?: string;
45
56
  }
46
57
  declare function getRuntimePathname(url: string, runtimeBasePath?: string): string | null;
47
58
  declare function matchRuntimeRoute(pathname: string): RuntimeRouteMatch;