@bud.sh/app 0.3.0 → 0.4.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 (77) hide show
  1. package/dist/client/assets/bud/design.js +1 -1
  2. package/dist/client/assets/canvas-C8hyPWoT.js +1 -0
  3. package/dist/client/assets/difference-worker-BfF4_N3e.js +8 -0
  4. package/dist/client/assets/files-D_Tm7gCK.js +1 -0
  5. package/dist/client/assets/i18n-BtfXum8P.js +1 -0
  6. package/dist/client/assets/index-Dpxjiok1.js +2 -0
  7. package/dist/client/assets/pages-B-_CKvDS.js +1 -0
  8. package/dist/client/assets/project-branches-DzXdaT9L.js +1 -0
  9. package/dist/client/assets/project-comments-CQkB2jbE.js +1 -0
  10. package/dist/client/assets/project-thread-links-BN7zL0AX.js +1 -0
  11. package/dist/client/assets/{project-threads-DezXiVK6.js → project-threads-CHGG-6fo.js} +104 -104
  12. package/dist/client/assets/{project-work-CB1JHqGW.js → project-work-L5fYMSOu.js} +1 -1
  13. package/dist/client/assets/task-design-DYqcqq1b.js +23 -0
  14. package/dist/client/assets/tasks-A2FKo3_h.js +1 -0
  15. package/dist/client/assets/tasks-request-DGigNdyK.js +1 -0
  16. package/dist/client/assets/workspace-CvWmU2RK.js +2 -0
  17. package/dist/client/index.html +2 -1
  18. package/dist/hosted/assets/{canvas-C69-96GR.js → canvas-MWtOwaWm.js} +69 -41
  19. package/dist/hosted/assets/{comment-data-CiDuvXi2.js → comment-data-DsgJWq4n.js} +7195 -1067
  20. package/dist/hosted/assets/{files-DQL5hzs3.js → files-Wvo11G2A.js} +92 -89
  21. package/dist/hosted/assets/{pages-DFs7jB6M.js → pages-BAmRFVtU.js} +31 -22
  22. package/dist/hosted/assets/{project-branches-CffV4IS1.js → project-branches-BNloVosX.js} +5 -5
  23. package/dist/hosted/assets/{project-thread-links-B1Y2bDUx.js → project-thread-links-BmY8HQXt.js} +12 -12
  24. package/dist/hosted/assets/{screen-BroBb1Z_.js → screen-QmqmI2Aq.js} +18 -18
  25. package/dist/hosted/assets/{tasks-mzmNhe2d.js → tasks-C8iWPrWg.js} +29 -29
  26. package/dist/hosted/assets/{tasks-request-Dcrpd2_8.js → tasks-request-C89fN1dK.js} +7 -7
  27. package/dist/hosted/hosted.js +48 -29
  28. package/dist/node/design.js +6235 -139
  29. package/dist/node/index.js +4 -2
  30. package/dist/node-host/assets/{canvas-BZhzizzt.js → canvas-D_F8SIlm.js} +70 -42
  31. package/dist/node-host/assets/{comment-data-nUlI0szN.js → comment-data-q_8INiZz.js} +7221 -1093
  32. package/dist/node-host/assets/{files-CTzcWGjb.js → files-xazOrNCX.js} +92 -89
  33. package/dist/node-host/assets/{pages-0aWH7z1j.js → pages-D6VkmdfQ.js} +31 -22
  34. package/dist/node-host/assets/{project-branches-1zs_0J_y.js → project-branches-C26hwmo3.js} +5 -5
  35. package/dist/node-host/assets/{project-thread-links-LpUAGcDr.js → project-thread-links-BOzjws5W.js} +12 -12
  36. package/dist/node-host/assets/{screen-DQoTOWYR.js → screen-Dog-YByu.js} +18 -18
  37. package/dist/node-host/assets/{tasks-C0WqnM7Q.js → tasks-A1_bpb3_.js} +30 -30
  38. package/dist/node-host/assets/{tasks-request-Bwky5ORf.js → tasks-request-Bie1PpQP.js} +7 -7
  39. package/dist/node-host/node-host.js +48 -29
  40. package/dist/server/assets/{branch-previews-BryeA_kA.js → branch-previews-ClkoJ0lM.js} +4 -2
  41. package/dist/server/assets/{canvas-D6tIkONI.js → canvas-Dn4RYoU2.js} +70 -42
  42. package/dist/server/assets/{comment-data-BuArfgcc.js → comment-data-B0OLHBGp.js} +7219 -1091
  43. package/dist/server/assets/{files-DkrEB48U.js → files-C5Vilfe4.js} +92 -89
  44. package/dist/server/assets/{listener-oKd5ETMj.js → listener-o1STuLju.js} +43 -29
  45. package/dist/server/assets/{pages-iEdmK_o2.js → pages-BNa0MHEc.js} +31 -22
  46. package/dist/server/assets/{project-branches-B7rtrvMl.js → project-branches-GtQYyOH5.js} +5 -5
  47. package/dist/server/assets/{project-thread-links-ue_ad9IX.js → project-thread-links-Nfz9NIny.js} +12 -12
  48. package/dist/server/assets/{project-work-6aOyja4v.js → project-work-Cy_BpHwR.js} +2 -2
  49. package/dist/server/assets/{screen-DMUB9a-K.js → screen-BtcJmZwj.js} +18 -18
  50. package/dist/server/assets/{server-DN5uwE4k.js → server-CYvuOSBA.js} +1 -1
  51. package/dist/server/assets/{services-Ber8opFE.js → services-DK4fqrlF.js} +2 -2
  52. package/dist/server/assets/{tasks-C2oa2wSC.js → tasks-B82tHwyQ.js} +31 -31
  53. package/dist/server/assets/{tasks-request-Bk5EoY_p.js → tasks-request-BtJBlkxV.js} +7 -6
  54. package/dist/server/capture.js +1 -1
  55. package/dist/server/migration.js +3 -3
  56. package/dist/server/runtime.js +4 -4
  57. package/dist/server/server.js +3 -3
  58. package/dist/types/assets/{config-Cuauq8xY.d.ts → config-DIC2cf_S.d.ts} +3 -0
  59. package/dist/types/assets/{contract-BsCQbbxD.d.ts → contract-CAT-XFNq.d.ts} +3 -1
  60. package/dist/types/companion.d.ts +1 -1
  61. package/dist/types/hosted.d.ts +2 -2
  62. package/dist/types/index.d.ts +1 -1
  63. package/dist/types/node-host.d.ts +2 -2
  64. package/dist/types/screen.d.ts +1 -1
  65. package/package.json +1 -1
  66. package/dist/client/assets/canvas-DxaIItAs.js +0 -1
  67. package/dist/client/assets/difference-worker-BpqPBs5g.js +0 -8
  68. package/dist/client/assets/files-C-7xoSt4.js +0 -1
  69. package/dist/client/assets/index-GPPa1XPO.js +0 -2
  70. package/dist/client/assets/pages-BVrTX_Eq.js +0 -1
  71. package/dist/client/assets/project-branches-DpxQx-T4.js +0 -1
  72. package/dist/client/assets/project-comments-D897aU5O.js +0 -1
  73. package/dist/client/assets/project-thread-links-Bp_TseD0.js +0 -1
  74. package/dist/client/assets/task-design-bktfn_Ok.js +0 -23
  75. package/dist/client/assets/tasks-Cqfj4fQN.js +0 -1
  76. package/dist/client/assets/tasks-request-mWMk9pBo.js +0 -1
  77. package/dist/client/assets/workspace-CznMN7F6.js +0 -2
