camox 0.62.0 → 0.65.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 (42) 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/canvasCamera.js +11 -2
  6. package/dist/features/canvas/useCanvasCamera.js +14 -10
  7. package/dist/features/page/CuratedPageContent.js +2 -1
  8. package/dist/features/page/UnplacedBlockContent.js +52 -0
  9. package/dist/features/preview/components/PreviewPanel.js +40 -12
  10. package/dist/features/preview/components/SidebarStack.js +78 -9
  11. package/dist/features/preview/strayCaretGuard.js +34 -0
  12. package/dist/features/provider/CoreCamoxProvider.js +5 -3
  13. package/dist/features/routes/pageRuntime.d.ts +2 -0
  14. package/dist/features/routes/pageRuntime.js +8 -5
  15. package/dist/features/runtime/pageApp.js +111 -74
  16. package/dist/features/runtime/pageClient.js +22 -2
  17. package/dist/features/runtime/runtime.d.ts +10 -0
  18. package/dist/features/runtime/runtime.js +314 -4
  19. package/dist/features/runtime/runtimeRoutes.d.ts +5 -0
  20. package/dist/features/runtime/runtimeRoutes.js +5 -0
  21. package/dist/features/studio/components/ChatWidget.js +88 -62
  22. package/dist/features/studio/components/ConnectAgent.js +313 -0
  23. package/dist/features/studio/components/Patrol.js +455 -205
  24. package/dist/features/studio/components/ReviewDraftSheet.js +431 -334
  25. package/dist/features/studio/components/Squareling.js +74 -41
  26. package/dist/features/studio/components/ThreadComposer.js +130 -107
  27. package/dist/features/studio/components/ThreadMessages.js +136 -30
  28. package/dist/features/studio/components/clientLogoPaths.js +10 -0
  29. package/dist/features/studio/findingSentence.js +37 -0
  30. package/dist/features/studio/reportSentence.js +15 -0
  31. package/dist/features/studio/studioStore.js +23 -1
  32. package/dist/features/studio/workingSentence.js +8 -0
  33. package/dist/lib/api-client.d.ts +1 -0
  34. package/dist/lib/auth.js +34 -32
  35. package/dist/lib/locale-path.js +1 -7
  36. package/dist/lib/queries.js +9 -1
  37. package/dist/lib/server-auth-cookie.js +17 -4
  38. package/dist/lib/studio-reads.d.ts +1 -1
  39. package/dist/lib/studio-reads.js +5 -1
  40. package/dist/package.js +1 -1
  41. package/dist/studio.css +1 -1
  42. package/package.json +16 -5
@@ -3,7 +3,7 @@ import { Squareling } from "./Squareling.js";
3
3
  import { ThreadMarkdown } from "./ThreadMarkdown.js";
4
4
  import { c } from "react/compiler-runtime";
5
5
  import * as React from "react";
6
- import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Avatar, AvatarFallback, AvatarImage } from "@camox/ui/avatar";
8
8
  import { Bubble, BubbleContent } from "@camox/ui/bubble";
9
9
  import { Message, MessageAvatar, MessageContent, MessageHeader } from "@camox/ui/message";
@@ -274,6 +274,7 @@ const SQUARELING_STATUSES = {
274
274
  exploring: "exploring",
275
275
  changing: "changing",
276
276
  building: "building",
277
+ checking: "checking",
277
278
  fixing: "fixing",
278
279
  startingDraft: "changing"
279
280
  };
@@ -282,6 +283,7 @@ const ACTIVITY_LINES = {
282
283
  exploring: "Looking around…",
283
284
  changing: "Making changes…",
284
285
  building: "Building…",
286
+ checking: "Checking the design…",
285
287
  fixing: "Fixing the build…",
286
288
  startingDraft: "Starting a draft…"
287
289
  };
@@ -292,7 +294,7 @@ const PUSH_OUTCOMES = {
292
294
  notPreviewed: "Built, but the preview can’t show it."
293
295
  };
