camox 0.58.1 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/core/createBlock.js +1 -1
  2. package/dist/core/editing/createEditableBlock.js +1 -1
  3. package/dist/core/lib/recordScope.js +1 -1
  4. package/dist/features/canvas/CamoxCanvas.js +2 -2
  5. package/dist/features/canvas/CanvasOverlays.js +1 -1
  6. package/dist/features/canvas/sidebar/LayoutPageSeo.js +1 -1
  7. package/dist/features/canvas/sidebar/PageInspector.js +1 -1
  8. package/dist/features/content/CamoxContent.js +1 -1
  9. package/dist/features/content/components/MissingFromLocale.js +1 -1
  10. package/dist/features/page/CuratedPageContent.js +1 -1
  11. package/dist/features/preview/CamoxPreview.js +3 -3
  12. package/dist/features/preview/components/AddBlockSheet.js +1 -1
  13. package/dist/features/preview/components/AssetLightbox.js +1 -1
  14. package/dist/features/preview/components/BlockActionsPopover.js +1 -1
  15. package/dist/features/preview/components/CreatePageModal.js +2 -2
  16. package/dist/features/preview/components/FieldOverlayStyles.js +1 -1
  17. package/dist/features/preview/components/MobilePreviewDrawer.js +1 -1
  18. package/dist/features/preview/components/Overlays.js +1 -1
  19. package/dist/features/preview/components/PageInfo.js +1 -1
  20. package/dist/features/preview/components/PagePicker.js +1 -1
  21. package/dist/features/preview/components/PageTree.js +1 -1
  22. package/dist/features/preview/components/PreviewPanel.js +1 -1
  23. package/dist/features/preview/components/PreviewToolbar.js +1 -1
  24. package/dist/features/preview/components/SelectionEditor.js +1 -1
  25. package/dist/features/preview/components/StudioEditingFallback.js +1 -1
  26. package/dist/features/provider/AuthenticatedCamoxProvider.js +1 -1
  27. package/dist/features/provider/CamoxProvider.js +1 -1
  28. package/dist/features/provider/CoreCamoxProvider.js +3 -3
  29. package/dist/features/provider/components/CommandPalette.js +1 -1
  30. package/dist/features/renderer/rendererHost.js +3 -7
  31. package/dist/features/routes/pageRuntime.d.ts +2 -5
  32. package/dist/features/routes/pageRuntime.js +1 -2
  33. package/dist/features/runtime/RuntimeChrome.js +1 -1
  34. package/dist/features/runtime/navigationTarget.js +1 -2
  35. package/dist/features/runtime/pageApp.js +1 -1
  36. package/dist/features/runtime/rendererClient.js +1 -1
  37. package/dist/features/runtime/runtime.d.ts +0 -5
  38. package/dist/features/runtime/runtime.js +28 -32
  39. package/dist/features/runtime/worker.js +3 -3
  40. package/dist/features/settings/FaviconSettings.js +1 -1
  41. package/dist/features/studio/CamoxStudio.js +1 -1
  42. package/dist/features/studio/FollowAgentProvider.js +1 -1
  43. package/dist/features/studio/components/ChatWidget.js +1 -1
  44. package/dist/features/studio/components/DraftsControl.js +232 -201
  45. package/dist/features/studio/components/LocalePicker.js +2 -2
  46. package/dist/features/studio/components/Navbar.js +3 -3
  47. package/dist/features/studio/components/Patrol.js +1 -1
  48. package/dist/features/studio/components/ProjectMenu.js +2 -2
  49. package/dist/features/studio/components/ReviewDraftSheet.js +9 -15
  50. package/dist/features/studio/components/ThreadMessages.js +103 -21
  51. package/dist/features/studio/components/UserButton.js +2 -2
  52. package/dist/features/studio/draftLabel.js +1 -2
  53. package/dist/features/studio/draftNotice.js +0 -1
  54. package/dist/features/studio/showInCanvas.js +1 -1
  55. package/dist/features/vite/definitionsSync.js +14 -60
  56. package/dist/features/vite/devSignIn.js +1 -1
  57. package/dist/features/vite/runtimeDev.js +0 -1
  58. package/dist/features/vite/vite.d.ts +8 -5
  59. package/dist/features/vite/vite.js +34 -25
  60. package/dist/lib/api-client-server.js +25 -8
  61. package/dist/lib/api-client.js +2 -6
  62. package/dist/lib/auth.js +1 -1
  63. package/dist/lib/draft-scope.js +5 -25
  64. package/dist/lib/preview-handoff.js +1 -1
  65. package/dist/lib/server-auth-cookie.js +12 -10
  66. package/dist/lib/studio-reads.js +1 -1
  67. package/dist/lib/use-thread.js +31 -35
  68. package/dist/package.js +1 -1
  69. package/dist/studio.css +1 -1
  70. package/package.json +4 -4
  71. package/skills/camox/references/block-definitions.md +1 -1
  72. package/dist/features/vite/publishSeededContent.js +0 -30
  73. package/skills/camox-cli/SKILL.md +0 -15
  74. package/skills/camox-cli/references/cli-common.md +0 -58
  75. package/skills/camox-cli/references/cli-content.md +0 -181
  76. package/skills/camox-cli/references/cli-publishing.md +0 -26
