camox 0.63.0 → 0.66.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 (60) hide show
  1. package/dist/core/createApp.d.ts +28 -28
  2. package/dist/core/createLayout.d.ts +1 -1
  3. package/dist/core/derivedRoutes.js +3 -30
  4. package/dist/core/editing/createEditableBlock.d.ts +604 -604
  5. package/dist/features/canvas/CamoxCanvas.js +195 -173
  6. package/dist/features/canvas/canvasCamera.js +11 -2
  7. package/dist/features/canvas/canvasFrameCapture.js +5 -12
  8. package/dist/features/canvas/canvasFrameDocument.js +90 -29
  9. package/dist/features/canvas/canvasItems.js +22 -6
  10. package/dist/features/canvas/sidebar/ContentSidebar.js +19 -21
  11. package/dist/features/canvas/sidebar/LayoutPageOverview.js +22 -49
  12. package/dist/features/canvas/sidebar/LayoutPageSeo.js +73 -76
  13. package/dist/features/canvas/sidebar/PageInspector.js +61 -50
  14. package/dist/features/canvas/sidebar/PageInspectorSkeleton.js +4 -4
  15. package/dist/features/canvas/useCanvasCamera.js +36 -15
  16. package/dist/features/page/CuratedPageContent.js +2 -1
  17. package/dist/features/page/UnplacedBlockContent.js +52 -0
  18. package/dist/features/preview/components/PageInfo.js +103 -100
  19. package/dist/features/preview/components/PreviewPanel.js +40 -12
  20. package/dist/features/preview/components/SidebarStack.js +78 -9
  21. package/dist/features/preview/strayCaretGuard.js +34 -0
  22. package/dist/features/provider/CoreCamoxProvider.js +5 -3
  23. package/dist/features/routes/pageRuntime.d.ts +2 -0
  24. package/dist/features/routes/pageRuntime.js +8 -5
  25. package/dist/features/runtime/RuntimeChrome.js +66 -40
  26. package/dist/features/runtime/pageApp.js +111 -74
  27. package/dist/features/runtime/pageClient.js +22 -2
  28. package/dist/features/runtime/runtime.d.ts +10 -0
  29. package/dist/features/runtime/runtime.js +314 -4
  30. package/dist/features/runtime/runtimeRoutes.d.ts +5 -0
  31. package/dist/features/runtime/runtimeRoutes.js +5 -0
  32. package/dist/features/studio/components/AgentSidebar.js +3 -3
  33. package/dist/features/studio/components/ChatWidget.js +106 -75
  34. package/dist/features/studio/components/ConnectAgent.js +313 -0
  35. package/dist/features/studio/components/DraftsControl.js +172 -159
  36. package/dist/features/studio/components/Patrol.js +605 -350
  37. package/dist/features/studio/components/ReviewDraftSheet.js +566 -430
  38. package/dist/features/studio/components/Squareling.js +74 -41
  39. package/dist/features/studio/components/ThreadComposer.js +192 -107
  40. package/dist/features/studio/components/ThreadMessages.js +95 -18
  41. package/dist/features/studio/components/UnnamedDraftBar.js +349 -0
  42. package/dist/features/studio/components/clientLogoPaths.js +10 -0
  43. package/dist/features/studio/findingSentence.js +37 -0
  44. package/dist/features/studio/patrollerBio.js +40 -0
  45. package/dist/features/studio/reportSentence.js +15 -0
  46. package/dist/features/studio/studioStore.js +36 -1
  47. package/dist/features/studio/workingSentence.js +10 -7
  48. package/dist/lib/api-client.d.ts +1 -0
  49. package/dist/lib/auth.js +34 -32
  50. package/dist/lib/queries.js +9 -1
  51. package/dist/lib/revisions.js +25 -3
  52. package/dist/lib/server-auth-cookie.js +17 -4
  53. package/dist/lib/studio-reads.d.ts +1 -1
  54. package/dist/lib/studio-reads.js +5 -1
  55. package/dist/lib/use-thread.js +1 -1
  56. package/dist/package.js +1 -1
  57. package/dist/studio.css +1 -1
  58. package/package.json +15 -4
  59. package/dist/features/canvas/sidebar/DerivedPageInspector.js +0 -49
  60. package/dist/features/canvas/sidebar/SingletonPageInspector.js +0 -39