294
296
  function ThreadMessageRow(t0) {
295
- const $ = c(36);
297
+ const $ = c(44);
296
298
  const { message, onOpenDraft } = t0;
297
299
  if (message.from === "draft") {
298
300
  let t1;
@@ -399,60 +401,88 @@ function ThreadMessageRow(t0) {
399
401
  } else t10 = $[25];
400
402
  return t10;
401
403
  }
404
+ if (message.from === "connectedAgent") {
405
+ const { member, client } = message;
406
+ const t1 = client.verified ? void 0 : client.name;
407
+ const t2 = `${member.name}’s ${client.verified ? client.name : "agent"}`;
408
+ let t3;
409
+ if ($[26] !== t1 || $[27] !== t2) {
410
+ t3 = /* @__PURE__ */ jsx(MessageHeader, {
411
+ className: "text-muted-foreground text-xs",
412
+ title: t1,
413
+ children: t2
414
+ });
415
+ $[26] = t1;
416
+ $[27] = t2;
417
+ $[28] = t3;
418
+ } else t3 = $[28];
419
+ let t4;
420
+ if ($[29] !== message.text) {
421
+ t4 = /* @__PURE__ */ jsx(Bubble, {
422
+ variant: "muted",
423
+ className: "max-w-full",
424
+ children: /* @__PURE__ */ jsx(BubbleContent, { children: /* @__PURE__ */ jsx(CroppedText, { text: message.text }) })
425
+ });
426
+ $[29] = message.text;
427
+ $[30] = t4;
428
+ } else t4 = $[30];
429
+ let t5;
430
+ if ($[31] !== t3 || $[32] !== t4) {
431
+ t5 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs(MessageContent, { children: [t3, t4] }) });
432
+ $[31] = t3;
433
+ $[32] = t4;
434
+ $[33] = t5;
435
+ } else t5 = $[33];
436
+ return t5;
437
+ }
402
438
  if (message.from === "camox") {
403
439
  let t1;
404
- if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
440
+ if ($[34] === Symbol.for("react.memo_cache_sentinel")) {
405
441
  t1 = /* @__PURE__ */ jsx(MessageHeader, {
406
442
  className: "text-muted-foreground text-xs",
407
443
  children: "Camox"
408
444
  });
409
- $[26] = t1;
410
- } else t1 = $[26];
445
+ $[34] = t1;
446
+ } else t1 = $[34];
411
447
  let t2;
412
- if ($[27] !== message.text) {
448
+ if ($[35] !== message.text) {
413
449
  t2 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs(MessageContent, { children: [t1, /* @__PURE__ */ jsx(Bubble, {
414
450
  variant: "muted",
415
451
  className: "max-w-full",
416
- children: /* @__PURE__ */ jsx(BubbleContent, {
417
- className: "whitespace-pre-wrap",
418
- children: message.text
419
- })
452
+ children: /* @__PURE__ */ jsx(BubbleContent, { children: /* @__PURE__ */ jsx(CroppedText, { text: message.text }) })
420
453
  })] }) });
421
- $[27] = message.text;
422
- $[28] = t2;
423
- } else t2 = $[28];
454
+ $[35] = message.text;
455
+ $[36] = t2;
456
+ } else t2 = $[36];
424
457
  return t2;
425
458
  }
426
459
  let t1;
427
- if ($[29] !== message.sender) {
460
+ if ($[37] !== message.sender) {
428
461
  t1 = /* @__PURE__ */ jsx(MessageAvatar, { children: /* @__PURE__ */ jsx(SenderAvatar, { sender: message.sender }) });
429
- $[29] = message.sender;
430
- $[30] = t1;
431
- } else t1 = $[30];
462
+ $[37] = message.sender;
463
+ $[38] = t1;
464
+ } else t1 = $[38];
432
465
  let t2;
433
- if ($[31] !== message.text) {
466
+ if ($[39] !== message.text) {
434
467
  t2 = /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Bubble, {
435
468
  align: "end",
436
469
  variant: "muted",
437
470
  className: "max-w-full",
438
- children: /* @__PURE__ */ jsx(BubbleContent, {
439
- className: "whitespace-pre-wrap",
440
- children: message.text
441
- })
471
+ children: /* @__PURE__ */ jsx(BubbleContent, { children: /* @__PURE__ */ jsx(CroppedText, { text: message.text }) })
442
472
  }) });
443
- $[31] = message.text;
444
- $[32] = t2;
445
- } else t2 = $[32];
473
+ $[39] = message.text;
474
+ $[40] = t2;
475
+ } else t2 = $[40];
446
476
  let t3;