@@ -1,7 +1,7 @@
1
- import { Kt as pageRoutes, Qt as Spinner, Sr as MAX_CANVAS_Y, Zt as Loader, _t as PagesWorkspace, dn as Text, in as IconButton, l as projectStore, ln as readPreference, rn as Select, un as writePreference } from "./comment-data-nUlI0szN.js";
2
- import { n as createProjectComments, t as defineScreen } from "./screen-DQoTOWYR.js";
1
+ import { Er as MAX_CANVAS_Y, Kt as pageRoutes, Qt as Spinner, Zt as Loader, _t as PagesWorkspace, hn as Text, in as IconButton, l as projectStore, ln as readPreference, pn as t, rn as Select, un as writePreference } from "./comment-data-q_8INiZz.js";
2
+ import { n as createProjectComments, t as defineScreen } from "./screen-Dog-YByu.js";
3
3
  import { n as workspaceTab, t as followWorkspaceTab } from "../node-host.js";
4
- import { n as createProjectPreviews, r as locatePreview, t as createProjectBranches } from "./project-branches-1zs_0J_y.js";
4
+ import { n as createProjectPreviews, r as locatePreview, t as createProjectBranches } from "./project-branches-C26hwmo3.js";
5
5
  import { array, number, object, optional, parseSafe, string } from "remix/data-schema";
6
6
  import { on, ref } from "remix/component";
7
7
  import { Fragment, jsx, jsxs } from "remix/component/jsx-runtime";
