camox 0.51.0 → 0.52.2

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 (91) hide show
  1. package/dist/core/createApp.d.ts +1 -0
  2. package/dist/core/createApp.js +1 -0
  3. package/dist/core/editing/contentEdits.js +78 -0
  4. package/dist/core/editing/createEditableBlock.js +277 -311
  5. package/dist/core/lib/contentType.js +2 -2
  6. package/dist/core/recordItem.js +3 -3
  7. package/dist/features/canvas/CamoxCanvas.js +15 -15
  8. package/dist/features/canvas/CanvasLoadingScreen.js +15 -11
  9. package/dist/features/canvas/CanvasPageFrame.js +81 -87
  10. package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -1
  11. package/dist/features/canvas/DerivedPagePathInput.js +12 -12
  12. package/dist/features/canvas/canvasOverlayGeometry.js +1 -1
  13. package/dist/features/canvas/canvasStore.js +2 -2
  14. package/dist/features/canvas/sidebar/CuratedPageInspector.js +1 -1
  15. package/dist/features/canvas/sidebar/LayoutPageBlocks.js +23 -31
  16. package/dist/features/canvas/useCanvasCamera.js +1 -1
  17. package/dist/features/canvas/useDiscoveredPreviewPaths.js +16 -9
  18. package/dist/features/page/CuratedPageContent.js +1 -1
  19. package/dist/features/preview/CamoxPreview.d.ts +8 -4
  20. package/dist/features/preview/CamoxPreview.js +257 -442
  21. package/dist/features/preview/EditablePageContent.js +244 -2
  22. package/dist/features/preview/EditablePageExperience.js +2 -4
  23. package/dist/features/preview/components/AddBlockSheet.js +55 -15
  24. package/dist/features/preview/components/BlockActionsPopover.js +12 -16
  25. package/dist/features/preview/components/BlockPeek.js +66 -0
  26. package/dist/features/preview/components/BlockThumbnail.js +134 -119
  27. package/dist/features/preview/components/CanvasPreview.js +1 -1
  28. package/dist/features/preview/components/CreatePageModal.js +150 -156
  29. package/dist/features/preview/components/Frame.js +27 -4
  30. package/dist/features/preview/components/MobilePreviewDrawer.js +1 -1
  31. package/dist/features/preview/components/PageInfo.js +161 -126
  32. package/dist/features/preview/components/PagePicker.js +2 -2
  33. package/dist/features/preview/components/PageTree.js +174 -190
  34. package/dist/features/preview/components/PreviewPanel.js +3 -3
  35. package/dist/features/preview/components/ReferenceFieldEditor.js +4 -4
  36. package/dist/features/preview/components/ReferenceListFieldEditor.js +4 -4
  37. package/dist/features/preview/components/RemovedBlockPlaceholder.js +37 -70
  38. package/dist/features/preview/components/RepeatableItemsList.js +1 -1
  39. package/dist/features/preview/components/SelectionEditor.js +248 -256
  40. package/dist/features/preview/components/StudioEditingFallback.js +22 -26
  41. package/dist/features/preview/components/useFieldWriter.js +19 -19
  42. package/dist/features/preview/components/useRecordView.js +105 -147
  43. package/dist/features/preview/components/useRepeatableItemActions.js +0 -2
  44. package/dist/features/preview/pageRecord.js +30 -24
  45. package/dist/features/preview/previewStore.js +2 -0
  46. package/dist/features/preview/studioNotice.js +20 -0
  47. package/dist/features/provider/AuthenticatedCamoxProvider.js +46 -30
  48. package/dist/features/renderer/RendererApp.js +542 -0
  49. package/dist/features/renderer/RendererFrames.js +450 -0
  50. package/dist/features/renderer/definitionAnswers.js +26 -0
  51. package/dist/features/renderer/protocol.js +35 -0
  52. package/dist/features/renderer/rendererHost.js +249 -0
  53. package/dist/features/renderer/studioConnection.js +186 -0
  54. package/dist/features/routes/pageRuntime.js +7 -2
  55. package/dist/features/runtime/RuntimeChrome.js +2 -5
  56. package/dist/features/runtime/assetPrefix.js +14 -0
  57. package/dist/features/runtime/breadcrumbs.js +1 -1
  58. package/dist/features/runtime/editingFallback.js +4 -8
  59. package/dist/features/runtime/livePages.js +1 -1
  60. package/dist/features/runtime/pageApp.js +57 -83
  61. package/dist/features/runtime/pageClient.js +1 -1
  62. package/dist/features/runtime/pageNavigation.js +3 -2
  63. package/dist/features/runtime/publicCache.js +15 -3
  64. package/dist/features/runtime/publishingDraft.js +23 -0
  65. package/dist/features/runtime/rendererClient.d.ts +8 -0
  66. package/dist/features/runtime/rendererClient.js +77 -0
  67. package/dist/features/runtime/runtime.d.ts +9 -0
  68. package/dist/features/runtime/runtime.js +36 -17
  69. package/dist/features/runtime/runtimeRoutes.d.ts +2 -0
  70. package/dist/features/runtime/runtimeRoutes.js +2 -0
  71. package/dist/features/runtime/worker.d.ts +10 -1
  72. package/dist/features/runtime/worker.js +20 -7
  73. package/dist/features/studio/FollowAgentProvider.js +10 -10
  74. package/dist/features/studio/components/ProjectMenu.js +1 -1
  75. package/dist/features/studio/components/ReviewDraftSheet.js +242 -138
  76. package/dist/features/studio/components/ThreadMessages.js +100 -65
  77. package/dist/features/studio/components/UserButton.js +1 -1
  78. package/dist/features/vite/definitionsSync.js +1 -1
  79. package/dist/features/vite/hostedChecks.js +127 -0
  80. package/dist/features/vite/runtimeDev.js +26 -0
  81. package/dist/features/vite/vite.js +33 -5
  82. package/dist/hooks/use-page-destinations.js +10 -10
  83. package/dist/lib/revisions.js +20 -0
  84. package/dist/lib/studio-definitions.js +160 -0
  85. package/dist/lib/studio-reads.js +65 -21
  86. package/dist/lib/use-project-room.js +16 -10
  87. package/dist/package.js +1 -1
  88. package/dist/studio.css +1 -1
  89. package/package.json +8 -4
  90. package/skills/camox/SKILL.md +4 -0
  91. package/skills/camox/references/artifacts.md +1 -1