447
- if ($[33] !== t1 || $[34] !== t2) {
477
+ if ($[41] !== t1 || $[42] !== t2) {
448
478
  t3 = /* @__PURE__ */ jsxs(Message, {
449
479
  align: "end",
450
480
  children: [t1, t2]
451
481
  });
452
- $[33] = t1;
453
- $[34] = t2;
454
- $[35] = t3;
455
- } else t3 = $[35];
482
+ $[41] = t1;
483
+ $[42] = t2;
484
+ $[43] = t3;
485
+ } else t3 = $[43];
456
486
  return t3;
457
487
  }
458
488
  function _temp(subject, index) {
@@ -461,6 +491,82 @@ function _temp(subject, index) {
461
491
  children: subject
462
492
  }, index);
463
493
  }
494
+ /**
495
+ * A message's text, cropped past its fourth line: the next ones fade out, above a button that
496
+ * shows it all. A message that fits shows as is.
497
+ */
498
+ function CroppedText(t0) {
499
+ const $ = c(17);
500
+ const { text } = t0;
501
+ const node = React.useRef(null);
502
+ const [overflows, setOverflows] = React.useState(false);
503
+ const [expanded, setExpanded] = React.useState(false);
504
+ let t1;
505
+ if ($[0] !== expanded) {
506
+ t1 = () => {
507
+ const element = node.current;
508
+ if (!element || expanded) return;
509
+ const measure = () => setOverflows(element.scrollHeight > element.clientHeight + 1);
510
+ measure();
511
+ const observer = new ResizeObserver(measure);
512
+ observer.observe(element);
513
+ return () => observer.disconnect();
514
+ };
515
+ $[0] = expanded;
516
+ $[1] = t1;
517
+ } else t1 = $[1];
518
+ let t2;
519
+ if ($[2] !== expanded || $[3] !== text) {
520
+ t2 = [text, expanded];
521
+ $[2] = expanded;
522
+ $[3] = text;
523
+ $[4] = t2;
524
+ } else t2 = $[4];
525
+ React.useLayoutEffect(t1, t2);
526
+ const cropped = !expanded && overflows;
527
+ const t3 = cropped || void 0;
528
+ const t4 = !expanded && "max-h-[5.5lh] overflow-hidden";
529
+ const t5 = cropped && "mask-[linear-gradient(to_bottom,black_4lh,transparent)]";
530
+ let t6;
531
+ if ($[5] !== t4 || $[6] !== t5) {
532
+ t6 = cn("whitespace-pre-wrap", t4, t5);
533
+ $[5] = t4;
534
+ $[6] = t5;
535
+ $[7] = t6;
536
+ } else t6 = $[7];
537
+ let t7;
538
+ if ($[8] !== t3 || $[9] !== t6 || $[10] !== text) {
539
+ t7 = /* @__PURE__ */ jsx("div", {
540
+ ref: node,
541
+ "data-thread-cropped": t3,
542
+ className: t6,
543
+ children: text
544
+ });
545
+ $[8] = t3;
546
+ $[9] = t6;
547
+ $[10] = text;
548
+ $[11] = t7;
549
+ } else t7 = $[11];
550
+ let t8;
551
+ if ($[12] !== cropped) {
552
+ t8 = cropped && /* @__PURE__ */ jsx("button", {
553
+ type: "button",
554
+ onClick: () => setExpanded(true),
555
+ className: "focus-visible:ring-ring mt-1 rounded-sm font-medium hover:underline focus-visible:ring-2 focus-visible:outline-none",
556
+ children: "Show full message"
557
+ });
558
+ $[12] = cropped;
559
+ $[13] = t8;
560
+ } else t8 = $[13];
561
+ let t9;
562
+ if ($[14] !== t7 || $[15] !== t8) {
563
+ t9 = /* @__PURE__ */ jsxs(Fragment, { children: [t7, t8] });
564
+ $[14] = t7;
565
+ $[15] = t8;
566
+ $[16] = t9;
567
+ } else t9 = $[16];
568
+ return t9;
569
+ }
464
570
  const initials = (name) => name.split(" ").map((part) => part[0]).join("").toUpperCase().slice(0, 2);
465
571
  const DRAFT_CARD_CLASSES = "bg-muted/50 flex w-full flex-col items-start gap-0.5 rounded-lg border px-3 py-2 text-left text-sm";
