@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,4 +1,4 @@
1
- import { Mr as parseWorkItemRef, Pr as parseWorkQuery, y as routes } from "./comment-data-nUlI0szN.js";
1
+ import { Ir as parseWorkItemRef, Rr as parseWorkQuery, pn as t, y as routes } from "./comment-data-q_8INiZz.js";
2
2
  import { createMultiMatcher } from "remix/route-pattern/match";
3
3
  //#region app/client/tasks-request.ts
4
4
  var reads = createMultiMatcher();
@@ -38,7 +38,7 @@ function validEndpoint(url, write) {
38
38
  * @returns A validated DTO; error responses never become an empty successful view.
39
39
  */
40
40
  async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
41
- if (!validEndpoint(url, Boolean(write))) throw new Error("Invalid task endpoint.");
41
+ if (!validEndpoint(url, Boolean(write))) throw new Error(t("app.client.tasksRequest.invalidEndpoint"));
42
42
  signal.throwIfAborted();
43
43
  let response = await fetcher(url, {
44
44
  method: write ? "POST" : "GET",
@@ -53,7 +53,7 @@ async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
53
53
  signal
54
54
  });
55
55
  let reader = response.body?.getReader();
56
- if (!reader) throw new Error("The task service returned an empty response.");
56
+ if (!reader) throw new Error(t("app.client.tasksRequest.emptyResponse"));
57
57
  let chunks = [];
58
58
  let size = 0;
59
59
  try {
@@ -62,7 +62,7 @@ async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
62
62
  let part = await reader.read();
63
63
  if (part.done) break;
64
64
  size += part.value.byteLength;
65
- if (size > 4194304) throw new Error("The task response is too large to display. Narrow the results and try again.");
65
+ if (size > 4194304) throw new Error(t("app.client.tasksRequest.responseTooLarge"));
66
66
  chunks.push(part.value);
67
67
  }
68
68
  } catch (cause) {
@@ -81,15 +81,15 @@ async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
81
81
  try {
82
82
  value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
83
83
  } catch {
84
- throw new Error("The task service returned an invalid response. Refresh and try again.");
84
+ throw new Error(t("app.client.tasksRequest.invalidResponse"));
85
85
  }
86
86
  if (!response.ok) {
87
- let error = value && typeof value === "object" && "error" in value && typeof value.error === "string" ? value.error.slice(0, 1e3) : "Could not load tasks. Try again.";
87
+ let error = value && typeof value === "object" && "error" in value && typeof value.error === "string" ? value.error.slice(0, 1e3) : t("app.client.tasksRequest.loadFailed");
88
88
  let code = value && typeof value === "object" && "code" in value && typeof value.code === "string" && /^[a-z][a-z-]{0,63}$/.test(value.code) ? value.code : "request";
89
89
  let currentRevision;
90
90
  if (value && typeof value === "object" && "currentRevision" in value) {
91
91
  let revision = value.currentRevision;
92
- if (revision !== null && (typeof revision !== "string" || !revision.trim() || revision.length > 256 || revision.includes("\0"))) throw new Error("The task service returned an invalid conflict response.");
92
+ if (revision !== null && (typeof revision !== "string" || !revision.trim() || revision.length > 256 || revision.includes("\0"))) throw new Error(t("app.client.tasksRequest.invalidConflictResponse"));
93
93
  currentRevision = revision;
94
94
  }
95
95
  throw new TaskRequestError(error, code, currentRevision);
@@ -1,5 +1,5 @@
1
- import { $r as PREVIEW_THUMBNAIL_WIDTHS, $t as Button, Ar as parseWorkDetail, Br as valueByRole, Cn as parseThreadLinkInput, Cr as parseElementAnchor, Ct as legacyConversationHref, Dn as parseThreadStatusInput, Dr as parseWorkActivity, Dt as taskHref, En as parseThreadStatus, Er as parseWorkActionInput, Et as selectedTask, Fr as parseWorkSnapshot, G as mergeThreads, Gr as statusCategory, Gt as fileRoutes, Hr as parseMessageAuthor, Ir as parseWorkSync, J as conversationId, Jr as nonempty$1, K as stableJson, Kr as WorkValidationError, Kt as pageRoutes, Lr as parseWorkSyncResult, Mr as parseWorkItemRef, Nr as parseWorkMutationResult, Or as parseWorkConnection, Pr as parseWorkQuery, Q as text$6, Qr as MAX_PREVIEW_HEIGHT, Rr as parseWorkUpdate, S as writeRows, Sn as parseThreadInput, Sr as MAX_CANVAS_Y, Tn as parseThreadSnapshot, Tr as parseSectionData, Tt as readConversationLocation, Ur as fieldByRole, V as parseTaskDesign, Vr as parseMessage, W as CommentMergeError, Wr as parseWorkSchema, X as parseCommentThread, Xr as record$2, Y as object$2, Yr as parseWork, Z as revision$2, Zr as strict$2, Zt as Loader, _ as requestComments, _n as parseAnchor, ai as parsePreviewId, an as LiveStatus, at as FieldCardHeader, b as plainRow, bn as parseThreadFilter, ct as initialShellTabs, d as saveWorkspace, dn as Text, dt as readTabsCookie, ei as PREVIEW_TILE_HEIGHT, ft as stepShellTab, gn as MAX_THREADS, gt as visitShellTab, hn as parseWorkChange, ht as tabsCookieValue, ii as parsePreviewHash, it as FieldCardContent, jr as parseWorkItem, jt as legacyFileHref, kr as parseWorkCreate, l as projectStore, ln as readPreference, lt as openShellTab, mn as parseLiveId, mt as tabsCookieName, n as unavailableComments, ni as parsePreviewBranch, nn as ShellTabBar, oi as parsePreviewPins, ot as activeTab, pn as RESYNC, pt as tabHref, q as parseCommentInput$1, qr as identifier$3, qt as taskRoutes, r as followThreads, ri as parsePreviewCapture, rt as FieldCard, si as parsePreviewViewport, sn as AppShell, st as closeShellTab, t as commentsUnavailable, un as writePreference, ut as parseShellTabs, v as projectLive, vn as parseThread, wn as parseThreadReplyInput, wr as parseWorkWorkspace, xn as parseThreadId, y as routes, yn as parseThreadCapabilities, yt as conversationDestination, zr as parseWorkValue } from "./assets/comment-data-nUlI0szN.js";
2
- import { n as requestTasks } from "./assets/tasks-request-Bwky5ORf.js";
1
+ import { $r as parseWork, $t as Button, An as parseThreadStatus, Ar as parseWorkActionInput, Br as parseWorkSync, Cn as parseThreadCapabilities, Ct as legacyConversationHref, Dn as parseThreadLinkInput, Dr as parseElementAnchor, Dt as taskHref, En as parseThreadInput, Er as MAX_CANVAS_Y, Et as selectedTask, Fr as parseWorkItem, G as mergeThreads, Gr as parseMessage, Gt as fileRoutes, Hr as parseWorkUpdate, Ir as parseWorkItemRef, J as conversationId, Jr as parseWorkSchema, K as stableJson, Kr as parseMessageAuthor, Kt as pageRoutes, Lr as parseWorkMutationResult, Mr as parseWorkConnection, Nr as parseWorkCreate, On as parseThreadReplyInput, Or as parseWorkWorkspace, Pr as parseWorkDetail, Q as text$6, Qr as nonempty$1, Rr as parseWorkQuery, S as writeRows, Sn as parseThread, Tn as parseThreadId, Tt as readConversationLocation, Ur as parseWorkValue, V as parseTaskDesign, Vr as parseWorkSyncResult, W as CommentMergeError, Wr as valueByRole, X as parseCommentThread, Xr as WorkValidationError, Y as object$2, Yr as statusCategory, Z as revision$2, Zr as identifier$3, Zt as Loader, _ as requestComments, _n as RESYNC, an as LiveStatus, at as FieldCardHeader, b as plainRow, bn as MAX_THREADS, ci as parsePreviewHash, ct as initialShellTabs, d as saveWorkspace, di as parsePreviewViewport, dn as isLocale, dt as readTabsCookie, ei as record$2, fn as locale, ft as stepShellTab, gt as visitShellTab, hn as Text, ht as tabsCookieValue, ii as PREVIEW_TILE_HEIGHT, it as FieldCardContent, jn as parseThreadStatusInput, jr as parseWorkActivity, jt as legacyFileHref, kn as parseThreadSnapshot, kr as parseSectionData, l as projectStore, li as parsePreviewId, ln as readPreference, lt as openShellTab, mn as useLocaleSource, mt as tabsCookieName, n as unavailableComments, ni as MAX_PREVIEW_HEIGHT, nn as ShellTabBar, oi as parsePreviewBranch, ot as activeTab, pn as t, pt as tabHref, q as parseCommentInput$1, qr as fieldByRole, qt as taskRoutes, r as followThreads, ri as PREVIEW_THUMBNAIL_WIDTHS, rt as FieldCard, si as parsePreviewCapture, sn as AppShell, st as closeShellTab, t as commentsUnavailable, ti as strict$2, ui as parsePreviewPins, un as writePreference, ut as parseShellTabs, v as projectLive, vn as parseLiveId, wn as parseThreadFilter, xn as parseAnchor, y as routes, yn as parseWorkChange, yt as conversationDestination, zr as parseWorkSnapshot } from "./assets/comment-data-q_8INiZz.js";
2
+ import { n as requestTasks } from "./assets/tasks-request-Bie1PpQP.js";
3
3
  import { access, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
4
4
  import * as Path from "node:path";
5
5
  import { dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -25,6 +25,7 @@ import { createMultiMatcher } from "remix/route-pattern/match";
25
25
  import { renderToString } from "remix/component/server";
26
26
  import { createRedirectResponse } from "remix/response/redirect";
27
27
  import { createServer } from "node:net";
28
+ import { AsyncLocalStorage } from "node:async_hooks";
28
29
  //#region ../beads-d1/src/lib/d1.ts
29
30
  /**
30
31
  * A failure whose outcome is known: the statements did not apply. Transports throw it for
@@ -22799,6 +22800,7 @@ var ConfigFields = check(section$3("Bud config must export an object.", {
22799
22800
  files: any(),
22800
22801
  logo: any(),
22801
22802
  theme: any(),
22803
+ locale: any(),
22802
22804
  picker: any(),
22803
22805
  branches: any(),
22804
22806
  flows: any()
@@ -22839,7 +22841,7 @@ var BudRoutesSchema = check(list(RouteSchema, "bud.config must define at least o
22839
22841
  var port = (value) => value === void 0 || Number.isInteger(value) && value >= 0 && value <= 65535;
22840
22842
  var remote$1 = (value) => typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value);
22841
22843
  /** A project's config, validated and normalized before Bud starts any listeners. */
22842
- var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(ConfigFields, (config) => ({
22844
+ var BudConfigSchema = delegate(check(delegate(check(check(delegate(check(delegate(ConfigFields, (config) => ({
22843
22845
  ...config,
22844
22846
  routes: parseConfig(BudRoutesSchema, config.routes)
22845
22847
  })), ({ port: companion, target }) => port(companion ?? 0) && port(target?.port) ? void 0 : "Ports must be integers between 0 and 65535."), ({ target, ...config }) => {
@@ -22848,7 +22850,7 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
22848
22850
  ...config,
22849
22851
  targetOrigin: origin ? normalizeOrigin(origin, "target.origin must be an HTTP(S) origin without a path or credentials.") : origin
22850
22852
  };
22851
- }), ({ comments }) => remote$1(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
22853
+ }), ({ locale }) => locale === void 0 || locale === "en" || locale === "de" ? void 0 : "locale must be \"en\" or \"de\"."), ({ comments }) => remote$1(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
22852
22854
  let flows = parseConfig(BudFlowsSchema, config.flows);
22853
22855
  let steps = flows.flatMap((flow) => flow.steps.map((step) => flowStepRoute(flow, step)));
22854
22856
  let home = config.routes.findIndex((route) => route.path === "/") + 1;
@@ -22875,6 +22877,7 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
22875
22877
  files: parseConfig(BudFilesSchema, config.files),
22876
22878
  logo: parseConfig(BudLogoSchema, config.logo),
22877
22879
  theme: parseConfig(BudThemeSchema, config.theme),
22880
+ locale: config.locale === void 0 ? "en" : config.locale,
22878
22881
  picker: parseConfig(BudPickerSchema, config.picker),
22879
22882
  branches: parseConfig(BudBranchesSchema, config.branches)
22880
22883
  }));
@@ -37470,10 +37473,10 @@ function ProjectLiveStatus(handle) {
37470
37473
  //#endregion
37471
37474
  //#region app/client/screens.ts
37472
37475
  var builtIns = {
37473
- "bud:pages": () => import("./assets/pages-0aWH7z1j.js"),
37474
- "bud:canvas": () => import("./assets/canvas-BZhzizzt.js"),
37475
- "bud:tasks": () => import("./assets/tasks-C0WqnM7Q.js"),
37476
- "bud:files": () => import("./assets/files-CTzcWGjb.js")
37476
+ "bud:pages": () => import("./assets/pages-D6VkmdfQ.js"),
37477
+ "bud:canvas": () => import("./assets/canvas-D_F8SIlm.js"),
37478
+ "bud:tasks": () => import("./assets/tasks-A1_bpb3_.js"),
37479
+ "bud:files": () => import("./assets/files-xazOrNCX.js")
37477
37480
  };
37478
37481
  /**
37479
37482
  * Imports a screen module: a built-in by its key, any other by the URL the server gave it.
@@ -37559,7 +37562,7 @@ function ScreenHost(handle) {
37559
37562
  } catch (cause) {
37560
37563
  if (handle.signal.aborted) return;
37561
37564
  console.error(cause);
37562
- error = cause instanceof Error && cause.message ? cause.message : `Could not open ${label}.`;
37565
+ error = cause instanceof Error && cause.message ? cause.message : t("app.client.screenHost.couldNotOpen", { label });
37563
37566
  }
37564
37567
  handle.update();
37565
37568
  }
@@ -37571,7 +37574,7 @@ function ScreenHost(handle) {
37571
37574
  let context = opened?.context;
37572
37575
  let routeError = "";
37573
37576
  if (opened && context) try {
37574
- if (location.routes && !matchScreenRoutes(location.routes, location.href)) throw new Error(`No screen route matches ${location.href}.`);
37577
+ if (location.routes && !matchScreenRoutes(location.routes, location.href)) throw new Error(t("app.client.screenHost.noRouteMatches", { href: location.href }));
37575
37578
  context = matchScreenContext(opened.screen.routes, {
37576
37579
  ...context,
37577
37580
  active: handle.props.active,
@@ -37579,7 +37582,7 @@ function ScreenHost(handle) {
37579
37582
  url: new URL(location.href, context.url.origin)
37580
37583
  });
37581
37584
  } catch (cause) {
37582
- routeError = cause instanceof Error ? cause.message : "This screen route is invalid.";
37585
+ routeError = cause instanceof Error ? cause.message : t("app.client.screenHost.invalidRoute");
37583
37586
  }
37584
37587
  return /* @__PURE__ */ jsx("div", {
37585
37588
  class: "h-full min-h-0",
@@ -37596,7 +37599,7 @@ function ScreenHost(handle) {
37596
37599
  class: "max-w-2xl",
37597
37600
  role: "alert",
37598
37601
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
37599
- title: `${label} didn't open`,
37602
+ title: t("app.client.screenHost.didntOpen", { label }),
37600
37603
  subtitle: error
37601
37604
  }), /* @__PURE__ */ jsxs(FieldCardContent, {
37602
37605
  class: "flex-col items-start gap-3",
@@ -37604,17 +37607,17 @@ function ScreenHost(handle) {
37604
37607
  as: "p",
37605
37608
  variant: "ui",
37606
37609
  tone: "muted",
37607
- children: "Fix the screen and retry. Details are in the browser console."
37610
+ children: t("app.client.screenHost.fixAndRetry")
37608
37611
  }), /* @__PURE__ */ jsx(Button, {
37609
37612
  mix: on("click", () => {
37610
37613
  if ("error" in module || handle.props.taskDesign && "error" in handle.props.taskDesign) window.location.reload();
37611
37614
  else open();
37612
37615
  }),
37613
- children: "Retry"
37616
+ children: t("app.client.screenHost.retry")
37614
37617
  })]
37615
37618
  })]
37616
37619
  })
37617
- }) : /* @__PURE__ */ jsx(Loader, { label: `Loading ${label}…` })
37620
+ }) : /* @__PURE__ */ jsx(Loader, { label: t("app.client.screenHost.loading", { label }) })
37618
37621
  });