@@ -1,19 +1,36 @@
1
- import { DRAFT_HEADER } from "@camox/api-contract/drafts";
1
+ import { DRAFT_CREATE_HEADER, DRAFT_HEADER, isContentWrite } from "@camox/api-contract/drafts";
2
2
  import { createORPCClient } from "@orpc/client";
3
3
  import { RPCLink } from "@orpc/client/fetch";
4
4
 
5
5
  //#region src/lib/api-client-server.ts
6
6
  /**
7
- * An API client for the plugin's own requests. `draftId` names the draft they read and write:
8
- * dev mode sends `development`, and a release names none, so it writes Live.
7
+ * An API client for the plugin's own requests. A release names no draft, so it writes Live. With
8
+ * `startsDraft`, its first content write starts that draft, as the studio's first write on Live
9
+ * does: reads see Live until a write has started it, and the draft from then on.
9
10
  */
10
- function createServerApiClient(apiUrl, authToken, draftId) {
11
- const headers = {};
12
- if (authToken) headers.Authorization = `Bearer ${authToken}`;
13
- if (draftId) headers[DRAFT_HEADER] = draftId;
11
+ function createServerApiClient(apiUrl, authToken, startsDraft) {
12
+ const base = {};
13
+ if (authToken) base.Authorization = `Bearer ${authToken}`;
14
+ let started = false;
14
15
  const link = new RPCLink({
15
16
  url: `${apiUrl}/rpc`,
16
- headers
17
+ headers: (_options, path) => {
18
+ if (!startsDraft) return base;
19
+ if (isContentWrite(path)) return {
20
+ ...base,
21
+ [DRAFT_HEADER]: startsDraft,
22
+ [DRAFT_CREATE_HEADER]: "1"
23
+ };
24
+ return started ? {
25
+ ...base,
26
+ [DRAFT_HEADER]: startsDraft
27
+ } : base;
28
+ },
29
+ fetch: async (request, init, _options, path) => {
30
+ const response = await fetch(request, init);
31
+ if (response.ok && isContentWrite(path)) started = true;
32
+ return response;
33
+ }
17
34
  });
18
35
  return createORPCClient(link);
19
36
  }
@@ -1,5 +1,5 @@
1
- import { getAuthCookieHeader, getAuthRequestCredentials } from "./auth.js";
2
1
  import { localeHeaders } from "./locale-path.js";
2
+ import { getAuthCookieHeader, getAuthRequestCredentials } from "./auth.js";
3
3
  import { DRAFT_HEADER, isContentWrite } from "@camox/api-contract/drafts";
4
4
  import { createORPCClient } from "@orpc/client";
5
5
  import { RPCLink } from "@orpc/client/fetch";
@@ -61,10 +61,6 @@ function createStudioClient(apiUrl, draftHeaders) {
61
61
  });
62
62
  return createORPCClient(link);