466
572
  /**
@@ -0,0 +1,10 @@
1
+ //#region src/features/studio/components/clientLogoPaths.ts
2
+ /** Each client's mark, as one SVG path on a 24×24 box. */
3
+ const CLIENT_LOGO_PATHS = {
4
+ claude: "m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z",
5
+ chatgpt: "M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z",
6
+ copilot: "M23.922 16.997C23.061 18.492 18.063 22.02 12 22.02 5.937 22.02.939 18.492.078 16.997A.641.641 0 0 1 0 16.741v-2.869a.883.883 0 0 1 .053-.22c.372-.935 1.347-2.292 2.605-2.656.167-.429.414-1.055.644-1.517a10.098 10.098 0 0 1-.052-1.086c0-1.331.282-2.499 1.132-3.368.397-.406.89-.717 1.474-.952C7.255 2.937 9.248 1.98 11.978 1.98c2.731 0 4.767.957 6.166 2.093.584.235 1.077.546 1.474.952.85.869 1.132 2.037 1.132 3.368 0 .368-.014.733-.052 1.086.23.462.477 1.088.644 1.517 1.258.364 2.233 1.721 2.605 2.656a.841.841 0 0 1 .053.22v2.869a.641.641 0 0 1-.078.256Zm-11.75-5.992h-.344a4.359 4.359 0 0 1-.355.508c-.77.947-1.918 1.492-3.508 1.492-1.725 0-2.989-.359-3.782-1.259a2.137 2.137 0 0 1-.085-.104L4 11.746v6.585c1.435.779 4.514 2.179 8 2.179 3.486 0 6.565-1.4 8-2.179v-6.585l-.098-.104s-.033.045-.085.104c-.793.9-2.057 1.259-3.782 1.259-1.59 0-2.738-.545-3.508-1.492a4.359 4.359 0 0 1-.355-.508Zm2.328 3.25c.549 0 1 .451 1 1v2c0 .549-.451 1-1 1-.549 0-1-.451-1-1v-2c0-.549.451-1 1-1Zm-5 0c.549 0 1 .451 1 1v2c0 .549-.451 1-1 1-.549 0-1-.451-1-1v-2c0-.549.451-1 1-1Zm3.313-6.185c.136 1.057.403 1.913.878 2.497.442.544 1.134.938 2.344.938 1.573 0 2.292-.337 2.657-.751.384-.435.558-1.15.558-2.361 0-1.14-.243-1.847-.705-2.319-.477-.488-1.319-.862-2.824-1.025-1.487-.161-2.192.138-2.533.529-.269.307-.437.808-.438 1.578v.021c0 .265.021.562.063.893Zm-1.626 0c.042-.331.063-.628.063-.894v-.02c-.001-.77-.169-1.271-.438-1.578-.341-.391-1.046-.69-2.533-.529-1.505.163-2.347.537-2.824 1.025-.462.472-.705 1.179-.705 2.319 0 1.211.175 1.926.558 2.361.365.414 1.084.751 2.657.751 1.21 0 1.902-.394 2.344-.938.475-.584.742-1.44.878-2.497Z"
7
+ };
8
+
9
+ //#endregion
10
+ export { CLIENT_LOGO_PATHS };
@@ -0,0 +1,37 @@
1
+ import { localeName } from "./localeOptions.js";
2
+ import { findingSentence } from "@camox/api-contract/patrol";
3
+
4
+ //#region src/features/studio/findingSentence.ts
5
+ /** Quoted as editors read it. */
6
+ const quoted = (text) => `“${text}”`;
7
+ /** What a dismissal dismissed, briefly: "Links to About", or the text it was about. */
8
+ function dismissedSentence(about) {
9
+ switch (about.kind) {
10
+ case "missingPage": return `Links to ${about.page ?? "a missing page"}`;
11
+ case "brokenLink": return `Links to ${about.url}`;
12
+ default: return quoted(about.text);
13
+ }
14
+ }
15
+ /** What dismissing a finding says, as its button says it: a broken link's is "This link is fine". */
16
+ function dismissLabel(about) {
17
+ return about.kind === "brokenLink" ? "This link is fine" : "Dismiss";
18
+ }
19
+ /** What applying a fix does, as its button says it. */
20
+ function fixLabel(fix) {
21
+ switch (fix.kind) {
22
+ case "removeLink": return "Remove the link";
23
+ case "replaceText": return `Replace with ${quoted(fix.replacement)}`;
24
+ }
25
+ }
26
+ /**
27
+ * What "Ask the agent" fills the draft's chat with: who found what, where, and the fix it
28
+ * suggests, for the agent to find among the draft's findings and fix.
29
+ */
30
+ function askAgentMessage(finding, role) {
31
+ const where = `(in ${localeName(finding.locale)}, field ${quoted(finding.field)})`;
32
+ const fix = finding.fix ? ` Suggested fix: ${fixLabel(finding.fix)}.` : "";
33
+ return `${role} finding: ${findingSentence(finding)} ${where}.${fix}`;
34
+ }
35
+
36
+ //#endregion
37
+ export { askAgentMessage, dismissLabel, dismissedSentence, findingSentence, fixLabel };
@@ -26,6 +26,21 @@ const PHRASES = {
26
26
  publishedCatchUp: {
27
27
  done: "Published the caught-up translations",
28
28
  failed: "Couldn't publish the caught-up translations"
29
+ },
30
+ removedLink: {
31
+ done: "Removed a link in",
32
+ failed: "Couldn't remove a link in",
33
+ locale: "in"
34
+ },
35
+ fixedLink: {
36
+ done: "Fixed a link in",
37
+ failed: "Couldn't fix a link in",
38
+ locale: "in"
39
+ },
40
+ fixedMistake: {
41
+ done: "Fixed a typo in",
42
+ failed: "Couldn't fix a typo in",
43
+ locale: "in"
29
44
  }
30
45
  };