@@ -43,7 +43,10 @@ function PagePicture(handle) {
43
43
  return /* @__PURE__ */ jsx("div", {
44
44
  class: inline ? "relative w-full" : "absolute inset-0 overflow-auto bg-board",
45
45
  role: "img",
46
- "aria-label": `${route.title} picture at ${size.width} pixels`,
46
+ "aria-label": t("app.client.pagePicture.pictureAt", {
47
+ title: route.title,
48
+ width: size.width
49
+ }),
47
50
  children: current ? /* @__PURE__ */ jsx("div", {
48
51
  class: `relative mx-auto ${picking ? "cursor-crosshair" : ""}`,
49
52
  style: { width: `min(100%, ${current.capture.width}px)` },
@@ -79,7 +82,7 @@ function PagePicture(handle) {
79
82
  variant: "ui",
80
83
  tone: "muted",
81
84
  class: "max-w-md text-center",
82
- children: "This page has no picture at this viewport yet. It appears once its branch is captured."
85
+ children: t("app.client.pagePicture.noPictureYet")
83
86
  }) : /* @__PURE__ */ jsx(Spinner, {
84
87
  size: 18,
85
88
  class: "text-muted"
@@ -191,9 +194,12 @@ function ProjectPages(handle) {
191
194
  }] : []);
192
195
  return pictureMode ? pictured : [{
193
196
  name: workingTree,
194
- caption: "live"
197
+ caption: t("app.client.projectPages.live")
195
198
  }, ...pictured];
196
199
  }
200
+ function branchLabel(name) {
201
+ return name === workingTree ? t("app.client.projectPages.workingTree") : name;
202
+ }
197
203
  let comparing = null;
198
204
  let pickedFrom;
199
205
  let viewport = project.previews.viewports[0];
@@ -328,26 +334,26 @@ function ProjectPages(handle) {
328
334
  if (!pictureMode && options.length < 2 && !project.routes.some((route) => route.flow)) return void 0;
329
335
  let compared = comparing;
330
336
  return {
331
- source: compared ? `Comparing ${compared.join(", ")}` : `Pictures of ${activeTab().branch ?? home}`,
337
+ source: compared ? t("app.client.projectPages.comparing", { branches: compared.join(", ") }) : t("app.client.projectPages.picturesOf", { branch: branchLabel(activeTab().branch ?? home) }),
332
338
  shows: (tab) => pictureMode || Boolean(tab.branch) || Boolean(compared) || Boolean(pageRoute(project.routes, tab.path)?.flow),
333
339
  actions: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(IconButton, {
334
340
  tooltip: true,
335
341
  icon: "compare",
336
- label: compared ? "Stop comparing" : "Compare branches side by side",
342
+ label: compared ? t("app.client.projectPages.stopComparing") : t("app.client.projectPages.compareBranches"),
337
343
  active: Boolean(compared),
338
344
  disabled: !compared && options.length < 2,
339
345
  mix: on("click", toggleComparing)
340
346
  }), compared ? /* @__PURE__ */ jsx("div", {
341
347
  role: "group",
342
- "aria-label": "Branches compared",
348
+ "aria-label": t("app.client.projectPages.branchesCompared"),
343
349
  class: "flex min-w-0 items-center gap-1.5",
344
350
  children: compared.map((name, index) => /* @__PURE__ */ jsx(Select, {
345
- label: `Compared branch ${index + 1}`,
351
+ label: t("app.client.projectPages.comparedBranch", { number: index + 1 }),
346
352
  value: name,
347
353
  class: "max-w-52",
348
354
  options: options.map((option) => ({
349
355
  value: option.name,
350
- label: option.name,
356
+ label: branchLabel(option.name),
351
357
  description: option.caption
352
358
  })),
353
359
  onChange: (value) => {
@@ -363,7 +369,10 @@ function ProjectPages(handle) {
363
369
  children: viewports.length > 1 ? viewports.map((option) => /* @__PURE__ */ jsx(IconButton, {
364
370
  tooltip: true,
365
371
  icon: option.mobile ? "mobile" : "desktop",
366
- label: `Show ${option.name} (${option.width})`,
372
+ label: t("app.client.projectPages.showViewport", {
373
+ name: option.name,
374
+ width: option.width
375
+ }),
367
376
  active: option.id === viewport.id,
368
377
  mix: on("click", () => {
369
378
  viewport = option;
@@ -381,7 +390,7 @@ function ProjectPages(handle) {
381
390
  variant: "ui",
382
391
  tone: "muted",
383
392
  class: "max-w-md text-center",
384
- children: "Only the pages in your routes have pictures."
393
+ children: t("app.client.projectPages.routePicturesOnly")
385
394
  })
386
395
  });
387
396
  if (compared) return /* @__PURE__ */ jsx("div", {
@@ -389,14 +398,14 @@ function ProjectPages(handle) {
389
398
  children: /* @__PURE__ */ jsx("div", {
390
399
  class: "flex w-full items-start justify-center gap-4 p-4",
391
400
  children: compared.map((branch) => /* @__PURE__ */ jsxs("section", {
392
- "aria-label": `${branch} picture`,
401
+ "aria-label": t("app.client.projectPages.branchPicture", { branch: branchLabel(branch) }),
393
402
  class: "flex min-w-0 flex-1 flex-col gap-2",
394
403
  style: { maxWidth: `${viewport.width}px` },
395
404
  children: [/* @__PURE__ */ jsx(Text, {
396
405
  variant: "ui-caption",
397
406
  tone: "muted",
398
407
  class: "truncate",
399
- children: branch
408
+ children: branchLabel(branch)
400
409
  }), /* @__PURE__ */ jsx(PagePicture, {
401
410
  previews: previewsOf(branch),
402
411
  route,
@@ -450,9 +459,9 @@ function ProjectPages(handle) {
450
459
  handle.update();
451
460
  }
452
461
  function unavailable(path) {
453
- if (!comments.loaded) return "Loading comments…";
454
- if (!comments.snapshot.capabilities.create) return "Comments here are read-only";
455
- if (!pageRoute(project.routes, path)) return "Only pages in your routes take comments. Add this page to bud.config.";
462
+ if (!comments.loaded) return t("app.client.projectPages.loadingComments");
463
+ if (!comments.snapshot.capabilities.create) return t("app.client.projectPages.commentsReadOnly");
464
+ if (!pageRoute(project.routes, path)) return t("app.client.projectPages.routeCommentsOnly");
456
465
  return null;
457
466
  }
458
467
  tabId = workspaceTab();
@@ -480,7 +489,7 @@ function ProjectPages(handle) {
480
489
  if (!branches.loaded && !branches.error) return /* @__PURE__ */ jsx("div", {
481
490
  class: "h-full min-h-0",
482
491
  mix: mount,
483
- children: /* @__PURE__ */ jsx(Loader, { label: "Loading branches…" })
492
+ children: /* @__PURE__ */ jsx(Loader, { label: t("app.client.projectPages.loadingBranches") })
484
493
  });
485
494
  let current = activeTab();
486
495
  if (!pictureMode && typeof window !== "undefined" && new URL(origin).origin === window.location.origin) return /* @__PURE__ */ jsx("div", {
@@ -491,7 +500,7 @@ function ProjectPages(handle) {
491
500
  variant: "ui",
492
501
  tone: "muted",
493
502
  class: "max-w-md text-center",
494
- children: "Your site is served from Bud's own address, so Pages can't show it. Serve it on its own port to browse it here."
503
+ children: t("app.client.projectPages.siteOnBudOrigin")
495
504
  })
496
505
  });
497
506
  return /* @__PURE__ */ jsx("div", {
@@ -528,7 +537,7 @@ function ProjectPages(handle) {
528
537
  unavailable: unavailable(current.path),
529
538
  draft: draft ? {
530
539
  path: draft.pick.path,
531
- label: draft.pick.element?.label ?? "A spot on the page",
540
+ label: draft.pick.element?.label ?? t("app.client.projectPages.spotOnPage"),
532
541
  source: draft.pick.element?.source ?? null
533
542
  } : null,
534
543
  busy: comments.busy,
@@ -547,7 +556,7 @@ function ProjectPages(handle) {
547
556
  design: project.designs.pages,
548
557
  sections: pictureMode || captured().length > 1 ? captured().map((branch) => ({
549
558
  id: branch.name,
550
- label: branch.name,
559
+ label: branchLabel(branch.name),
551
560
  caption: branch.caption
552
561
  })) : void 0,
553
562
  section: current.branch ?? home,
@@ -1,4 +1,4 @@
1
- import { Cr as parseElementAnchor, S as writeRows, b as plainRow, c as previewKey, l as projectStore, ni as parsePreviewBranch, oi as parsePreviewPins, ti as parsePreview, v as projectLive, y as routes } from "./comment-data-nUlI0szN.js";
1
+ import { Dr as parseElementAnchor, S as writeRows, ai as parsePreview, b as plainRow, c as previewKey, l as projectStore, oi as parsePreviewBranch, pn as t, ui as parsePreviewPins, v as projectLive, y as routes } from "./comment-data-q_8INiZz.js";
2
2
  import { array, boolean, enum_, nullable, number, object, parseSafe, string } from "remix/data-schema";
3
3
  //#region app/client/project-previews.ts
4
4
  /**
@@ -163,12 +163,12 @@ async function request(url, token, signal, method = "GET") {
163
163
  });
164
164
  if (!response.ok) {
165
165
  let body = await response.json().catch(() => null);
166
- throw new Error(typeof body?.error === "string" ? body.error : `Preview request failed (${response.status}).`);
166
+ throw new Error(typeof body?.error === "string" ? body.error : t("app.client.projectPreviews.requestFailed", { status: response.status }));
167
167
  }
168
168
  return response;
169
169
  }
170
170
  function message(cause) {
171
- return cause instanceof Error ? cause.message : "Bud could not load this preview.";
171
+ return cause instanceof Error ? cause.message : t("app.client.projectPreviews.loadFailed");
172
172
  }
173
173
  /**
174
174
  * Shows server-captured route screenshots for one canvas from the project's local store,
@@ -502,7 +502,7 @@ var BranchListSchema = object({
502
502
  */
503
503
  function parseBranchList(value) {
504
504
  let result = parseSafe(BranchListSchema, value);
505
- if (!result.success) throw new TypeError("The companion returned an invalid branch list.");
505
+ if (!result.success) throw new TypeError(t("app.client.projectBranches.invalidBranchList"));
506
506
  return result.value;
507
507
  }
508
508
  /**
@@ -533,7 +533,7 @@ function createProjectBranches(options) {
533
533
  signal
534
534
  });
535
535
  let body = await response.json().catch(() => null);
536
- if (!response.ok) throw new Error(typeof body?.error === "string" ? body.error : "Bud could not load branches.");
536
+ if (!response.ok) throw new Error(typeof body?.error === "string" ? body.error : t("app.client.projectBranches.loadError"));
537
537
  return parseBranchList(body);
538
538
  }
539
539
  async function load() {
@@ -1,5 +1,5 @@
1
- import { Br as valueByRole, C as ThreadTaskLinks, Cn as parseThreadLinkInput, Dt as taskHref, Mr as parseWorkItemRef, _ as requestComments, in as IconButton, vn as parseThread, w as ThreadTaskPicker, wr as parseWorkWorkspace, y as routes } from "./comment-data-nUlI0szN.js";
2
- import { n as requestTasks } from "./tasks-request-Bwky5ORf.js";
1
+ import { C as ThreadTaskLinks, Dn as parseThreadLinkInput, Dt as taskHref, Ir as parseWorkItemRef, Or as parseWorkWorkspace, Sn as parseThread, Wr as valueByRole, _ as requestComments, in as IconButton, pn as t, w as ThreadTaskPicker, y as routes } from "./comment-data-q_8INiZz.js";
2
+ import { n as requestTasks } from "./tasks-request-Bie1PpQP.js";
3
3
  import { on, ref } from "remix/component";
4
4
  import { Fragment, jsx, jsxs } from "remix/component/jsx-runtime";
5
5
  //#region app/client/project-task-picker.tsx
@@ -36,7 +36,7 @@ function ProjectTaskPicker(handle) {
36
36
  });
37
37
  let { schema, snapshot } = await requestTasks(routes.tasksApi.workspace.href() + "?" + search, token, signal, parseWorkWorkspace);
38
38
  signal.throwIfAborted();
39
- if (snapshot.connection.state !== "ready") throw new Error(snapshot.connection.message ?? "The task adapter is unavailable.");
39
+ if (snapshot.connection.state !== "ready") throw new Error(snapshot.connection.message ?? t("app.client.projectTaskPicker.adapterUnavailable"));
40
40
  items = snapshot.items.map((item) => {
41
41
  let title = valueByRole(item, schema, "title");
42
42
  return {
@@ -51,7 +51,7 @@ function ProjectTaskPicker(handle) {
51
51
  }
52
52
  truncated = snapshot.truncated;
53
53
  } catch (cause) {
54
- if (!signal.aborted) error = cause instanceof Error ? cause.message : "Could not load tasks.";
54
+ if (!signal.aborted) error = cause instanceof Error ? cause.message : t("app.client.projectTaskPicker.loadFailed");
55
55
  } finally {
56
56
  if (request === controller) {
57
57
  loading = false;
@@ -106,9 +106,9 @@ function ProjectTaskPicker(handle) {
106
106
  * @returns Bounded mutations, each applied against the previous acknowledged revision.
107
107
  */
108
108
  function planThreadLinks(thread, selected) {
109
- if (selected.length > 100 || new Set(selected).size !== selected.length) throw new Error(`Select up to 100 different tasks.`);
109
+ if (selected.length > 100 || new Set(selected).size !== selected.length) throw new Error(t("app.client.threadLinks.selectUpToTasks", { count: 100 }));
110
110
  for (let id of selected) parseWorkItemRef({ id });
111
- if (!thread.anchor && !selected.length) throw new Error("A task discussion needs at least one linked task.");
111
+ if (!thread.anchor && !selected.length) throw new Error(t("app.client.threadLinks.taskDiscussionNeedsLinkedTask"));
112
112
  let current = new Set(thread.itemIds);
113
113
  let desired = new Set(selected);
114
114
  let changes = [];
@@ -129,7 +129,7 @@ function planThreadLinks(thread, selected) {
129
129
  });
130
130
  for (let id of added) current.add(id);
131
131
  }
132
- if (!removed.length && !added.length) throw new Error("Could not prepare task links.");
132
+ if (!removed.length && !added.length) throw new Error(t("app.client.threadLinks.couldNotPrepareTaskLinks"));
133
133
  }
134
134
  return changes;
135
135
  }
@@ -146,7 +146,7 @@ async function saveThreadLinks(thread, selected, mutate, signal) {
146
146
  let current = thread;
147
147
  for (let change of changes) {
148
148
  signal.throwIfAborted();
149
- if (!current.revision) throw new Error("Review the latest discussion before changing links.");
149
+ if (!current.revision) throw new Error(t("app.client.threadLinks.reviewLatestDiscussionBeforeChangingLinks"));
150
150
  let input = parseThreadLinkInput({
151
151
  itemIds: change.itemIds,
152
152
  expectedRevision: current.revision
@@ -156,7 +156,7 @@ async function saveThreadLinks(thread, selected, mutate, signal) {
156
156
  let expected = new Set(current.itemIds);
157
157
  for (let id of change.itemIds) if (change.action === "link") expected.add(id);
158
158
  else expected.delete(id);
159
- if (next.id !== current.id || !next.revision || next.itemIds.length !== expected.size || next.itemIds.some((id) => !expected.has(id)) || JSON.stringify(next.anchor) !== JSON.stringify(current.anchor)) throw new Error("The task link change was not confirmed. Review the latest links before saving again.");
159
+ if (next.id !== current.id || !next.revision || next.itemIds.length !== expected.size || next.itemIds.some((id) => !expected.has(id)) || JSON.stringify(next.anchor) !== JSON.stringify(current.anchor)) throw new Error(t("app.client.threadLinks.taskLinkChangeNotConfirmed"));
160
160
  current = next;
161
161
  }
162
162
  return current;
@@ -179,7 +179,7 @@ function ProjectThreadLinks(handle) {
179
179
  async function review() {
180
180
  if (!editing) return;
181
181
  let next = await requestComments(routes.comments.get.href({ id: editing.id }), handle.props.project.token, handle.signal, parseThread);
182
- if (next.id !== editing.id) throw new Error("The service returned another discussion.");
182
+ if (next.id !== editing.id) throw new Error(t("app.client.projectThreadLinks.anotherDiscussion"));
183
183
  editing = next;
184
184
  needsReview = false;
185
185
  reviewed = true;
@@ -202,7 +202,7 @@ function ProjectThreadLinks(handle) {
202
202
  }
203
203
  return () => /* @__PURE__ */ jsxs(Fragment, { children: [handle.props.part === "action" ? handle.props.canLink ? /* @__PURE__ */ jsx(IconButton, {
204
204
  icon: "tasks",
205
- label: "Link tasks",
205
+ label: t("app.client.projectThreadLinks.linkTasks"),
206
206
  size: "small",
207
207
  tooltip: true,
208
208
  disabled: handle.props.busy,
@@ -220,7 +220,7 @@ function ProjectThreadLinks(handle) {
220
220
  onClose: close,
221
221
  onReview: needsReview ? review : void 0,
222
222
  onSave: async (selected) => {
223
- if (!editing || needsReview) throw new Error("Review the latest links before saving.");
223
+ if (!editing || needsReview) throw new Error(t("app.client.projectThreadLinks.reviewLinksBeforeSaving"));
224
224
  try {
225
225
  let initial = editing;
226
226
  editing = await saveThreadLinks(initial, selected, (action, input) => {
@@ -1,4 +1,4 @@
1
- import { Tn as parseThreadSnapshot, _ as requestComments, a as saveThreads, b as plainRow, bn as parseThreadFilter, cn as toast, g as CommentsRequestError, i as saveThread, l as projectStore, n as unavailableComments, o as threadMatches, vn as parseThread, x as writeOptimistically, xn as parseThreadId, y as routes } from "./comment-data-nUlI0szN.js";
1
+ import { Sn as parseThread, Tn as parseThreadId, _ as requestComments, a as saveThreads, b as plainRow, cn as toast, g as CommentsRequestError, i as saveThread, kn as parseThreadSnapshot, l as projectStore, n as unavailableComments, o as threadMatches, pn as t, wn as parseThreadFilter, x as writeOptimistically, y as routes } from "./comment-data-q_8INiZz.js";
2
2
  //#region ../work/src/lib/predict.ts
3
3
  /**
4
4
  * How a new task shows until its adapter confirms it: the requested values, and the creation
@@ -161,7 +161,7 @@ async function loadItemComments(token, itemId, signal, conversationId) {
161
161
  signal.throwIfAborted();
162
162
  return {
163
163
  snapshot: null,
164
- error: cause instanceof Error ? cause.message : "Could not load the discussion."
164
+ error: cause instanceof Error ? cause.message : t("app.client.projectComments.couldNotLoadDiscussion")
165
165
  };
166
166
  }
167
167
  }
@@ -220,7 +220,7 @@ async function loadComments(project, signal, fetcher = fetch) {
220
220
  signal.throwIfAborted();
221
221
  return {
222
222
  snapshot: null,
223
- error: cause instanceof Error ? cause.message : "Could not load comments."
223
+ error: cause instanceof Error ? cause.message : t("app.client.projectComments.couldNotLoadComments")
224
224
  };
225
225
  }
226
226
  }
@@ -307,7 +307,7 @@ function createProjectComments(options) {
307
307
  error = "";
308
308
  }
309
309
  } catch (cause) {
310
- if (!signal.aborted && turn === generation) error = cause instanceof Error ? cause.message : "Could not load comments.";
310
+ if (!signal.aborted && turn === generation) error = cause instanceof Error ? cause.message : t("app.client.projectComments.couldNotLoadComments");
311
311
  } finally {
312
312
  if (turn === generation) {
313
313
  loading = false;
@@ -327,16 +327,16 @@ function createProjectComments(options) {
327
327
  changed();
328
328
  try {
329
329
  let thread = await request(routes.comments.get.href({ id }), signal, parseThread);
330
- if (thread.id !== id) throw new CommentsRequestError("The comments service returned another discussion.");
330
+ if (thread.id !== id) throw new CommentsRequestError(t("app.client.projectComments.serviceReturnedAnotherDiscussion"));
331
331
  if (!signal.aborted && turn === generation) {
332
332
  await merge(thread);
333
- if (!includes(thread)) throw new CommentsRequestError("This discussion is no longer part of this view.");
333
+ if (!includes(thread)) throw new CommentsRequestError(t("app.client.projectComments.discussionNoLongerInView"));
334
334
  if (!detailFailures.size) error = "";
335
335
  }
336
336
  } catch (cause) {
337
337
  if (!signal.aborted && turn === generation) {
338
338
  detailFailures.add(id);
339
- error = cause instanceof Error ? cause.message : "Could not load the discussion.";
339
+ error = cause instanceof Error ? cause.message : t("app.client.projectComments.couldNotLoadDiscussion");
340
340
  }
341
341
  } finally {
342
342
  if (details.get(id) === controller) {
@@ -346,10 +346,10 @@ function createProjectComments(options) {
346
346
  }
347
347
  }
348
348
  function requireCapability(capability) {
349
- if (busy) throw new Error("Wait for the current comment operation to finish.");
350
- if (!loaded) throw new Error("Load comments before saving.");
351
- if (snapshot.connection.state !== "ready") throw new Error(snapshot.connection.message ?? "The comment adapter is unavailable.");
352
- if (!snapshot.capabilities[capability]) throw new Error("The comment adapter does not support this operation.");
349
+ if (busy) throw new Error(t("app.client.projectComments.waitForCurrentOperation"));
350
+ if (!loaded) throw new Error(t("app.client.projectComments.loadCommentsBeforeSaving"));
351
+ if (snapshot.connection.state !== "ready") throw new Error(snapshot.connection.message ?? t("app.client.projectComments.adapterUnavailable"));
352
+ if (!snapshot.capabilities[capability]) throw new Error(t("app.client.projectComments.adapterDoesNotSupportOperation"));
353
353
  }
354
354
  async function operate(url, body, parse, apply, success, threadId, predict) {
355
355
  cancelReads();
@@ -371,13 +371,13 @@ function createProjectComments(options) {
371
371
  return snapshot;
372
372
  } catch (cause) {
373
373
  if (!options.signal.aborted) {
374
- error = cause instanceof Error ? cause.message : "The comment write was not confirmed.";
375
- if (predict) toast.error("Bud undid a comment change", { description: error });
374
+ error = cause instanceof Error ? cause.message : t("app.client.projectComments.commentWriteNotConfirmed");
375
+ if (predict) toast.error(t("app.client.projectComments.budUndidCommentChange"), { description: error });
376
376
  if (cause instanceof CommentsRequestError && cause.code === "conflict") try {
377
377
  await adopt(await request(listHref(filter), options.signal, parseThreadSnapshot));
378
378
  if (threadId) {
379
379
  let detail = await request(routes.comments.get.href({ id: threadId }), options.signal, parseThread);
380
- if (detail.id !== threadId) throw new CommentsRequestError("The comments service returned another discussion.");
380
+ if (detail.id !== threadId) throw new CommentsRequestError(t("app.client.projectComments.serviceReturnedAnotherDiscussion"));
381
381
  await merge(detail);
382
382
  }
383
383
  } catch {}
@@ -422,7 +422,7 @@ function createProjectComments(options) {
422
422
  let pending = `pending-${crypto.randomUUID()}`;
423
423
  if (change.id) {
424
424
  let id = parseThreadId(change.id), thread = snapshot.threads.find((item) => item.id === id);
425
- if (!thread?.revision) throw new Error("Reload this discussion before saving.");
425
+ if (!thread?.revision) throw new Error(t("app.client.projectComments.reloadDiscussionBeforeSaving"));
426
426
  let next;
427
427
  if (change.status !== void 0) {
428
428
  url = routes.comments.status.href({ id });
@@ -467,16 +467,16 @@ function createProjectComments(options) {
467
467
  let saved;
468
468
  return {
469
469
  ...await operate(url, body, parseThread, (thread) => {
470
- if (change.id && thread.id !== change.id) throw new CommentsRequestError("The comment write could not be confirmed. Refresh before trying again.", 409, "uncertain");
470
+ if (change.id && thread.id !== change.id) throw new CommentsRequestError(t("app.client.projectComments.refreshBeforeTryingAgain"), 409, "uncertain");
471
471
  saved = thread;
472
472
  return merge(thread);
473
- }, "Comment saved", change.id, predict),
473
+ }, t("app.client.projectComments.commentSaved"), change.id, predict),
474
474
  thread: saved
475
475
  };
476
476
  },
477
477
  async sync() {
478
478
  requireCapability("sync");
479
- return operate(routes.comments.sync.href(), { direction: "both" }, parseThreadSnapshot, adopt, "Comments synced");
479
+ return operate(routes.comments.sync.href(), { direction: "both" }, parseThreadSnapshot, adopt, t("app.client.projectComments.commentsSynced"));
480
480
  }
481
481
  };
482
482
  }
@@ -1,8 +1,8 @@
1
- import { $t as Button, Ar as parseWorkDetail, B as TaskWorkspace, C as ThreadTaskLinks, Dt as taskHref, Et as selectedTask, Jt as CommentThreadDetail, Lr as parseWorkSyncResult, Nr as parseWorkMutationResult, Ot as taskRole, S as writeRows, T as ItemDiscussion, Tr as parseSectionData, Tt as readConversationLocation, Xt as CommentComposer, Yt as CommentThreadItem, b as plainRow, cn as toast, dn as Text, h as storedWorkspace, l as projectStore, nt as CommentThreadHeading, qt as taskRoutes, t as commentsUnavailable, x as writeOptimistically, xt as conversationLabel, y as routes } from "./comment-data-nUlI0szN.js";
2
- import { a as predictWorkAction, i as loadItemComments, n as createProjectComments, o as predictWorkCreate, s as predictWorkUpdate, t as defineScreen } from "./screen-DQoTOWYR.js";
3
- import { n as requestTasks, t as TaskRequestError } from "./tasks-request-Bwky5ORf.js";
1
+ import { $t as Button, B as TaskWorkspace, C as ThreadTaskLinks, Dt as taskHref, Et as selectedTask, Jt as CommentThreadDetail, Lr as parseWorkMutationResult, Ot as taskRole, Pr as parseWorkDetail, S as writeRows, T as ItemDiscussion, Tt as readConversationLocation, Vr as parseWorkSyncResult, Xt as CommentComposer, Yt as CommentThreadItem, b as plainRow, cn as toast, h as storedWorkspace, hn as Text, kr as parseSectionData, l as projectStore, nt as CommentThreadHeading, pn as t, qt as taskRoutes, x as writeOptimistically, xt as conversationLabel, y as routes } from "./comment-data-q_8INiZz.js";
2
+ import { a as predictWorkAction, i as loadItemComments, n as createProjectComments, o as predictWorkCreate, s as predictWorkUpdate, t as defineScreen } from "./screen-Dog-YByu.js";
3
+ import { n as requestTasks, t as TaskRequestError } from "./tasks-request-Bie1PpQP.js";
4
4
  import { r as projectWork } from "../node-host.js";
5
- import { n as ProjectTaskPicker, t as ProjectThreadLinks } from "./project-thread-links-LpUAGcDr.js";
5
+ import { n as ProjectTaskPicker, t as ProjectThreadLinks } from "./project-thread-links-BOzjws5W.js";
6
6
  import { on, ref } from "remix/component";
7
7
  import { Fragment, jsx, jsxs } from "remix/component/jsx-runtime";
8
8
  //#region app/client/task-discussion.tsx
@@ -72,13 +72,13 @@ function TaskDiscussion(handle) {
72
72
  creating = false;
73
73
  handle.update();
74
74
  }),
75
- children: "Reply"
75
+ children: t("app.client.taskDiscussion.reply")
76
76
  }) : null]
77
77
  });
78
78
  }
79
79
  function heading(thread) {
80
80
  return /* @__PURE__ */ jsx(CommentThreadHeading, {
81
- location: thread.anchor ? conversationLabel(thread) : "Discussion",
81
+ location: thread.anchor ? conversationLabel(thread) : t("app.client.taskDiscussion.discussion"),
82
82
  actions: handle.props.renderThreadLinks ? void 0 : /* @__PURE__ */ jsx(ProjectThreadLinks, {
83
83
  part: "action",
84
84
  ...threadLinks(thread)
@@ -112,7 +112,7 @@ function TaskDiscussion(handle) {
112
112
  let count = threads.reduce((sum, thread) => sum + thread.messageCount, 0);
113
113
  return /* @__PURE__ */ jsxs("section", {
114
114
  class: "flex min-w-0 flex-col gap-3",
115
- "aria-label": "Task discussion",
115
+ "aria-label": t("app.client.taskDiscussion.taskDiscussion"),
116
116
  mix: mount,
117
117
  children: [
118
118
  /* @__PURE__ */ jsxs("div", {
@@ -122,7 +122,7 @@ function TaskDiscussion(handle) {
122
122
  as: "h3",
123
123
  variant: "ui",
124
124
  weight: "medium",
125
- children: "Discussion"
125
+ children: t("app.client.taskDiscussion.discussion")
126
126
  }),
127
127
  count ? /* @__PURE__ */ jsx(Text, {
128
128
  variant: "ui-caption",
@@ -138,7 +138,7 @@ function TaskDiscussion(handle) {
138
138
  creating = true;
139
139
  handle.update();
140
140
  }),
141
- children: "New discussion"
141
+ children: t("app.client.taskDiscussion.newDiscussion")
142
142
  }) : null
143
143
  ]
144
144
  }),
@@ -154,18 +154,18 @@ function TaskDiscussion(handle) {
154
154
  mix: on("click", () => {
155
155
  comments.load().then(() => selected ? comments.loadThread(selected, true) : void 0);
156
156
  }),
157
- children: "Reload comments"
157
+ children: t("app.client.taskDiscussion.reloadComments")
158
158
  })]
159
159
  }) : null,
160
160
  !comments.loaded ? /* @__PURE__ */ jsx(Text, {
161
161
  variant: "ui-caption",
162
162
  tone: "muted",
163
163
  role: "status",
164
- children: "Loading discussion…"
164
+ children: t("app.client.taskDiscussion.loadingDiscussion")
165
165
  }) : !comments.ready ? /* @__PURE__ */ jsx(Text, {
166
166
  variant: "ui-caption",
167
167
  tone: "muted",
168
- children: commentsUnavailable
168
+ children: t("app.client.taskDiscussion.commentsUnavailable")
169
169
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [threads.length ? threads.map((thread) => !thread.anchor ? /* @__PURE__ */ jsx(ItemDiscussion, {
170
170
  thread,
171
171
  links: links(thread)
@@ -200,12 +200,12 @@ function TaskDiscussion(handle) {
200
200
  }, thread.id)) : /* @__PURE__ */ jsx(Text, {
201
201
  variant: "ui-caption",
202
202
  tone: "muted",
203
- children: "No discussion yet."
203
+ children: t("app.client.taskDiscussion.noDiscussionYet")
204
204
  }), capabilities.create || capabilities.reply && (explicit || threads.some((thread) => !thread.anchor)) ? /* @__PURE__ */ jsxs("div", {
205
205
  class: "flex min-w-0 flex-col gap-2",
206
206
  children: [capabilities.create && (creating || !explicit && !threads.length) ? /* @__PURE__ */ jsxs("div", {
207
207
  class: "flex items-start justify-between gap-2",
208
- "aria-label": "New discussion tasks",
208
+ "aria-label": t("app.client.taskDiscussion.newDiscussionTasks"),
209
209
  children: [/* @__PURE__ */ jsx(ThreadTaskLinks, {
210
210
  itemIds: draftItemIds,
211
211
  items: handle.props.items ?? [],
@@ -223,19 +223,19 @@ function TaskDiscussion(handle) {
223
223
  creating = false;
224
224
  handle.update();
225
225
  }),
226
- children: "Cancel"
226
+ children: t("app.client.taskDiscussion.cancel")
227
227
  }) : null]
228
- }) : threads.length > 1 ? /* @__PURE__ */ jsxs(Text, {
228
+ }) : threads.length > 1 ? /* @__PURE__ */ jsx(Text, {
229
229
  variant: "ui-caption",
230
230
  tone: "muted",
231
- children: ["Replying to ", reply?.messages[0]?.author.name ?? "the discussion"]
231
+ children: t("app.client.taskDiscussion.replyingTo", { name: reply?.messages[0]?.author.name ?? t("app.client.taskDiscussion.theDiscussion") })
232
232
  }) : null, /* @__PURE__ */ jsx(CommentComposer, {
233
- label: "Add task comment",
234
- placeholder: "Add context for your team…",
233
+ label: t("app.client.taskDiscussion.addTaskComment"),
234
+ placeholder: t("app.client.taskDiscussion.addContextPlaceholder"),
235
235
  busy: comments.busy || replyPending,
236
236
  class: "w-full",
237
237
  onSubmit: async (body) => {
238
- if (replyPending) throw new Error("Load the selected discussion before replying.");
238
+ if (replyPending) throw new Error(t("app.client.taskDiscussion.loadBeforeReplying"));
239
239
  if (reply && capabilities.reply) {
240
240
  await write({
241
241
  id: reply.id,
@@ -285,7 +285,7 @@ async function loadTask(token, id, schema, signal, conversationId) {
285
285
  schema
286
286
  ]);
287
287
  let detail = parseWorkDetail(value, resolved);
288
- if (detail.item.id !== id) throw new Error("The task service returned a different task. Refresh and try again.");
288
+ if (detail.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefresh"));
289
289
  return {
290
290
  detail,
291
291
  discussion
@@ -312,7 +312,7 @@ async function loadTasks(project, signal, href, conversationId) {
312
312
  if (!stored || cause instanceof TaskRequestError) throw cause;
313
313
  return open(projectWork(project).refresh(signal).then((value) => value.schema));
314
314
  }).catch((cause) => {
315
- taskError = cause instanceof Error ? cause.message : "Could not load this task.";
315
+ taskError = cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTask");
316
316
  return null;
317
317
  }) : null;
318
318
  try {
@@ -330,7 +330,7 @@ async function loadTasks(project, signal, href, conversationId) {
330
330
  return {
331
331
  store,
332
332
  stored: false,
333
- error: cause instanceof Error ? cause.message : "Could not load tasks."
333
+ error: cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTasks")
334
334
  };
335
335
  }
336
336
  }
@@ -384,7 +384,7 @@ function ProjectTasks(handle) {
384
384
  };
385
385
  }
386
386
  function currentSchema() {
387
- if (!workspace) throw new Error("Refresh tasks before making changes.");
387
+ if (!workspace) throw new Error(t("app.client.projectTasks.refreshBeforeChanges"));
388
388
  return workspace.schema;
389
389
  }
390
390
  async function write(url, body, parse) {
@@ -421,7 +421,7 @@ function ProjectTasks(handle) {
421
421
  ...body,
422
422
  expectedRevision: prior.to
423
423
  } : body, (value) => parseWorkMutationResult(value, currentSchema()));
424
- if (id && result.item.id !== id) throw new Error("The task service returned a different task. Refresh before retrying.");
424
+ if (id && result.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefreshRetry"));
425
425
  await accepted(result.item);
426
426
  settled.resolve(from ? {
427
427
  from,
@@ -433,7 +433,7 @@ function ProjectTasks(handle) {
433
433
  return item;
434
434
  } catch (cause) {
435
435
  settled.resolve(null);
436
- if (!handle.signal.aborted) toast.error("Bud undid a task change", { description: cause instanceof Error ? cause.message : "The write was not confirmed." });
436
+ if (!handle.signal.aborted) toast.error(t("app.client.projectTasks.undidTaskChange"), { description: cause instanceof Error ? cause.message : t("app.client.projectTasks.writeNotConfirmed") });
437
437
  refreshAfterWrite();
438
438
  throw cause;
439
439
  } finally {
@@ -458,7 +458,7 @@ function ProjectTasks(handle) {
458
458
  act: (id, action) => save(routes.tasksApi.mutate.href({ id }), action, predicted(id, (item) => predictWorkAction(currentSchema(), item, action)), id),
459
459
  get: async (id) => {
460
460
  let next = await requestTasks(routes.tasksApi.show.href({ id }), project.token, handle.signal, (value) => parseWorkDetail(value, currentSchema()));
461
- if (next.item.id !== id) throw new Error("The task service returned a different task. Refresh and try again.");
461
+ if (next.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefresh"));
462
462
  return next.item;
463
463
  },
464
464
  section: (id, section, signal = handle.signal) => requestTasks(routes.tasksApi.section.href({
@@ -489,7 +489,7 @@ function ProjectTasks(handle) {
489
489
  try {
490
490
  await work.refresh(AbortSignal.any([handle.signal, controller.signal]));
491
491
  } catch (cause) {
492
- if (!controller.signal.aborted && !handle.signal.aborted) error = cause instanceof Error ? cause.message : "Could not load tasks.";
492
+ if (!controller.signal.aborted && !handle.signal.aborted) error = cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTasks");
493
493
  } finally {
494
494
  if (listRequest === controller) {
495
495
  loading = false;
@@ -525,10 +525,10 @@ function ProjectTasks(handle) {
525
525
  }
526
526
  let next = await requestTasks(routes.tasksApi.show.href({ id }), project.token, signal, (value) => parseWorkDetail(value, currentSchema()));
527
527
  if (controller.signal.aborted || id !== selectedId) return;
528
- if (next.item.id !== id) throw new Error("The task service returned a different task. Refresh and try again.");
528
+ if (next.item.id !== id) throw new Error(t("app.client.projectTasks.differentTaskRefresh"));
529
529
  detail = next;
530
530
  } catch (cause) {
531
- if (!controller.signal.aborted && !handle.signal.aborted && id === selectedId) detailError = cause instanceof Error ? cause.message : "Could not load this task.";
531
+ if (!controller.signal.aborted && !handle.signal.aborted && id === selectedId) detailError = cause instanceof Error ? cause.message : t("app.client.projectTasks.couldNotLoadTask");
532
532
  } finally {
533
533
  if (detailRequest === controller) {
534
534
  detailLoading = false;