37619
37622
  };
37620
37623
  }
@@ -37633,11 +37636,11 @@ async function resolveConversation(id, targets, token, signal, fetcher = fetch)
37633
37636
  id = parseThreadId(id);
37634
37637
  let thread = await requestComments(routes.comments.get.href({ id }), token, signal, parseThread, fetcher);
37635
37638
  signal.throwIfAborted();
37636
- if (thread.id !== id) throw new Error("The comments service returned another discussion.");
37639
+ if (thread.id !== id) throw new Error(t("app.client.conversationRoute.anotherDiscussion"));
37637
37640
  let destination = conversationDestination(thread);
37638
37641
  let target = targets.find((target) => target.module === destination.module);
37639
- if (!target) throw new Error("Enable the screen that owns this discussion to open it.");
37640
- if (target.routes && !matchScreenRoutes(target.routes, destination.href)) throw new Error("The discussion location is not available in this screen.");
37642
+ if (!target) throw new Error(t("app.client.conversationRoute.enableOwningScreen"));
37643
+ if (target.routes && !matchScreenRoutes(target.routes, destination.href)) throw new Error(t("app.client.conversationRoute.locationUnavailable"));
37641
37644
  return {
37642
37645
  active: target.id,
37643
37646
  module: { url: target.module },
@@ -37712,14 +37715,14 @@ function Workspace(handle) {
37712
37715
  changed();
37713
37716
  try {
37714
37717
  let project = handle.props.project;
37715
- if (!project) throw new Error("Connect a project to open this discussion.");
37718
+ if (!project) throw new Error(t("app.client.workspace.connectProjectToOpenDiscussion"));
37716
37719
  let next = await resolveConversation(id, handle.props.conversationTargets ?? [], project.token, signal);
37717
37720
  if (!signal.aborted && conversation === id) {
37718
37721
  presented = next;
37719
37722
  hrefs.set(next.active, window.location.pathname);
37720
37723
  }
37721
37724
  } catch (cause) {
37722
- if (!signal.aborted) error = cause instanceof Error ? cause.message : "Could not open this discussion.";
37725
+ if (!signal.aborted) error = cause instanceof Error ? cause.message : t("app.client.workspace.couldNotOpenDiscussion");
37723
37726
  } finally {
37724
37727
  if (request === controller && !signal.aborted) {
37725
37728
  loading = false;
@@ -37762,7 +37765,7 @@ function Workspace(handle) {
37762
37765
  let { pathname } = new URL(href, "http://bud.local");
37763
37766
  if (readConversationLocation(href)) return {
37764
37767
  href,
37765
- title: "Discussion",
37768
+ title: t("app.client.workspace.discussion"),
37766
37769
  icon: "message"
37767
37770
  };
37768
37771
  let match = handle.props.screens.map((item) => ({
@@ -37924,19 +37927,19 @@ function Workspace(handle) {
37924
37927
  children: project ? /* @__PURE__ */ jsxs("div", {
37925
37928
  class: "h-full min-h-0",
37926
37929
  mix: mount,
37927
- children: [loading ? /* @__PURE__ */ jsx(Loader, { label: "Opening discussion…" }) : error ? /* @__PURE__ */ jsx("div", {
37930
+ children: [loading ? /* @__PURE__ */ jsx(Loader, { label: t("app.client.workspace.openingDiscussion") }) : error ? /* @__PURE__ */ jsx("div", {
37928
37931
  class: "p-3 md:p-4",
37929
37932
  children: /* @__PURE__ */ jsxs(FieldCard, {
37930
37933
  class: "max-w-2xl",
37931
37934
  role: "alert",
37932
37935
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
37933
- title: "Discussion didn't open",
37936
+ title: t("app.client.workspace.discussionDidntOpen"),
37934
37937
  subtitle: error
37935
37938
  }), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Button, {
37936
37939
  mix: on("click", () => {
37937
37940
  if (conversation) resolve(conversation);
37938
37941
  }),
37939
- children: "Retry"
37942
+ children: t("app.client.workspace.retry")
37940
37943
  }) })]
37941
37944
  })
37942
37945
  }) : null, [...opened].map(([id, { module, taskDesign, location }]) => /* @__PURE__ */ jsx("div", {
@@ -37954,12 +37957,12 @@ function Workspace(handle) {
37954
37957
  }) : /* @__PURE__ */ jsxs(FieldCard, {
37955
37958
  class: "max-w-2xl",
37956
37959
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
37957
- title: "Your project, side by side.",
37958
- subtitle: "Open Bud beside a local project to review its pages, files and tasks."
37960
+ title: t("app.client.workspace.yourProjectSideBySide"),
37961
+ subtitle: t("app.client.workspace.openBudBesideLocalProject")
37959
37962
  }), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Text, {
37960
37963
  as: "p",
37961
37964
  variant: "ui",
37962
- children: "Start your project with the Bud Vite plugin, or run Bud from its directory. Its screens appear here when the companion connects."
37965
+ children: t("app.client.workspace.startProjectWithBudVitePlugin")
37963
37966
  }) })]
37964
37967
  })
37965
37968
  });
@@ -38116,7 +38119,7 @@ function createAppController(template, project, runtime) {
38116
38119
  let title = (conversation ? "Discussion" : screen.label).replace(/[&<>]/g, (character) => `&#${character.charCodeAt(0)};`);
38117
38120
  let logo = project?.config.logo;
38118
38121
  let shell = logo ? withLogoIcons(template, logo, (theme) => routes.logo.href({ theme })) : template;
38119
- return new Response(shell.replace("<title>bud.sh</title>", () => `<title>${title} · bud.sh</title>`).replace("</head>", () => project ? `${themeStyle(project.config.theme)}</head>` : "</head>").replace("<!--app-html-->", () => content), { headers: { "Content-Type": "text/html; charset=utf-8" } });
38122
+ return new Response(shell.replace("<html lang=\"en\"", () => `<html lang="${locale()}"`).replace("<title>bud.sh</title>", () => `<title>${title} · bud.sh</title>`).replace("</head>", () => project ? `${themeStyle(project.config.theme)}</head>` : "</head>").replace("<!--app-html-->", () => content), { headers: { "Content-Type": "text/html; charset=utf-8" } });
38120
38123
  }
38121
38124
  return createController(routes, { actions: {
38122
38125
  conversation({ params, request }) {
@@ -41248,6 +41251,19 @@ function previewAnchors(root) {
41248
41251
  };
41249
41252
  }
41250
41253
  //#endregion
41254
+ //#region app/server/locale.ts
41255
+ var requestLocale = new AsyncLocalStorage();
41256
+ useLocaleSource(() => requestLocale.getStore());
41257
+ /**
41258
+ * Runs a request's handling in its language, so what it renders speaks it.
41259
+ * @param value - The request's locale; English when it isn't one of Bud's.
41260
+ * @param handle - The request's handling.
41261
+ * @returns What the handling returns.
41262
+ */
41263
+ function inLocale(value, handle) {
41264
+ return requestLocale.run(isLocale(value) ? value : "en", handle);
41265
+ }
41266
+ //#endregion
41251
41267
  //#region app/server/hosted/contract.ts
41252
41268
  /**
41253
41269
  * The screens a hosted Bud shows: Pages as captured pictures, and no Files, which need a
@@ -41403,7 +41419,10 @@ function createHostedApp(options) {
41403
41419
  return made;
41404
41420
  };
41405
41421
  return {
41406
- fetch: async (request, context) => routerFor(hostedAuthor(context?.author)).fetch(request),
41422
+ fetch: async (request, context) => {
41423
+ let router = routerFor(hostedAuthor(context?.author));
41424
+ return inLocale(context?.locale ?? config.locale, () => router.fetch(request));
41425
+ },
41407
41426
  dispose: () => runtime.dispose()
41408
41427
  };
41409
41428
  }
@@ -29084,6 +29084,7 @@ var ConfigFields = check(section("Bud config must export an object.", {
29084
29084
  files: any(),
29085
29085
  logo: any(),
29086
29086
  theme: any(),
29087
+ locale: any(),
29087
29088
  picker: any(),
29088
29089
  branches: any(),
29089
29090
  flows: any()
@@ -29124,7 +29125,7 @@ var BudRoutesSchema = check(list(RouteSchema, "bud.config must define at least o
29124
29125
  var port = (value) => value === void 0 || Number.isInteger(value) && value >= 0 && value <= 65535;
29125
29126
  var remote$1 = (value) => typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value);
29126
29127
  /** A project's config, validated and normalized before Bud starts any listeners. */
29127
- var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(ConfigFields, (config) => ({
29128
+ var BudConfigSchema = delegate(check(delegate(check(check(delegate(check(delegate(ConfigFields, (config) => ({
29128
29129
  ...config,
29129
29130
  routes: parseConfig(BudRoutesSchema, config.routes)
29130
29131
  })), ({ port: companion, target }) => port(companion ?? 0) && port(target?.port) ? void 0 : "Ports must be integers between 0 and 65535."), ({ target, ...config }) => {
@@ -29133,7 +29134,7 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
29133
29134
  ...config,
29134
29135
  targetOrigin: origin ? normalizeOrigin(origin, "target.origin must be an HTTP(S) origin without a path or credentials.") : origin
29135
29136
  };
29136
- }), ({ comments }) => remote$1(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
29137
+ }), ({ locale }) => locale === void 0 || locale === "en" || locale === "de" ? void 0 : "locale must be \"en\" or \"de\"."), ({ comments }) => remote$1(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
29137
29138
  let flows = parseConfig(BudFlowsSchema, config.flows);
29138
29139
  let steps = flows.flatMap((flow) => flow.steps.map((step) => flowStepRoute(flow, step)));
29139
29140
  let home = config.routes.findIndex((route) => route.path === "/") + 1;
@@ -29160,6 +29161,7 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
29160
29161
  files: parseConfig(BudFilesSchema, config.files),
29161
29162
  logo: parseConfig(BudLogoSchema, config.logo),
29162
29163
  theme: parseConfig(BudThemeSchema, config.theme),
29164
+ locale: config.locale === void 0 ? "en" : config.locale,
29163
29165
  picker: parseConfig(BudPickerSchema, config.picker),
29164
29166
  branches: parseConfig(BudBranchesSchema, config.branches)
29165
29167
  }));