@@ -4,13 +4,20 @@ import { localeName } from "./localeOptions.js";
4
4
  const PHRASES = {
5
5
  translator: {
6
6
  verb: "Translating",
7
- locale: "into",
8
- singleLocale: "Add a language to translate into"
7
+ locale: "into"
9
8
  },
10
9
  seoManager: {
11
10
  verb: "Writing SEO for",
12
11
  verbFor: { file: "Describing" },
13
12
  locale: "in"
13
+ },
14
+ linkChecker: {
15
+ verb: "Checking links in",
16
+ locale: "in"
17
+ },
18
+ proofreader: {
19
+ verb: "Proofreading",
20
+ locale: "in"
14
21
  }
15
22
  };
16
23
  /** The most locales a burst names; past it, the first are named and the rest counted. */
@@ -46,10 +53,6 @@ function workingSentence(id, working) {
46
53
  inLocales(locales, phrase.locale)
47
54
  ].filter(Boolean).join(" ");
48
55
  }
49
- /** What a patroller says while idle in a project with a single locale, if anything. */
50
- function singleLocaleSentence(id) {
51
- return PHRASES[id].singleLocale;
52
- }
53
56
 
54
57
  //#endregion
55
- export { singleLocaleSentence, workingSentence };
58
+ export { workingSentence };
@@ -1,5 +1,6 @@
1
1
  import "./locale-path.js";
2
2
  import "./draft-scope.js";
3
+ import { createTanstackQueryUtils } from "@orpc/tanstack-query";
3
4
  import { Router } from "@camox/api-contract/router";
4
5
  import { RouterClient } from "@orpc/server";
5
6
  //#region src/lib/api-client.d.ts
package/dist/lib/auth.js CHANGED
@@ -251,58 +251,60 @@ function useSessionState(authClient, track) {
251
251
  return React.useSyncExternalStore(track ? subscribe : noSubscription, getSnapshot, getSnapshot);
252
252
  }
253
253
  function useAuthState() {
254
- const $ = c(9);
255
- const { authClient, initialAuthenticated: t0, exchangingToken: t1 } = useAuthContext();
254
+ const $ = c(10);
255
+ const { authClient, initialAuthenticated: t0, exchangingToken: t1, sessionless: t2 } = useAuthContext();
256
256
  const initialAuthenticated = t0 === void 0 ? false : t0;
257
257
  const exchangingToken = t1 === void 0 ? false : t1;
258
+ const sessionless = t2 === void 0 ? false : t2;
258
259
  const hydrated = React.useSyncExternalStore(subscribeToHydration, _temp2, _temp3);
259
- let t2;
260
- if ($[0] !== hydrated || $[1] !== initialAuthenticated) {
261
- t2 = initialAuthenticated || hydrated && hasStoredAuthCookie();
260
+ let t3;
261
+ if ($[0] !== hydrated || $[1] !== initialAuthenticated || $[2] !== sessionless) {
262
+ t3 = !sessionless && (initialAuthenticated || hydrated && hasStoredAuthCookie());
262
263
  $[0] = hydrated;
263
264
  $[1] = initialAuthenticated;
264
- $[2] = t2;
265
- } else t2 = $[2];
266
- const tracksSession = t2;
265
+ $[2] = sessionless;
266
+ $[3] = t3;
267
+ } else t3 = $[3];
268
+ const tracksSession = t3;
267
269
  const { data: session, isPending } = useSessionState(authClient, tracksSession);
268
270
  if (exchangingToken && !initialAuthenticated) {
269
- let t3;
270
- if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
271
- t3 = {
271
+ let t4;
272
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
273
+ t4 = {
272
274
  isAuthenticated: false,
273
275
  isLoading: true
274
276
  };
275
- $[3] = t3;
276
- } else t3 = $[3];
277
- return t3;
277
+ $[4] = t4;
278
+ } else t4 = $[4];
279
+ return t4;
278
280
  }