@@ -0,0 +1,249 @@
1
+ import { referencePickerFocus } from "../preview/referencePickerFocus.js";
2
+ import { canvasStore } from "../canvas/canvasStore.js";
3
+ import { previewStore } from "../preview/previewStore.js";
4
+ import { SDK_VERSION, deferredInbox } from "./protocol.js";
5
+ import { createContentEditor } from "../../core/editing/contentEdits.js";
6
+ import { LIVE_KEY_SCOPE, scopeOfKey } from "@camox/api-contract/query-keys";
7
+
8
+ //#region src/features/renderer/rendererHost.ts
9
+ const mirrored = {
10
+ preview: previewStore,
11
+ canvas: canvasStore
12
+ };
13
+ const forwarded = {
14
+ preview: previewStore,
15
+ referencePickerFocus
16
+ };
17
+ /**
18
+ * The host of the studio whose cache is `studioCache`, viewing the draft `draftScope` names, which
19
+ * the renderer shows too. Without them, the renderer hears nothing of the studio's cache.
20
+ * The edits the renderer makes in place are written with the studio's own mutations, through
21
+ * `queryClient`; without it, every one fails. What the renderer's pages tell the editor goes to
22
+ * `notify`.
23
+ */
24
+ function createRendererHost({ studioCache, draftScope, queryClient, notify } = {}) {
25
+ const applyEdit = queryClient && studioCache ? createContentEditor({
26
+ queryClient,
27
+ studioCache
28
+ }) : void 0;
29
+ const frames = /* @__PURE__ */ new Map();
30
+ const thumbnails = /* @__PURE__ */ new Map();
31
+ /** The questions asked that no renderer answered yet. */
32
+ const asked = /* @__PURE__ */ new Map();
33
+ let lastAskId = 0;
34
+ const connectionListeners = /* @__PURE__ */ new Set();
35
+ let status = "connecting";
36
+ const statusListeners = /* @__PURE__ */ new Set();
37
+ const setStatus = (next) => {
38
+ if (status === next) return;
39
+ status = next;
40
+ for (const listener of statusListeners) listener();
41
+ };
42
+ let renderer = null;
43
+ /** Sends to the renderer once it said hello, in the protocol it speaks. */
44
+ let welcomed = false;
45
+ const post = (message) => {
46
+ if (welcomed) renderer?.(message);
47
+ };
48
+ const postStore = (store) => post({
49
+ type: "store",
50
+ store,
51
+ context: mirrored[store].getSnapshot().context
52
+ });
53
+ const subscriptions = Object.keys(mirrored).map((store) => mirrored[store].subscribe(() => postStore(store)));
54
+ const shownScope = () => draftScope?.current() ?? LIVE_KEY_SCOPE;
55
+ const shown = (queryKey) => scopeOfKey(queryKey) === shownScope();
56
+ /** Whether a project room invalidation reaches the reads of the draft the renderer shows. */
57
+ const reaches = async (message, projectId) => {
58
+ if (message.draftId === null) return true;
59
+ if (!draftScope) return false;
60
+ return await draftScope.keyScopeOfDraft(message.draftId, projectId) === shownScope();
61
+ };
62
+ const relayChange = (change) => {
63
+ switch (change.type) {
64
+ case "received": {
65
+ const { message, projectId } = change;
66
+ reaches(message, projectId).then((reached) => reached && post({
67
+ type: "invalidate",
68
+ message,
69
+ projectId
70
+ }));
71
+ return;
72
+ }
73
+ case "wrote":
74
+ if (!shown(change.queryKey)) return;
75
+ post({
76
+ type: "write",
77
+ queryKey: change.queryKey,
78
+ ..."value" in change && change.value !== void 0 && { value: change.value }
79
+ });
80
+ return;
81
+ case "outdated": {
82
+ const queryKeys = change.queryKeys.filter(shown);
83
+ if (queryKeys.length) post({
84
+ type: "outdated",
85
+ queryKeys
86
+ });
87
+ return;
88
+ }
89
+ }
90
+ };
91
+ const stopRelaying = studioCache?.subscribe(relayChange);
92
+ const stopFollowing = draftScope?.subscribe(({ draftId, move }) => {
93
+ if (move === "started" && draftId) post({
94
+ type: "draftStarted",
95
+ draftId
96
+ });
97
+ });
98
+ const receive = (message) => {
99
+ switch (message.type) {
100
+ case "hello":
101
+ if (message.protocol !== 1) return;
102
+ renderer?.({
103
+ type: "welcome",
104
+ protocol: 1,
105
+ sdkVersion: SDK_VERSION
106
+ });
107
+ if (message.sdkVersion !== SDK_VERSION) return setStatus("updating");
108
+ welcomed = true;
109
+ setStatus("connected");
110
+ for (const store of Object.keys(mirrored)) postStore(store);
111
+ for (const [frameId, { slot, target }] of frames) post({
112
+ type: "frame",
113
+ frameId,
114
+ slot,
115
+ target
116
+ });
117
+ for (const [thumbnailId, { slot, blockType }] of thumbnails) post({
118
+ type: "thumbnail",
119
+ thumbnailId,
120
+ slot,
121
+ blockType
122
+ });
123
+ for (const [askId, { question }] of asked) post({
124
+ type: "ask",
125
+ askId,
126
+ question
127
+ });
128
+ for (const listener of connectionListeners) listener();
129
+ return;
130
+ case "storeEvent": {
131
+ const store = forwarded[message.store];
132
+ if (!store) return;
133
+ store.send(message.event);
134
+ return;
135
+ }
136
+ case "rendered": return frames.get(message.frameId)?.hears.rendered?.();
137
+ case "error": return frames.get(message.frameId)?.hears.failed?.(message.message);
138
+ case "navigate": return frames.get(message.frameId)?.hears.navigate?.(message.to);
139
+ case "reveal": return frames.get(message.frameId)?.hears.reveal?.(message.element, message.immediate);
140
+ case "answer": {
141
+ const question = asked.get(message.askId);
142
+ if (!question) return;
143
+ asked.delete(message.askId);
144
+ if (message.error === void 0) question.resolve(message.value ?? null);
145
+ else question.reject(new Error(message.error));
146
+ return;
147
+ }
148
+ case "notify": return notify?.(message.notice);
149
+ case "edit": {
150
+ const { editId, edit } = message;
151
+ const sender = renderer;
152
+ const answer = (error) => {
153
+ if (renderer === sender) post({
154
+ type: "edited",
155
+ editId,
156
+ ...error && { error }
157
+ });
158
+ };
159
+ if (!applyEdit) return answer("The studio can't save changes made here.");
160
+ applyEdit(edit).then(() => answer(), (cause) => answer(cause instanceof Error ? cause.message : "Couldn't save the change."));
161
+ return;
162
+ }
163
+ }
164
+ };
165
+ return {
166
+ handle: { connect(inbox) {
167
+ renderer = deferredInbox(inbox);
168
+ welcomed = false;
169
+ const connected = renderer;
170
+ return deferredInbox((message) => {
171
+ if (renderer === connected) receive(message);
172
+ });
173
+ } },
174
+ show(frameId, slot, target, hears = {}) {
175
+ frames.set(frameId, {
176
+ slot,
177
+ target,
178
+ hears
179
+ });
180
+ post({
181
+ type: "frame",
182
+ frameId,
183
+ slot,
184
+ target
185
+ });
186
+ },
187
+ hide(frameId) {
188
+ if (!frames.delete(frameId)) return;
189
+ post({
190
+ type: "frameEnd",
191
+ frameId
192
+ });
193
+ },
194
+ ask(question) {
195
+ return new Promise((resolve, reject) => {
196
+ const askId = ++lastAskId;
197
+ asked.set(askId, {
198
+ question,
199
+ resolve,
200
+ reject
201
+ });
202
+ post({
203
+ type: "ask",
204
+ askId,
205
+ question
206
+ });
207
+ });
208
+ },
209
+ showThumbnail(thumbnailId, slot, blockType) {
210
+ thumbnails.set(thumbnailId, {
211
+ slot,
212
+ blockType
213
+ });
214
+ post({
215
+ type: "thumbnail",
216
+ thumbnailId,
217
+ slot,
218
+ blockType
219
+ });
220
+ },
221
+ hideThumbnail(thumbnailId) {
222
+ if (!thumbnails.delete(thumbnailId)) return;
223
+ post({
224
+ type: "thumbnailEnd",
225
+ thumbnailId
226
+ });
227
+ },
228
+ subscribe(listener) {
229
+ connectionListeners.add(listener);
230
+ return () => connectionListeners.delete(listener);
231
+ },
232
+ status: () => status,
233
+ subscribeStatus(listener) {
234
+ statusListeners.add(listener);
235
+ return () => statusListeners.delete(listener);
236
+ },
237
+ close() {
238
+ connectionListeners.clear();
239
+ statusListeners.clear();
240
+ for (const subscription of subscriptions) subscription.unsubscribe();
241
+ stopRelaying?.();
242
+ stopFollowing?.();
243
+ renderer = null;
244
+ }
245
+ };
246
+ }
247
+
248
+ //#endregion
249
+ export { createRendererHost };
@@ -0,0 +1,186 @@
1
+ import { referencePickerFocus } from "../preview/referencePickerFocus.js";
2
+ import { canvasStore } from "../canvas/canvasStore.js";
3
+ import { previewStore } from "../preview/previewStore.js";
4
+ import { SDK_VERSION, deferredInbox } from "./protocol.js";
5
+ import { answerFromCode } from "./definitionAnswers.js";
6
+
7
+ //#region src/features/renderer/studioConnection.ts
8
+ const mirrors = {
9
+ preview: previewStore,
10
+ canvas: canvasStore
11
+ };
12
+ const forwards = {
13
+ preview: previewStore,
14
+ referencePickerFocus
15
+ };
16
+ /**
17
+ * Connects the renderer document to the studio through its handle. This document's stores then
18
+ * mirror the studio's, its own events are forwarded to the studio, which owns their truth, and
19
+ * its cache follows the studio's (see `protocol.ts`). The studio's questions are answered from
20
+ * `camoxApp`, the draft's code; without it, none can be.
21
+ */
22
+ function connectToStudio(handle, { queryClient, studioCache, draftScope, camoxApp }) {
23
+ let welcomed = false;
24
+ let frames = [];
25
+ const listeners = /* @__PURE__ */ new Set();
26
+ let thumbnails = [];
27
+ const changed = () => {
28
+ for (const listener of listeners) listener();
29
+ };
30
+ const setFrames = (next) => {
31
+ frames = next;
32
+ changed();
33
+ };
34
+ const setThumbnails = (next) => {
35
+ thumbnails = next;
36
+ changed();
37
+ };
38
+ /** The edits sent to the studio that it hasn't answered yet. */
39
+ const editsInFlight = /* @__PURE__ */ new Map();
40
+ let lastEditId = 0;
41
+ /** Whether the renderer holds the read under `queryKey`. */
42
+ const holds = (queryKey) => !!queryClient.getQueryCache().find({
43
+ queryKey,
44
+ exact: true
45
+ });
46
+ const receive = (message) => {
47
+ if (message.type === "welcome") {
48
+ welcomed = message.protocol === 1 && message.sdkVersion === SDK_VERSION;
49
+ return;
50
+ }
51
+ if (!welcomed) return;
52
+ switch (message.type) {
53
+ case "frame": {
54
+ const frame = {
55
+ frameId: message.frameId,
56
+ slot: message.slot,
57
+ target: message.target
58
+ };
59
+ const index = frames.findIndex(({ frameId }) => frameId === message.frameId);
60
+ setFrames(index < 0 ? [...frames, frame] : frames.map((shown, at) => at === index ? frame : shown));
61
+ return;
62
+ }
63
+ case "frameEnd":
64
+ setFrames(frames.filter(({ frameId }) => frameId !== message.frameId));
65
+ return;
66
+ case "store":
67
+ mirrors[message.store]?.send({
68
+ type: "mirror",
69
+ context: message.context
70
+ });
71
+ return;
72
+ case "draftStarted":
73
+ draftScope.enterStarted(message.draftId);
74
+ return;
75
+ case "invalidate":
76
+ studioCache.receive(message.message, message.projectId);
77
+ return;
78
+ case "write":
79
+ if (!holds(message.queryKey)) return;
80
+ if ("value" in message && message.value !== void 0) queryClient.setQueryData(message.queryKey, message.value);
81
+ else queryClient.removeQueries({
82
+ queryKey: message.queryKey,
83
+ exact: true
84
+ });
85
+ return;
86
+ case "outdated":
87
+ for (const queryKey of message.queryKeys) if (holds(queryKey)) queryClient.invalidateQueries({
88
+ queryKey,
89
+ exact: true
90
+ });
91
+ return;
92
+ case "thumbnail": {
93
+ const { thumbnailId, slot, blockType } = message;
94
+ const thumbnail = {
95
+ thumbnailId,
96
+ slot,
97
+ blockType
98
+ };
99
+ const index = thumbnails.findIndex((shown) => shown.thumbnailId === thumbnailId);
100
+ setThumbnails(index < 0 ? [...thumbnails, thumbnail] : thumbnails.map((shown, at) => at === index ? thumbnail : shown));
101
+ return;
102
+ }
103
+ case "thumbnailEnd":
104
+ setThumbnails(thumbnails.filter(({ thumbnailId }) => thumbnailId !== message.thumbnailId));
105
+ return;
106
+ case "ask": {
107
+ const { askId, question } = message;
108
+ if (!camoxApp) {
109
+ studio({
110
+ type: "answer",
111
+ askId,
112
+ error: "The renderer has no code to answer with."
113
+ });
114
+ return;
115
+ }
116
+ answerFromCode(camoxApp, question).then((value) => studio({
117
+ type: "answer",
118
+ askId,
119
+ value
120
+ }), (error) => studio({
121
+ type: "answer",
122
+ askId,
123
+ error: error instanceof Error ? error.message : String(error)
124
+ }));
125
+ return;
126
+ }
127
+ case "edited": {
128
+ const edit = editsInFlight.get(message.editId);
129
+ if (!edit) return;
130
+ editsInFlight.delete(message.editId);
131
+ if (message.error === void 0) edit.resolve();
132
+ else edit.reject(new Error(message.error));
133
+ return;
134
+ }
135
+ }
136
+ };
137
+ const studio = handle.connect(deferredInbox(receive));
138
+ studio({
139
+ type: "hello",
140
+ protocol: 1,
141
+ sdkVersion: SDK_VERSION
142
+ });
143
+ const inspections = Object.keys(forwards).map((store) => forwards[store].inspect((inspection) => {
144
+ const event = inspection.event;
145
+ if (inspection.type !== "@xstate.transition" || !event) return;
146
+ if (event.type === "mirror" || event.type.startsWith("@xstate.")) return;
147
+ studio({
148
+ type: "storeEvent",
149
+ store,
150
+ event: { ...event }
151
+ });
152
+ }));
153
+ return {
154
+ connected: () => welcomed,
155
+ frames: () => frames,
156
+ thumbnails: () => thumbnails,
157
+ subscribe(listener) {
158
+ listeners.add(listener);
159
+ return () => listeners.delete(listener);
160
+ },
161
+ send: (message) => studio(message),
162
+ notify: (notice) => studio({
163
+ type: "notify",
164
+ notice
165
+ }),
166
+ edit: (edit) => new Promise((resolve, reject) => {
167
+ const editId = ++lastEditId;
168
+ editsInFlight.set(editId, {
169
+ resolve,
170
+ reject
171
+ });
172
+ studio({
173
+ type: "edit",
174
+ editId,
175
+ edit
176
+ });
177
+ }),
178
+ close() {
179
+ for (const inspection of inspections) inspection.unsubscribe();
180
+ listeners.clear();
181
+ }
182
+ };
183
+ }
184
+
185
+ //#endregion
186
+ export { connectToStudio };
@@ -2,10 +2,11 @@ import { UNNAMED_DEFAULT_LOCALE, localeHeaders } from "../../lib/locale-path.js"
2
2
  import { createDraftScope, withoutDraft } from "../../lib/draft-scope.js";