31
46
  /** A report as a sentence: "Wrote SEO for Pricing in German", or the text an AI wrote. */
@@ -11,7 +11,9 @@ const studioStore = createStore({
11
11
  },
12
12
  isDeleteDraftDialogOpen: false,
13
13
  localeDialog: null,
14
- websiteLocation: null
14
+ websiteLocation: null,
15
+ agentAsk: null,
16
+ agentAsks: 0
15
17
  },
16
18
  on: {
17
19
  openCommandPalette: (context) => {
@@ -88,6 +90,26 @@ const studioStore = createStore({
88
90
  localeDialog: null
89
91
  };
90
92
  },
93
+ askAgent: (context, event) => {
94
+ return {
95
+ ...context,
96
+ reviewDraftSheet: {
97
+ open: false,
98
+ startRenaming: false
99
+ },
100
+ agentAsk: {
101
+ text: event.text,
102
+ id: context.agentAsks + 1
103
+ },
104
+ agentAsks: context.agentAsks + 1
105
+ };
106
+ },
107
+ takeAgentAsk: (context) => {
108
+ return {
109
+ ...context,
110
+ agentAsk: null
111
+ };
112
+ },
91
113
  rememberWebsiteLocation: (context, event) => {
92
114
  const previous = context.websiteLocation;
93
115
  const { locale, path, search, hash } = event.location;
@@ -11,6 +11,14 @@ const PHRASES = {
11
11
  verb: "Writing SEO for",
12
12
  verbFor: { file: "Describing" },
13
13
  locale: "in"
14
+ },
15
+ linkChecker: {
16
+ verb: "Checking links in",
17
+ locale: "in"
18
+ },
19
+ proofreader: {
20
+ verb: "Proofreading",
21
+ locale: "in"
14
22
  }
15
23
  };
16
24
  /** The most locales a burst names; past it, the first are named and the rest counted. */
@@ -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;
@@ -1,5 +1,5 @@
1
1
  import { LOCALE_HEADER } from "@camox/api-contract/drafts";
2
- import { localePrefix, parseLocalePath } from "@camox/api-contract/locales";
2
+ import { localizedPath, parseLocalePath } from "@camox/api-contract/locales";
3
3
 
4
4
  //#region src/lib/locale-path.ts
5
5
  /**
@@ -22,12 +22,6 @@ const UNNAMED_DEFAULT_LOCALE = {
22
22
  function localeHeaders(locale) {
23
23
  return locale && !locale.isDefault ? { [LOCALE_HEADER]: locale.tag } : {};
24
24
  }
25
- /** A locale-relative path under its locale's prefix, keeping its search and hash: `/` is `/de`. */
26
- function localizedPath(path, locale) {
27
- const prefix = localePrefix(locale);
28
- if (!prefix) return path;
29
- return path === "/" || /^\/[?#]/.test(path) ? `${prefix}${path.slice(1)}` : `${prefix}${path}`;
30
- }
31
25
 
32
26
  //#endregion
33
27
  export { UNNAMED_DEFAULT_LOCALE, localeHeaders, localizedPath, parseLocalePath };
@@ -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,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 }) => [
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.62.0";
2
+ var version = "0.65.1";
3
3
 
4
4
  //#endregion
5
5
  export { version };