63
63
  }
64
- function getApiClient() {
65
- if (!_client) throw new Error("API client not initialized — call initApiClient first");
66
- return _client;
67
- }
68
64
  function getOrpc() {
69
65
  if (!_orpc) throw new Error("API client not initialized — call initApiClient first");
70
66
  return _orpc;
@@ -83,4 +79,4 @@ function getContentWriteHeaders() {
83
79
  }
84
80
 
85
81
  //#endregion
86
- export { getApiClient, getApiUrl, getContentWriteHeaders, getDraftReadClient, getOrpc, initApiClient };
82
+ export { getApiUrl, getContentWriteHeaders, getDraftReadClient, getOrpc, initApiClient };
package/dist/lib/auth.js CHANGED
@@ -230,7 +230,7 @@ function useProcessOtt(authClient, target, initialAuthenticated = false) {
230
230
  window.location.reload();
231
231
  return;
232
232
  } catch {
233
- setError("Preview sign-in failed. The link may have expired or already been used. Run camox preview again for a fresh link.");
233
+ setError("Preview sign-in failed. The link may have expired or already been used. Restart the dev server for a fresh link.");
234
234
  }
235
235
  setReady(true);
236
236
  })();
@@ -1,9 +1,8 @@
1
1
  import { UNNAMED_DEFAULT_LOCALE, localeHeaders } from "./locale-path.js";
2
- import { getApiClient } from "./api-client.js";
3
2
  import { c } from "react/compiler-runtime";
4
3
  import * as React from "react";
5
4
  import { generateUuidv7 } from "@camox/api-contract/ids";
6
- import { DEVELOPMENT_DRAFT, DRAFT_CREATE_HEADER, DRAFT_HEADER, DRAFT_SEARCH_PARAM } from "@camox/api-contract/drafts";
5
+ import { DRAFT_CREATE_HEADER, DRAFT_HEADER, DRAFT_SEARCH_PARAM } from "@camox/api-contract/drafts";
7
6
  import { LIVE_KEY_SCOPE, scopeOfKey } from "@camox/api-contract/query-keys";
8
7
 
9
8
  //#region src/lib/draft-scope.ts
@@ -17,22 +16,14 @@ function withoutDraft(href) {
17
16
  url.searchParams.delete(DRAFT_SEARCH_PARAM);
18
17
  return url.href;
19
18
  }