279
281
  if (!tracksSession) {
280
- const t3 = !hydrated;
281
- let t4;
282
- if ($[4] !== t3) {
283
- t4 = {
282
+ const t4 = !hydrated;
283
+ let t5;
284
+ if ($[5] !== t4) {
285
+ t5 = {
284
286
  isAuthenticated: false,
285
- isLoading: t3
287
+ isLoading: t4
286
288
  };
287
- $[4] = t3;
288
289
  $[5] = t4;
289
- } else t4 = $[5];
290
- return t4;
290
+ $[6] = t5;
291
+ } else t5 = $[6];
292
+ return t5;
291
293
  }
292
294
  const useInitial = !hydrated || isPending;
293
- const t3 = useInitial ? initialAuthenticated : !!session;
294
- const t4 = useInitial && !initialAuthenticated;
295
- let t5;
296
- if ($[6] !== t3 || $[7] !== t4) {
297
- t5 = {
298
- isAuthenticated: t3,
299
- isLoading: t4
295
+ const t4 = useInitial ? initialAuthenticated : !!session;
296
+ const t5 = useInitial && !initialAuthenticated;
297
+ let t6;
298
+ if ($[7] !== t4 || $[8] !== t5) {
299
+ t6 = {
300
+ isAuthenticated: t4,
301
+ isLoading: t5
300
302
  };
301
- $[6] = t3;
302
303
  $[7] = t4;
303
304
  $[8] = t5;
304
- } else t5 = $[8];
305
- return t5;
305
+ $[9] = t6;
306
+ } else t6 = $[9];
307
+ return t6;
306
308
  }
307
309
  function _temp3() {
308
310
  return false;
@@ -50,6 +50,14 @@ const draftMutations = {
50
50
  /** Drops the draft's copy of an entity it conflicts on, to use Live's version instead. */
51
51
  revertToLive: () => getOrpc().drafts.revertToLive.mutationOptions()
52
52
  };
53
+ /**
54
+ * What members do with a patroller's finding (ADR 0034): apply its fix in the draft viewed, or
55
+ * dismiss it for the whole project.
56
+ */
57
+ const patrolMutations = {
58
+ applyFix: () => getOrpc().patrol.applyFix.mutationOptions(),
59
+ dismissFinding: () => getOrpc().patrol.dismissFinding.mutationOptions()
60
+ };
53
61
  /** Adding and removing locales: content writes, so on Live they start a draft (ADR 0015). */
54
62
  const localeMutations = {
55
63
  add: () => getOrpc().locales.add.mutationOptions(),
@@ -131,4 +139,4 @@ const fileMutations = {
131
139
  };
132
140
 
133
141
  //#endregion
134
- export { blockMutations, collectionMutations, draftMutations, fileMutations, localeMutations, pageMutations, projectMutations, repeatableItemMutations };
142
+ export { blockMutations, collectionMutations, draftMutations, fileMutations, localeMutations, pageMutations, patrolMutations, projectMutations, repeatableItemMutations };
@@ -1,20 +1,42 @@
1
+ import { c } from "react/compiler-runtime";
1
2
  import * as React from "react";
2
3
 
3
4
  //#region src/lib/revisions.ts
4
5
  function createRevisions() {
5
6
  const listeners = /* @__PURE__ */ new Set();
7
+ const latest = /* @__PURE__ */ new Map();
6
8
  return {
7
9
  announce(revision) {
10
+ latest.set(revision.draftId, revision.revisionId);
8
11
  for (const listener of listeners) listener(revision);
9
12
  },
10
- subscribe(listener) {
13
+ subscribe: (listener) => {
11
14
  listeners.add(listener);
12
15
  return () => listeners.delete(listener);
13
- }
16
+ },
17
+ latest: (draftId) => latest.get(draftId) ?? null
14
18
  };
15
19
  }
16
20
  /** The studio's announced revisions. The signed-in studio provides it. */
17
21
  const RevisionsContext = React.createContext(null);
22
+ const noSubscription = () => () => {};
23
+ /**
24
+ * The last revision announced for `draftId` since the studio opened, or null, following each new
25
+ * one: what a URL rendered by the draft's code names, so it is fetched again once a new one built.
26
+ */
27
+ function useLatestRevision(draftId) {
28
+ const $ = c(3);
29
+ const revisions = React.use(RevisionsContext);
30
+ let t0;
31
+ if ($[0] !== draftId || $[1] !== revisions) {
32
+ t0 = () => revisions && draftId ? revisions.latest(draftId) : null;
33
+ $[0] = draftId;
34
+ $[1] = revisions;
35
+ $[2] = t0;
36
+ } else t0 = $[2];
37
+ const latest = t0;
38
+ return React.useSyncExternalStore(revisions?.subscribe ?? noSubscription, latest, latest);
39
+ }
18
40
 
19
41
  //#endregion
20
- export { RevisionsContext, createRevisions };
42
+ export { RevisionsContext, createRevisions, useLatestRevision };
@@ -1,3 +1,5 @@
1
+ import { LOOK_TOKEN_COOKIE } from "@camox/api-contract/drafts";
2
+
1
3
  //#region src/lib/server-auth-cookie.ts
2
4
  const SERVER_AUTH_COOKIE_NAME = "camox_auth_cookie";
3
5
  /** How long the mirror lasts. The API still refuses a session that expired before it. */
@@ -20,14 +22,25 @@ function getServerAuthCookieHeader(headers) {
20
22
  }
21
23
  /** The mirror's session in a `Cookie` header's value, or `document.cookie`. */
22
24
  function readServerAuthCookie(cookieHeader) {
23
- const mirror = cookieHeader.split(";").map((part) => part.trim()).find((part) => part.startsWith(`${SERVER_AUTH_COOKIE_NAME}=`));
24
- if (!mirror) return "";
25
+ return readCookie(cookieHeader, SERVER_AUTH_COOKIE_NAME);
26
+ }
27
+ /**
28
+ * The look token a request carries in `LOOK_TOKEN_COOKIE` (ADR 0032), set by the browser the
29
+ * agent's `look` tool drives for the website's host only. Empty when there's none.
30
+ */
31
+ function getLookToken(headers) {
32
+ return readCookie(headers.get("Cookie") ?? "", LOOK_TOKEN_COOKIE);
33
+ }
34
+ /** The cookie `name`'s value in a `Cookie` header's value, decoded, or empty when there's none. */
35
+ function readCookie(cookieHeader, name) {
36
+ const cookie = cookieHeader.split(";").map((part) => part.trim()).find((part) => part.startsWith(`${name}=`));
37
+ if (!cookie) return "";
25
38
  try {
26
- return decodeURIComponent(mirror.slice(18));
39
+ return decodeURIComponent(cookie.slice(name.length + 1));
27
40
  } catch {
28
41
  return "";
29
42
  }
30
43
  }
31
44
 
32
45
  //#endregion
33
- export { SERVER_AUTH_COOKIE_NAME, buildClearServerAuthCookieHeader, buildServerAuthCookieHeader, getServerAuthCookieHeader, readServerAuthCookie };
46
+ export { SERVER_AUTH_COOKIE_NAME, buildClearServerAuthCookieHeader, buildServerAuthCookieHeader, getLookToken, getServerAuthCookieHeader, readServerAuthCookie };
@@ -6,8 +6,8 @@ import { ApiClient } from "./api-client.js";
6
6
  import "./queries.js";
7
7
  import "react";
8
8
  import "@camox/api-contract/query-keys";
9
- import { QueryClient } from "@tanstack/react-query";
10
9
  import "@camox/api-contract/patrol";
10
+ import { QueryClient } from "@tanstack/react-query";
11
11
  //#region src/lib/studio-reads.d.ts
12
12
  type LayoutRead = Awaited<ReturnType<ApiClient["layouts"]["get"]>>;
13
13
  /** A layout's structure: its blocks before and after the content of its pages. */
@@ -5,6 +5,7 @@ import { collectFileIds } from "./normalized-data.js";
5
5
  import { c } from "react/compiler-runtime";
6
6
  import * as React from "react";
7
7
  import { inScope, queryKeys, scopeOfKey } from "@camox/api-contract/query-keys";
8
+ import { PATROLLER_IDS } from "@camox/api-contract/patrol";
8
9
  import { useQueries, useQuery, useSuspenseQuery } from "@tanstack/react-query";
9
10
 
10
11
  //#region src/lib/studio-reads.ts
@@ -209,7 +210,10 @@ const reads = {
209
210
  patrol: entry({
210
211
  key: ({ projectId }) => [...queryKeys.patrol.get, projectId],
211
212
  everyLocale: true,
212
- fetch: ({ api, signal }, { projectId }) => api.patrol.get({ projectId }, { signal })
213
+ fetch: ({ api, signal }, { projectId }) => api.patrol.get({
214
+ projectId,
215
+ patrollers: [...PATROLLER_IDS]
216
+ }, { signal })
213
217
  }),
214
218
  publishPreview: entry({
215
219
  key: ({ draftId }) => [
@@ -18,7 +18,7 @@ const OPEN = 1;
18
18
  *
19
19
  * When the agent on Live hands a request off to a draft it started, the studio that sent a
20
20
  * message of that run hears it (`onEnterDraft`), to enter the draft. The editor's Live thread
21
- * stays as it is, ending that turn on the draft's card, which every studio on Live shows.
21
+ * then starts afresh from the draft's card, which every studio on Live shows alone.
22
22
  *
23
23
  * It reports what each writing script touched (`touched`), for runs one of its messages was part
24
24
  * of only, so that the studio follows the agent; teammates' runs leave it where it is.
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.63.0";
2
+ var version = "0.66.0";
3
3
 
4
4
  //#endregion
5
5
  export { version };