3
3
  import { createStudioCache, reads } from "../../lib/studio-reads.js";
4
4
  import { linkFallbackLocale } from "../../lib/link-pages.js";
5
- import { curatedLayoutContext } from "../../core/layoutContext.js";
6
5
  import { PAGE_IMAGE_SIZE, pageImageUrl } from "../og/pageImage.js";
6
+ import { curatedLayoutContext } from "../../core/layoutContext.js";
7
7
  import { isArticle } from "../../core/structuredData.js";
8
- import { DRAFT_ENDED_ERROR_CODE, DRAFT_HEADER, NO_DEVELOPMENT_DRAFT_ERROR } from "@camox/api-contract/drafts";
8
+ import { publishingDraft } from "../runtime/publishingDraft.js";
9
+ import { DRAFT_ENDED_ERROR_CODE, DRAFT_HEADER, NO_DEVELOPMENT_DRAFT_ERROR, PUBLISHING_DRAFT_HEADER } from "@camox/api-contract/drafts";
9
10
  import { createORPCClient } from "@orpc/client";
10
11
  import { RPCLink } from "@orpc/client/fetch";
11
12
 
@@ -38,6 +39,10 @@ function parseMarkdownPath(pathname) {
38
39
  function createServerApiClient(apiUrl, options) {
39
40
  const headers = localeHeaders(options?.locale);
40
41
  if (options?.draftId && options.authCookieHeader) headers[DRAFT_HEADER] = options.draftId;
42
+ else {
43
+ const publishing = publishingDraft();
44
+ if (publishing) headers[PUBLISHING_DRAFT_HEADER] = publishing;
45
+ }
41
46
  return createORPCClient(new RPCLink({
42
47
  url: `${apiUrl}/rpc`,
43
48
  headers,
@@ -1,9 +1,9 @@
1
1
  import { previewStore, selectIsCanvasShown } from "../preview/previewStore.js";
2
2
  import { shownRecordPage } from "../preview/pageRecord.js";
3
+ import { StudioEditingFallback } from "../preview/components/StudioEditingFallback.js";
3
4
  import { Navbar } from "../studio/components/Navbar.js";
4
5
  import { EditingFallbackContext } from "./editingFallback.js";
5
6
  import { PreviewToolbar } from "../preview/components/PreviewToolbar.js";
6
- import { StudioEditingFallback } from "../preview/components/StudioEditingFallback.js";
7
7
  import { ChatSidebar } from "../studio/components/ChatSidebar.js";
8
8
  import { FollowAgentProvider } from "../studio/FollowAgentProvider.js";
9
9
  import { c } from "react/compiler-runtime";
@@ -12,10 +12,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
12
12
  import studioCssUrl from "virtual:camox-studio-css";
13
13
 
14
14
  //#region src/features/runtime/RuntimeChrome.tsx
15
- const renderEditingFallback = ({ layoutPage, children }) => /* @__PURE__ */ jsx(StudioEditingFallback, {
16
- layoutPage,
17
- children
18
- });
15
+ const renderEditingFallback = ({ layoutPage }) => /* @__PURE__ */ jsx(StudioEditingFallback, { layoutPage });
19
16
  function RuntimeChrome(t0) {
20
17
  const $ = c(32);
21
18
  const { input, children } = t0;
@@ -0,0 +1,14 @@
1
+ //#region src/features/runtime/assetPrefix.ts
2
+ /**
3
+ * One build serves under any URL prefix: Live at `/`, a revision's preview at
4
+ * `/_camox/r/<revisionId>/`. Whatever loads the server bundle sets this global first, never per
5
+ * request: the bundle collects its asset URLs when it loads. Unset, assets are under `/`.
6
+ */
7
+ const ASSET_PREFIX_GLOBAL = "__CAMOX_ASSET_PREFIX__";
8
+ /** The prefix the server build's assets are under, ending with `/`. */
9
+ function assetPrefix() {
10
+ return globalThis["__CAMOX_ASSET_PREFIX__"] ?? "/";
11
+ }
12
+
13
+ //#endregion
14
+ export { ASSET_PREFIX_GLOBAL, assetPrefix };
@@ -1,7 +1,7 @@
1
1
  import { recordPageValues } from "../../core/recordItem.js";
2
- import { curatedPageTitle } from "../../core/layoutContext.js";
3
2
  import { getPageDestinations } from "../../core/pageDestinations.js";
4
3
  import { matchDerivedLayout } from "../../core/derivedRoutes.js";
4
+ import { curatedPageTitle } from "../../core/layoutContext.js";
5
5
 
6
6
  //#region src/features/runtime/breadcrumbs.ts
7
7
  /** The paths above a page's, from the home page down: none for the home page itself. */
@@ -2,18 +2,14 @@ import * as React from "react";
2
2
 
3
3
  //#region src/features/runtime/editingFallback.tsx
4
4
  /**
5
- * Renders the read-only page while the editor is still loading. The Studio
5
+ * Renders what holds the page's place while the editor is still loading. The Studio
6
6
  * chrome provides it, so visitors never download it.
7
7
  */
8
8
  const EditingFallbackContext = React.createContext(null);
9
- /** The read-only page shown until the editor activates, laid out for the Canvas when it shows. */
10
- function EditingFallbackSlot({ layoutPage, children }) {
9
+ /** What shows until the editor activates, laid out for the Canvas when it shows. */
10
+ function EditingFallbackSlot({ layoutPage }) {
11
11
  const render = React.use(EditingFallbackContext);
12
- if (!render) return children;
13
- return render({
14
- layoutPage,
15
- children
16
- });
12
+ return render ? render({ layoutPage }) : null;
17
13
  }
18
14
 
19
15
  //#endregion
@@ -1,7 +1,7 @@
1
1
  import { createRequestContext } from "../../core/requestContext.js";
2
- import { curatedPageTitle } from "../../core/layoutContext.js";
3
2
  import { getPageDestinations } from "../../core/pageDestinations.js";
4
3
  import { discoverLayoutPages } from "../../core/derivedRoutes.js";
4
+ import { curatedPageTitle } from "../../core/layoutContext.js";
5
5
 
6
6
  //#region src/features/runtime/livePages.ts
7
7
  /**