20
- function buildIsDevelopmentMode() {
21
- return typeof __CAMOX_DEVELOPMENT_DRAFT__ !== "undefined" && __CAMOX_DEVELOPMENT_DRAFT__;
22
- }
23
- function createDraftScope({ queryClient, browser, developmentMode = buildIsDevelopmentMode(), loadDevelopmentDraftId = async (projectId) => (await getApiClient().drafts.get({ projectId }))?.id }) {
24
- let current = developmentMode ? DEVELOPMENT_DRAFT : null;
19
+ function createDraftScope({ queryClient, browser }) {
20
+ let current = null;
25
21
  let locale = UNNAMED_DEFAULT_LOCALE;
26
22
  let pending = null;
27
- let developmentDraftId;
28
23
  let adoptedRender = false;
29
24
  const listeners = /* @__PURE__ */ new Set();
30
25
  const noticeListeners = /* @__PURE__ */ new Set();
31
26
  const pendingNotices = [];
32
- const draftOfRender = (rendered) => {
33
- if (!developmentMode) return rendered.draftId ?? null;
34
- return rendered.refusedDraft ? null : DEVELOPMENT_DRAFT;
35
- };
36
27
  const notify = (move) => {
37
28
  for (const listener of listeners) listener({
38
29
  draftId: current,
@@ -75,7 +66,7 @@ function createDraftScope({ queryClient, browser, developmentMode = buildIsDevel
75
66
  },
76
67
  settle: noop
77
68
  };
78
- if (!contentWrite || developmentMode) return {
69
+ if (!contentWrite) return {
79
70
  headers: inLocale,
80
71
  settle: noop
81
72
  };
@@ -102,7 +93,6 @@ function createDraftScope({ queryClient, browser, developmentMode = buildIsDevel
102
93
  return {
103
94
  current: () => current,
104
95
  locale: () => locale,
105
- developmentMode,
106
96
  subscribe(listener) {
107
97
  listeners.add(listener);
108
98
  return () => listeners.delete(listener);
@@ -114,7 +104,6 @@ function createDraftScope({ queryClient, browser, developmentMode = buildIsDevel
114
104
  return true;
115
105
  },
116
106
  hrefFor(to, { from, draft } = {}) {
117
- if (developmentMode) return to;
118
107
  const base = new URL(from ?? browser?.location.href ?? "http://camox.local");
119
108
  const target = new URL(to, base);
120
109
  if (target.origin !== base.origin) return to;
@@ -131,7 +120,7 @@ function createDraftScope({ queryClient, browser, developmentMode = buildIsDevel
131
120
  replaceUrl(null);
132
121
  if (rendered.refusedDraft.notice) report(rendered.refusedDraft.notice);
133
122
  }
134
- const next = draftOfRender(rendered);
123
+ const next = rendered.draftId ?? null;
135
124
  if (next !== null) pending = null;
136
125
  const firstRender = !adoptedRender;
137
126
  adoptedRender = true;
@@ -149,7 +138,6 @@ function createDraftScope({ queryClient, browser, developmentMode = buildIsDevel
149
138
  },
150
139
  leave(ended, { notice = false } = {}) {
151
140
  queryClient.invalidateQueries({ queryKey: ["camox"] });
152
- if (developmentMode) return false;
153
141
  pending = null;
154
142
  current = null;
155
143
  replaceUrl(null);
@@ -158,19 +146,11 @@ function createDraftScope({ queryClient, browser, developmentMode = buildIsDevel
158
146
  ...ended
159
147
  });
160
148
  notify("left");
161
- return true;
162
149
  },
163
150
  onNotice(listener) {
164
151
  noticeListeners.add(listener);
165
152
  for (const notice of pendingNotices.splice(0)) listener(notice);
166
153
  return () => noticeListeners.delete(listener);
167
- },
168
- async keyScopeOfDraft(draftId, projectId) {
169
- if (current !== DEVELOPMENT_DRAFT) return draftId;
170
- developmentDraftId ??= loadDevelopmentDraftId(projectId).catch(() => {
171
- developmentDraftId = void 0;
172
- });
173
- return draftId === await developmentDraftId ? DEVELOPMENT_DRAFT : draftId;
174
154
  }
175
155
  };
176
156
  }
@@ -6,7 +6,7 @@ function validatePreviewTarget(raw, target) {
6
6
  const expected = JSON.parse(raw);
7
7
  if (expected.projectUuid === target.projectUuid && typeof expected.apiUrl === "string" && expected.apiUrl.replace(/\/+$/, "") === target.apiUrl.replace(/\/+$/, "")) return null;
8
8
  } catch {}
9
- return "Preview destination does not match the CLI project or backend. Check --cwd and --url, then run camox preview again.";
9
+ return "This sign-in link is for another project or backend. Open the one your dev server printed.";
10
10
  }
11
11
 
12
12
  //#endregion
@@ -3,18 +3,20 @@ const SERVER_AUTH_COOKIE_NAME = "camox_auth_cookie";
3
3
  function buildClearServerAuthCookieHeader() {
4
4
  return `${SERVER_AUTH_COOKIE_NAME}=; Path=/; SameSite=Lax; Max-Age=0`;
5
5
  }
6
- /** Forward credentials to the API for validation; cookie presence is not authentication.
7
- * Cross-domain sessions use the mirror. Same-host sessions (including localhost
8
- * on different ports) may only have Better Auth's native, HttpOnly cookie.
6
+ /**
7
+ * The session to forward to the API for validation; cookie presence is not authentication. Only
8
+ * the studio's session counts, which the SDK mirrors into `camox_auth_cookie`. Better Auth's own
9
+ * cookies don't: on a Custom Domain like camox.ai they're the dashboard's session, set for every
10
+ * host (ADR 0028). Mirrored in apps/dispatch/src/previews.ts: keep the two in sync.
9
11
  */
10
12
  function getServerAuthCookieHeader(headers) {
11
- const cookies = (headers.get("Cookie") ?? "").split(";").map((part) => part.trim());
12
- const mirror = cookies.find((part) => part.startsWith(`${SERVER_AUTH_COOKIE_NAME}=`));
13
- if (mirror) try {
14
- const value = decodeURIComponent(mirror.slice(18));
15
- if (value) return value;
16
- } catch {}
17
- return cookies.filter((part) => part.startsWith("better-auth.session_token=") || part.startsWith("__Secure-better-auth.session_token=")).join("; ");
13
+ const mirror = (headers.get("Cookie") ?? "").split(";").map((part) => part.trim()).find((part) => part.startsWith(`${SERVER_AUTH_COOKIE_NAME}=`));
14
+ if (!mirror) return "";
15
+ try {
16
+ return decodeURIComponent(mirror.slice(18));
17
+ } catch {
18
+ return "";
19
+ }
18
20
  }
19
21
 
20
22
  //#endregion
@@ -484,7 +484,7 @@ function createStudioCache({ queryClient, scope, api, runtimeJson = (endpoint, b
484
484
  message,
485
485
  projectId
486
486
  });
487
- const keyScope = message.draftId === null ? null : await scope.keyScopeOfDraft(message.draftId, projectId);
487
+ const keyScope = message.draftId;
488
488
  await Promise.all(message.targets.map((queryKey) => queryClient.invalidateQueries({
489
489
  queryKey,
490
490
  predicate: keyScope === null ? void 0 : (query) => scopeOfKey(query.queryKey) === keyScope
@@ -1,7 +1,6 @@
1
1
  import { apiSocketOptions } from "./api-socket.js";
2
2
  import { c } from "react/compiler-runtime";
3
3
  import * as React from "react";
4
- import { DEVELOPMENT_DRAFT } from "@camox/api-contract/drafts";
5
4
  import { usePartySocket } from "partysocket/react";
6
5
  import { EMPTY_THREAD_STATE, THREAD_ENDED_CLOSE_CODE, reduceThreadFrame, threadViewOf } from "@camox/api-contract/threads";
7
6
 
@@ -11,10 +10,10 @@ const OPEN = 1;
11
10
  /**
12
11
  * Follows a thread in a project over its socket, folding Pi's events into the thread's view: the
13
12
  * signed-in editor's Live thread when `draftId` is null, or the draft's thread, which `draftId`
14
- * names as the draft header does (`development` in dev mode). A message is resent with its
13
+ * names. A message is resent with its
15
14
  * operation id each time the thread shows itself after the socket opens, until it accepts it,
16
15
  * which records it once, or turns it down. A draft's thread that ended with its draft isn't
17
- * reconnected to, but for a development draft's, which moves on to a new thread. Mount it once
16
+ * reconnected to. Mount it once
18
17
  * per project and draft.
19
18
  *
20
19
  * When the agent on Live hands a request off to a draft it started, the studio that sent a
@@ -25,7 +24,7 @@ const OPEN = 1;
25
24
  * of only, so that the studio follows the agent; teammates' runs leave it where it is.
26
25
  */
27
26
  function useThread(apiUrl, projectId, draftId, t0) {
28
- const $ = c(47);
27
+ const $ = c(46);
29
28
  let t1;
30
29
  if ($[0] !== t0) {
31
30
  t1 = t0 === void 0 ? {} : t0;
@@ -71,17 +70,14 @@ function useThread(apiUrl, projectId, draftId, t0) {
71
70
  basePath: draftId ? `threads/draft/${projectId}/${draftId}` : `threads/live/${projectId}`,
72
71
  maxEnqueuedMessages: 0,
73
72
  shouldReconnectOnClose: _temp,
74
- onClose(event_0) {
75
- if (event_0.code === THREAD_ENDED_CLOSE_CODE && draftId === DEVELOPMENT_DRAFT) socket.reconnect();
76
- },
77
73
  onOpen() {
78
74
  shown.current = false;
79
75
  },
80
- onMessage(event_1) {
81
- if (typeof event_1.data !== "string") return;
76
+ onMessage(event_0) {
77
+ if (typeof event_0.data !== "string") return;
82
78
  let frame;
83
79
  try {
84
- frame = JSON.parse(event_1.data);
80
+ frame = JSON.parse(event_0.data);
85
81
  } catch {
86
82
  return;
87
83
  }
@@ -184,6 +180,7 @@ function useThread(apiUrl, projectId, draftId, t0) {
184
180
  } else t11 = $[30];
185
181
  React.useEffect(t10, t11);
186
182
  let t12;
183
+ let t13;
187
184
  if ($[31] !== socket) {
188
185
  t12 = () => {
189
186
  const resync = () => {
@@ -192,18 +189,17 @@ function useThread(apiUrl, projectId, draftId, t0) {
192
189
  document.addEventListener("visibilitychange", resync);
193
190
  return () => document.removeEventListener("visibilitychange", resync);
194
191
  };
192
+ t13 = [socket];
195
193
  $[31] = socket;
196
194
  $[32] = t12;
197
- } else t12 = $[32];
198
- let t13;
199
- if ($[33] !== socket) {
200
- t13 = [socket];
201
- $[33] = socket;
202
- $[34] = t13;
203
- } else t13 = $[34];
195
+ $[33] = t13;
196
+ } else {
197
+ t12 = $[32];
198
+ t13 = $[33];
199
+ }
204
200
  React.useEffect(t12, t13);
205
201
  let t14;
206
- if ($[35] !== sent || $[36] !== socket) {
202
+ if ($[34] !== sent || $[35] !== socket) {
207
203
  t14 = (text_0) => {
208
204
  const operationId_3 = crypto.randomUUID();
209
205
  sent.add(operationId_3);
@@ -215,21 +211,21 @@ function useThread(apiUrl, projectId, draftId, t0) {
215
211
  text: text_0
216
212
  });
217
213
  };
218
- $[35] = sent;
219
- $[36] = socket;
220
- $[37] = t14;
221
- } else t14 = $[37];
214
+ $[34] = sent;
215
+ $[35] = socket;
216
+ $[36] = t14;
217
+ } else t14 = $[36];
222
218
  const send = t14;
223
219
  let t15;
224
- if ($[38] !== socket) {
220
+ if ($[37] !== socket) {
225
221
  t15 = () => transmit(socket, { type: "stop" });
226
- $[38] = socket;
227
- $[39] = t15;
228
- } else t15 = $[39];
222
+ $[37] = socket;
223
+ $[38] = t15;
224
+ } else t15 = $[38];
229
225
  const stop = t15;
230
226
  const t16 = unaccepted.size > 0;
231
227
  let t17;
232
- if ($[40] !== error || $[41] !== send || $[42] !== stop || $[43] !== t16 || $[44] !== touched || $[45] !== view) {
228
+ if ($[39] !== error || $[40] !== send || $[41] !== stop || $[42] !== t16 || $[43] !== touched || $[44] !== view) {
233
229
  t17 = {
234
230
  view,
235
231
  sending: t16,
@@ -238,14 +234,14 @@ function useThread(apiUrl, projectId, draftId, t0) {
238
234
  error,
239
235
  touched
240
236
  };
241
- $[40] = error;
242
- $[41] = send;
243
- $[42] = stop;
244
- $[43] = t16;
245
- $[44] = touched;
246
- $[45] = view;
247
- $[46] = t17;
248
- } else t17 = $[46];
237
+ $[39] = error;
238
+ $[40] = send;
239
+ $[41] = stop;
240
+ $[42] = t16;
241
+ $[43] = touched;
242
+ $[44] = view;
243
+ $[45] = t17;
244
+ } else t17 = $[45];
249
245
  return t17;
250
246
  }
251
247
  function _temp2(count) {
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.58.1";
2
+ var version = "0.59.0";
3
3
 
4
4
  //#endregion
5
5
  export { version };