@bud.sh/app 0.3.0 → 0.4.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 (77) hide show
  1. package/dist/client/assets/bud/design.js +1 -1
  2. package/dist/client/assets/canvas-DIKX_HCJ.js +1 -0
  3. package/dist/client/assets/difference-worker-CwQNxTJL.js +8 -0
  4. package/dist/client/assets/files-BQ9O3Z8-.js +1 -0
  5. package/dist/client/assets/i18n-CL0jBP7Y.js +1 -0
  6. package/dist/client/assets/index-COvyj5zV.js +2 -0
  7. package/dist/client/assets/pages-BGl2Yuza.js +1 -0
  8. package/dist/client/assets/project-branches-DwKjCwZv.js +1 -0
  9. package/dist/client/assets/project-comments-Cdip2Y10.js +1 -0
  10. package/dist/client/assets/project-thread-links-BGIkGQeU.js +1 -0
  11. package/dist/client/assets/{project-threads-DezXiVK6.js → project-threads-DeKXNqeP.js} +104 -104
  12. package/dist/client/assets/{project-work-CB1JHqGW.js → project-work-B7PGQtV9.js} +1 -1
  13. package/dist/client/assets/task-design-DJp5yho1.js +23 -0
  14. package/dist/client/assets/tasks-BHXj727C.js +1 -0
  15. package/dist/client/assets/tasks-request-CsUB5__X.js +1 -0
  16. package/dist/client/assets/workspace-CbO7RsI2.js +2 -0
  17. package/dist/client/index.html +2 -1
  18. package/dist/hosted/assets/{canvas-C69-96GR.js → canvas-gWj_ocKK.js} +69 -41
  19. package/dist/hosted/assets/{comment-data-CiDuvXi2.js → comment-data-C-rNCB1n.js} +7323 -1068
  20. package/dist/hosted/assets/{files-DQL5hzs3.js → files-CAPvbavY.js} +92 -89
  21. package/dist/hosted/assets/{pages-DFs7jB6M.js → pages-DJiOvT-l.js} +31 -22
  22. package/dist/hosted/assets/{project-branches-CffV4IS1.js → project-branches-BgrfdvtL.js} +5 -5
  23. package/dist/hosted/assets/{project-thread-links-B1Y2bDUx.js → project-thread-links-B7uzThL9.js} +12 -12
  24. package/dist/hosted/assets/{screen-BroBb1Z_.js → screen-D4D9nh4V.js} +18 -18
  25. package/dist/hosted/assets/{tasks-mzmNhe2d.js → tasks-C7aGLzBw.js} +29 -29
  26. package/dist/hosted/assets/{tasks-request-Dcrpd2_8.js → tasks-request-CikFdCgP.js} +7 -7
  27. package/dist/hosted/hosted.js +58 -31
  28. package/dist/node/design.js +6355 -139
  29. package/dist/node/index.js +4 -2
  30. package/dist/node-host/assets/{canvas-BZhzizzt.js → canvas-kXzp80a5.js} +70 -42
  31. package/dist/node-host/assets/{comment-data-nUlI0szN.js → comment-data-CeuJbJKJ.js} +7349 -1094
  32. package/dist/node-host/assets/{files-CTzcWGjb.js → files-0YIuvKzb.js} +92 -89
  33. package/dist/node-host/assets/{pages-0aWH7z1j.js → pages-DOF22SyG.js} +31 -22
  34. package/dist/node-host/assets/{project-branches-1zs_0J_y.js → project-branches-BeEwMBIw.js} +5 -5
  35. package/dist/node-host/assets/{project-thread-links-LpUAGcDr.js → project-thread-links-Bs_MtfBC.js} +12 -12
  36. package/dist/node-host/assets/{screen-DQoTOWYR.js → screen-DRTAKkn3.js} +18 -18
  37. package/dist/node-host/assets/{tasks-C0WqnM7Q.js → tasks-BA_hcbj5.js} +30 -30
  38. package/dist/node-host/assets/{tasks-request-Bwky5ORf.js → tasks-request-Doc9y_20.js} +7 -7
  39. package/dist/node-host/node-host.js +58 -31
  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-CiN6jNIZ.js} +70 -42
  42. package/dist/server/assets/{comment-data-BuArfgcc.js → comment-data-9SqALQjT.js} +7347 -1092
  43. package/dist/server/assets/{files-DkrEB48U.js → files-DBVqeLdL.js} +92 -89
  44. package/dist/server/assets/{listener-oKd5ETMj.js → listener-DGWKUc2o.js} +53 -31
  45. package/dist/server/assets/{pages-iEdmK_o2.js → pages-DJMLr6j9.js} +31 -22
  46. package/dist/server/assets/{project-branches-B7rtrvMl.js → project-branches-DbKEmiQT.js} +5 -5
  47. package/dist/server/assets/{project-thread-links-ue_ad9IX.js → project-thread-links-C0kV_Pcl.js} +12 -12
  48. package/dist/server/assets/{project-work-6aOyja4v.js → project-work-C0FN3w5Y.js} +2 -2
  49. package/dist/server/assets/{screen-DMUB9a-K.js → screen-BtvzEiIa.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-DX71x8f9.js} +31 -31
  53. package/dist/server/assets/{tasks-request-Bk5EoY_p.js → tasks-request-C3jk9Jyu.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 { Fn as createMultiMatcher, gi as parseWorkQuery, mi as parseWorkItemRef, y as routes } from "./comment-data-CiDuvXi2.js";
1
+ import { Bn as createMultiMatcher, mn as t, xi as parseWorkQuery, y as routes, yi as parseWorkItemRef } from "./comment-data-C-rNCB1n.js";
2
2
  //#region app/client/tasks-request.ts
3
3
  var reads = createMultiMatcher();
4
4
  var writes = createMultiMatcher();
@@ -37,7 +37,7 @@ function validEndpoint(url, write) {
37
37
  * @returns A validated DTO; error responses never become an empty successful view.
38
38
  */
39
39
  async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
40
- if (!validEndpoint(url, Boolean(write))) throw new Error("Invalid task endpoint.");
40
+ if (!validEndpoint(url, Boolean(write))) throw new Error(t("app.client.tasksRequest.invalidEndpoint"));
41
41
  signal.throwIfAborted();
42
42
  let response = await fetcher(url, {
43
43
  method: write ? "POST" : "GET",
@@ -52,7 +52,7 @@ async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
52
52
  signal
53
53
  });
54
54
  let reader = response.body?.getReader();
55
- if (!reader) throw new Error("The task service returned an empty response.");
55
+ if (!reader) throw new Error(t("app.client.tasksRequest.emptyResponse"));
56
56
  let chunks = [];
57
57
  let size = 0;
58
58
  try {
@@ -61,7 +61,7 @@ async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
61
61
  let part = await reader.read();
62
62
  if (part.done) break;
63
63
  size += part.value.byteLength;
64
- if (size > 4194304) throw new Error("The task response is too large to display. Narrow the results and try again.");
64
+ if (size > 4194304) throw new Error(t("app.client.tasksRequest.responseTooLarge"));
65
65
  chunks.push(part.value);
66
66
  }
67
67
  } catch (cause) {
@@ -80,15 +80,15 @@ async function requestTasks(url, token, signal, parse, fetcher = fetch, write) {
80
80
  try {
81
81
  value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
82
82
  } catch {
83
- throw new Error("The task service returned an invalid response. Refresh and try again.");
83
+ throw new Error(t("app.client.tasksRequest.invalidResponse"));
84
84
  }
85
85
  if (!response.ok) {
86
- let error = value && typeof value === "object" && "error" in value && typeof value.error === "string" ? value.error.slice(0, 1e3) : "Could not load tasks. Try again.";
86
+ let error = value && typeof value === "object" && "error" in value && typeof value.error === "string" ? value.error.slice(0, 1e3) : t("app.client.tasksRequest.loadFailed");
87
87
  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";
88
88
  let currentRevision;
89
89
  if (value && typeof value === "object" && "currentRevision" in value) {
90
90
  let revision = value.currentRevision;
91
- 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.");
91
+ if (revision !== null && (typeof revision !== "string" || !revision.trim() || revision.length > 256 || revision.includes("\0"))) throw new Error(t("app.client.tasksRequest.invalidConflictResponse"));
92
92
  currentRevision = revision;
93
93
  }
94
94
  throw new TaskRequestError(error, code, currentRevision);
@@ -1,5 +1,5 @@
1
- import { $n as parseAnchor, $t as Button, Ai as nonempty$1, An as Frame, Bi as fail$11, Bn as PREVIEW_THUMBNAIL_WIDTHS, Ci as parseMessage, Cn as SELF_CLOSING_TAGS, Ct as legacyConversationHref, Di as statusCategory, Dn as serializeStyleObject, Dt as taskHref, Ei as parseWorkSchema, En as sanitizeUrlAttribute, Et as selectedTask, Fi as array$2, Fn as createMultiMatcher, G as mergeThreads, Gi as parse, Gn as parsePreviewHash, Gt as fileRoutes, Hi as number$2, Ii as boolean$2, In as equal, J as conversationId, Jn as parsePreviewViewport, K as stableJson, Ki as parseSafe, Kn as parsePreviewId, Kt as pageRoutes, Li as createSchema, Ln as joinPatterns, Mi as record$1, Mn as createFrameHandle, Ni as strict$2, Nn as jsx, Oi as WorkValidationError, On as shouldStringifyBooleanAttribute, Pi as any, Pn as Route, Q as text$6, Qn as MAX_THREADS, Ri as defaulted, Rn as RoutePattern, S as writeRows, Si as valueByRole, Sn as FRAMEWORK_PROPS, Ti as fieldByRole, Tn as normalizeAttributeName, Tt as readConversationLocation, Ui as object$1, Un as parsePreviewBranch, V as parseTaskDesign, Vn as PREVIEW_TILE_HEIGHT, W as CommentMergeError, Wi as optional$3, Wn as parsePreviewCapture, X as parseCommentThread, Xn as parseLiveId, Y as object$2, Yn as RESYNC, Z as revision$2, Zn as parseWorkChange, Zt as Loader, _ as requestComments, _i as parseWorkSnapshot, _n as normalizeUnsafeHTMLProps, ai as parseElementAnchor, an as LiveStatus, ar as parseThreadLinkInput, at as FieldCardHeader, b as plainRow, bi as parseWorkUpdate, bn as clientEntry, ci as parseWorkActionInput, cr as parseThreadStatus, ct as initialShellTabs, d as saveWorkspace, di as parseWorkCreate, dn as Text, dt as readTabsCookie, er as parseThread, fi as parseWorkDetail, ft as stepShellTab, gi as parseWorkQuery, gn as on, gt as visitShellTab, hi as parseWorkMutationResult, hn as stripFlushMarkers, ht as tabsCookieValue, ii as MAX_CANVAS_Y, ir as parseThreadInput, it as FieldCardContent, ji as parseWork, jn as createComponent, jt as legacyFileHref, ki as identifier$3, kn as Fragment, l as projectStore, li as parseWorkActivity, ln as readPreference, lr as parseThreadStatusInput, lt as openShellTab, mi as parseWorkItemRef, mn as appendFlushMarker, mt as tabsCookieName, n as unavailableComments, nn as ShellTabBar, nr as parseThreadFilter, oi as parseWorkWorkspace, or as parseThreadReplyInput, ot as activeTab, pi as parseWorkItem, pn as ref$1, pt as tabHref, q as parseCommentInput$1, qi as string$2, qn as parsePreviewPins, qt as taskRoutes, r as followThreads, rr as parseThreadId, rt as FieldCard, si as parseSectionData, sn as AppShell, sr as parseThreadSnapshot, st as closeShellTab, t as commentsUnavailable, tr as parseThreadCapabilities, ui as parseWorkConnection, un as writePreference, ut as parseShellTabs, v as projectLive, vi as parseWorkSync, vn as composeMixedProps, wi as parseMessageAuthor, wn as isAllowedHostPropName, xi as parseWorkValue, xn as isEntry, y as routes, yi as parseWorkSyncResult, yn as resolveMixDescriptors, yt as conversationDestination, zi as enum_, zn as MAX_PREVIEW_HEIGHT } from "./assets/comment-data-CiDuvXi2.js";
2
- import { n as requestTasks } from "./assets/tasks-request-Dcrpd2_8.js";
1
+ import { $n as parsePreviewViewport, $t as Button, Ai as fieldByRole, An as normalizeAttributeName, Bi as array$2, Bn as createMultiMatcher, Ci as parseWorkSync, Cn as composeMixedProps, Ct as legacyConversationHref, Di as valueByRole, Dn as FRAMEWORK_PROPS, Dt as taskHref, Ei as parseWorkValue, En as isEntry, Et as selectedTask, Fi as nonempty$1, Fn as Frame, G as mergeThreads, Gi as fail$11, Gn as PREVIEW_THUMBNAIL_WIDTHS, Gt as fileRoutes, Hi as createSchema, Hn as joinPatterns, Ii as parseWork, In as createComponent, J as conversationId, Ji as object$1, Jn as parsePreviewBranch, K as stableJson, Kn as PREVIEW_TILE_HEIGHT, Kt as pageRoutes, Li as record$1, Ln as createFrameHandle, Mi as statusCategory, Mn as serializeStyleObject, Ni as WorkValidationError, Nn as shouldStringifyBooleanAttribute, Oi as parseMessage, On as SELF_CLOSING_TAGS, Pi as identifier$3, Pn as Fragment, Q as text$6, Qi as string$2, Qn as parsePreviewPins, Ri as strict$2, Rn as jsx, S as writeRows, Si as parseWorkSnapshot, Sn as normalizeUnsafeHTMLProps, Ti as parseWorkUpdate, Tn as clientEntry, Tt as readConversationLocation, Ui as defaulted, Un as RoutePattern, V as parseTaskDesign, Vi as boolean$2, Vn as equal, W as CommentMergeError, Wi as enum_, Wn as MAX_PREVIEW_HEIGHT, X as parseCommentThread, Xi as parse, Xn as parsePreviewHash, Y as object$2, Yi as optional$3, Yn as parsePreviewCapture, Z as revision$2, Zi as parseSafe, Zn as parsePreviewId, Zt as Loader, _ as requestComments, _i as parseWorkDetail, an as LiveStatus, ar as parseThread, at as FieldCardHeader, b as plainRow, bi as parseWorkMutationResult, bn as stripFlushMarkers, cr as parseThreadId, ct as initialShellTabs, d as saveWorkspace, di as parseWorkWorkspace, dn as hasMessage, dr as parseThreadReplyInput, dt as readTabsCookie, er as RESYNC, fi as parseSectionData, fn as isLocale, fr as parseThreadSnapshot, ft as stepShellTab, gi as parseWorkCreate, gn as Text, gt as visitShellTab, hi as parseWorkConnection, hn as useLocaleSource, ht as tabsCookieValue, ir as parseAnchor, it as FieldCardContent, ji as parseWorkSchema, jn as sanitizeUrlAttribute, jt as legacyFileHref, ki as parseMessageAuthor, kn as isAllowedHostPropName, l as projectStore, li as MAX_CANVAS_Y, ln as readPreference, lr as parseThreadInput, lt as openShellTab, mi as parseWorkActivity, mn as t, mr as parseThreadStatusInput, mt as tabsCookieName, n as unavailableComments, nn as ShellTabBar, nr as parseWorkChange, or as parseThreadCapabilities, ot as activeTab, pi as parseWorkActionInput, pn as locale, pr as parseThreadStatus, pt as tabHref, q as parseCommentInput$1, qi as number$2, qt as taskRoutes, r as followThreads, rr as MAX_THREADS, rt as FieldCard, sn as AppShell, sr as parseThreadFilter, st as closeShellTab, t as commentsUnavailable, tr as parseLiveId, ui as parseElementAnchor, un as writePreference, ur as parseThreadLinkInput, ut as parseShellTabs, v as projectLive, vi as parseWorkItem, vn as ref$1, wi as parseWorkSyncResult, wn as resolveMixDescriptors, xi as parseWorkQuery, xn as on, y as routes, yi as parseWorkItemRef, yn as appendFlushMarker, yt as conversationDestination, zi as any, zn as Route } from "./assets/comment-data-C-rNCB1n.js";
2
+ import { n as requestTasks } from "./assets/tasks-request-CikFdCgP.js";
3
3
  import * as Crypto from "node:crypto";
4
4
  import { createHash, randomBytes, randomUUID } from "node:crypto";
5
5
  import * as Path from "node:path";
@@ -16,6 +16,7 @@ import { pathToFileURL } from "node:url";
16
16
  import { access, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
17
17
  import { gunzipSync, gzipSync } from "node:zlib";
18
18
  import { createServer } from "node:net";
19
+ import { AsyncLocalStorage } from "node:async_hooks";
19
20
  //#region ../../node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/Pipeable.js
20
21
  /**
21
22
  * The `Pipeable` module defines the shared interface and implementation helpers
@@ -36808,6 +36809,7 @@ var ConfigFields = check(section("Bud config must export an object.", {
36808
36809
  files: any(),
36809
36810
  logo: any(),
36810
36811
  theme: any(),
36812
+ locale: any(),
36811
36813
  picker: any(),
36812
36814
  branches: any(),
36813
36815
  flows: any()
@@ -36848,7 +36850,7 @@ var BudRoutesSchema = check(list(RouteSchema, "bud.config must define at least o
36848
36850
  var port = (value) => value === void 0 || Number.isInteger(value) && value >= 0 && value <= 65535;
36849
36851
  var remote = (value) => typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value);
36850
36852
  /** A project's config, validated and normalized before Bud starts any listeners. */
36851
- var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(ConfigFields, (config) => ({
36853
+ var BudConfigSchema = delegate(check(delegate(check(check(delegate(check(delegate(ConfigFields, (config) => ({
36852
36854
  ...config,
36853
36855
  routes: parseConfig(BudRoutesSchema, config.routes)
36854
36856
  })), ({ port: companion, target }) => port(companion ?? 0) && port(target?.port) ? void 0 : "Ports must be integers between 0 and 65535."), ({ target, ...config }) => {
@@ -36857,7 +36859,7 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
36857
36859
  ...config,
36858
36860
  targetOrigin: origin ? normalizeOrigin(origin, "target.origin must be an HTTP(S) origin without a path or credentials.") : origin
36859
36861
  };
36860
- }), ({ comments }) => remote(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
36862
+ }), ({ locale }) => locale === void 0 || locale === "en" || locale === "de" ? void 0 : "locale must be \"en\" or \"de\"."), ({ comments }) => remote(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
36861
36863
  let flows = parseConfig(BudFlowsSchema, config.flows);
36862
36864
  let steps = flows.flatMap((flow) => flow.steps.map((step) => flowStepRoute(flow, step)));
36863
36865
  let home = config.routes.findIndex((route) => route.path === "/") + 1;
@@ -36884,6 +36886,7 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
36884
36886
  files: parseConfig(BudFilesSchema, config.files),
36885
36887
  logo: parseConfig(BudLogoSchema, config.logo),
36886
36888
  theme: parseConfig(BudThemeSchema, config.theme),
36889
+ locale: config.locale === void 0 ? "en" : config.locale,
36887
36890
  picker: parseConfig(BudPickerSchema, config.picker),
36888
36891
  branches: parseConfig(BudBranchesSchema, config.branches)
36889
36892
  }));
@@ -38763,10 +38766,10 @@ function ProjectLiveStatus(handle) {
38763
38766
  //#endregion
38764
38767
  //#region app/client/screens.ts
38765
38768
  var builtIns = {
38766
- "bud:pages": () => import("./assets/pages-DFs7jB6M.js"),
38767
- "bud:canvas": () => import("./assets/canvas-C69-96GR.js"),
38768
- "bud:tasks": () => import("./assets/tasks-mzmNhe2d.js"),
38769
- "bud:files": () => import("./assets/files-DQL5hzs3.js")
38769
+ "bud:pages": () => import("./assets/pages-DJiOvT-l.js"),
38770
+ "bud:canvas": () => import("./assets/canvas-gWj_ocKK.js"),
38771
+ "bud:tasks": () => import("./assets/tasks-C7aGLzBw.js"),
38772
+ "bud:files": () => import("./assets/files-CAPvbavY.js")
38770
38773
  };
38771
38774
  /**
38772
38775
  * Imports a screen module: a built-in by its key, any other by the URL the server gave it.
@@ -38852,7 +38855,7 @@ function ScreenHost(handle) {
38852
38855
  } catch (cause) {
38853
38856
  if (handle.signal.aborted) return;
38854
38857
  console.error(cause);
38855
- error = cause instanceof Error && cause.message ? cause.message : `Could not open ${label}.`;
38858
+ error = cause instanceof Error && cause.message ? cause.message : t("app.client.screenHost.couldNotOpen", { label });
38856
38859
  }
38857
38860
  handle.update();
38858
38861
  }
@@ -38864,7 +38867,7 @@ function ScreenHost(handle) {
38864
38867
  let context = opened?.context;
38865
38868
  let routeError = "";
38866
38869
  if (opened && context) try {
38867
- if (location.routes && !matchScreenRoutes(location.routes, location.href)) throw new Error(`No screen route matches ${location.href}.`);
38870
+ if (location.routes && !matchScreenRoutes(location.routes, location.href)) throw new Error(t("app.client.screenHost.noRouteMatches", { href: location.href }));
38868
38871
  context = matchScreenContext(opened.screen.routes, {
38869
38872
  ...context,
38870
38873
  active: handle.props.active,
@@ -38872,7 +38875,7 @@ function ScreenHost(handle) {
38872
38875
  url: new URL(location.href, context.url.origin)
38873
38876
  });
38874
38877
  } catch (cause) {
38875
- routeError = cause instanceof Error ? cause.message : "This screen route is invalid.";
38878
+ routeError = cause instanceof Error ? cause.message : t("app.client.screenHost.invalidRoute");
38876
38879
  }
38877
38880
  return /* @__PURE__ */ jsx("div", {
38878
38881
  class: "h-full min-h-0",
@@ -38889,7 +38892,7 @@ function ScreenHost(handle) {
38889
38892
  class: "max-w-2xl",
38890
38893
  role: "alert",
38891
38894
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
38892
- title: `${label} didn't open`,
38895
+ title: t("app.client.screenHost.didntOpen", { label }),
38893
38896
  subtitle: error
38894
38897
  }), /* @__PURE__ */ jsx(FieldCardContent, {
38895
38898
  class: "flex-col items-start gap-3",
@@ -38897,17 +38900,17 @@ function ScreenHost(handle) {
38897
38900
  as: "p",
38898
38901
  variant: "ui",
38899
38902
  tone: "muted",
38900
- children: "Fix the screen and retry. Details are in the browser console."
38903
+ children: t("app.client.screenHost.fixAndRetry")
38901
38904
  }), /* @__PURE__ */ jsx(Button, {
38902
38905
  mix: on("click", () => {
38903
38906
  if ("error" in module || handle.props.taskDesign && "error" in handle.props.taskDesign) window.location.reload();
38904
38907
  else open();
38905
38908
  }),
38906
- children: "Retry"
38909
+ children: t("app.client.screenHost.retry")
38907
38910
  })]
38908
38911
  })]
38909
38912
  })
38910
- }) : /* @__PURE__ */ jsx(Loader, { label: `Loading ${label}…` })
38913
+ }) : /* @__PURE__ */ jsx(Loader, { label: t("app.client.screenHost.loading", { label }) })
38911
38914
  });
38912
38915
  };
38913
38916
  }
@@ -38926,11 +38929,11 @@ async function resolveConversation(id, targets, token, signal, fetcher = fetch)
38926
38929
  id = parseThreadId(id);
38927
38930
  let thread = await requestComments(routes.comments.get.href({ id }), token, signal, parseThread, fetcher);
38928
38931
  signal.throwIfAborted();
38929
- if (thread.id !== id) throw new Error("The comments service returned another discussion.");
38932
+ if (thread.id !== id) throw new Error(t("app.client.conversationRoute.anotherDiscussion"));
38930
38933
  let destination = conversationDestination(thread);
38931
38934
  let target = targets.find((target) => target.module === destination.module);
38932
- if (!target) throw new Error("Enable the screen that owns this discussion to open it.");
38933
- if (target.routes && !matchScreenRoutes(target.routes, destination.href)) throw new Error("The discussion location is not available in this screen.");
38935
+ if (!target) throw new Error(t("app.client.conversationRoute.enableOwningScreen"));
38936
+ if (target.routes && !matchScreenRoutes(target.routes, destination.href)) throw new Error(t("app.client.conversationRoute.locationUnavailable"));
38934
38937
  return {
38935
38938
  active: target.id,
38936
38939
  module: { url: target.module },
@@ -39005,14 +39008,14 @@ function Workspace(handle) {
39005
39008
  changed();
39006
39009
  try {
39007
39010
  let project = handle.props.project;
39008
- if (!project) throw new Error("Connect a project to open this discussion.");
39011
+ if (!project) throw new Error(t("app.client.workspace.connectProjectToOpenDiscussion"));
39009
39012
  let next = await resolveConversation(id, handle.props.conversationTargets ?? [], project.token, signal);
39010
39013
  if (!signal.aborted && conversation === id) {
39011
39014
  presented = next;
39012
39015
  hrefs.set(next.active, window.location.pathname);
39013
39016
  }
39014
39017
  } catch (cause) {
39015
- if (!signal.aborted) error = cause instanceof Error ? cause.message : "Could not open this discussion.";
39018
+ if (!signal.aborted) error = cause instanceof Error ? cause.message : t("app.client.workspace.couldNotOpenDiscussion");
39016
39019
  } finally {
39017
39020
  if (request === controller && !signal.aborted) {
39018
39021
  loading = false;
@@ -39055,7 +39058,7 @@ function Workspace(handle) {
39055
39058
  let { pathname } = new URL(href, "http://bud.local");
39056
39059
  if (readConversationLocation(href)) return {
39057
39060
  href,
39058
- title: "Discussion",
39061
+ title: t("app.client.workspace.discussion"),
39059
39062
  icon: "message"
39060
39063
  };
39061
39064
  let match = handle.props.screens.map((item) => ({
@@ -39217,19 +39220,19 @@ function Workspace(handle) {
39217
39220
  children: project ? /* @__PURE__ */ jsx("div", {
39218
39221
  class: "h-full min-h-0",
39219
39222
  mix: mount,
39220
- children: [loading ? /* @__PURE__ */ jsx(Loader, { label: "Opening discussion…" }) : error ? /* @__PURE__ */ jsx("div", {
39223
+ children: [loading ? /* @__PURE__ */ jsx(Loader, { label: t("app.client.workspace.openingDiscussion") }) : error ? /* @__PURE__ */ jsx("div", {
39221
39224
  class: "p-3 md:p-4",
39222
39225
  children: /* @__PURE__ */ jsx(FieldCard, {
39223
39226
  class: "max-w-2xl",
39224
39227
  role: "alert",
39225
39228
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
39226
- title: "Discussion didn't open",
39229
+ title: t("app.client.workspace.discussionDidntOpen"),
39227
39230
  subtitle: error
39228
39231
  }), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Button, {
39229
39232
  mix: on("click", () => {
39230
39233
  if (conversation) resolve(conversation);
39231
39234
  }),
39232
- children: "Retry"
39235
+ children: t("app.client.workspace.retry")
39233
39236
  }) })]
39234
39237
  })
39235
39238
  }) : null, [...opened].map(([id, { module, taskDesign, location }]) => /* @__PURE__ */ jsx("div", {
@@ -39247,12 +39250,12 @@ function Workspace(handle) {
39247
39250
  }) : /* @__PURE__ */ jsx(FieldCard, {
39248
39251
  class: "max-w-2xl",
39249
39252
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
39250
- title: "Your project, side by side.",
39251
- subtitle: "Open Bud beside a local project to review its pages, files and tasks."
39253
+ title: t("app.client.workspace.yourProjectSideBySide"),
39254
+ subtitle: t("app.client.workspace.openBudBesideLocalProject")
39252
39255
  }), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Text, {
39253
39256
  as: "p",
39254
39257
  variant: "ui",
39255
- children: "Start your project with the Bud Vite plugin, or run Bud from its directory. Its screens appear here when the companion connects."
39258
+ children: t("app.client.workspace.startProjectWithBudVitePlugin")
39256
39259
  }) })]
39257
39260
  })
39258
39261
  });
@@ -39339,6 +39342,11 @@ var builtInRoutes = {
39339
39342
  "bud:tasks": taskRoutes,
39340
39343
  "bud:files": fileRoutes
39341
39344
  };
39345
+ function screenName(screen) {
39346
+ let builtIn = defaultScreens.find((candidate) => candidate.module === screen.module);
39347
+ let key = `screens.${builtIn?.id}`;
39348
+ return builtIn && screen.label === builtIn.label && hasMessage(key) ? t(key) : screen.label;
39349
+ }
39342
39350
  /**
39343
39351
  * Connects the route contract to application responses.
39344
39352
  * @param template - The development or production HTML shell.
@@ -39379,7 +39387,10 @@ function createAppController(template, project, runtime) {
39379
39387
  let module = await prepare(screen, request.signal);
39380
39388
  let taskDesign = project && (screen.module === "bud:tasks" || conversation !== void 0 && screens.some((target) => target.module === "bud:tasks")) && project.config.designs.tasks !== "bud:beads" && runtime ? await runtime.runPromise(ScreenModules.use((modules) => modules.module(taskDesignModuleId)), { signal: request.signal }) : void 0;
39381
39389
  let content = await renderToString(/* @__PURE__ */ jsx(WorkspacePage, {
39382
- screens: items,
39390
+ screens: items.map((item, index) => ({
39391
+ ...item,
39392
+ label: screenName(screens[index])
39393
+ })),
39383
39394
  active: screen.id,
39384
39395
  module,
39385
39396
  taskDesign,
@@ -39406,10 +39417,10 @@ function createAppController(template, project, runtime) {
39406
39417
  ...patterns ? { routes: patterns } : {}
39407
39418
  }
39408
39419
  }));
39409
- let title = (conversation ? "Discussion" : screen.label).replace(/[&<>]/g, (character) => `&#${character.charCodeAt(0)};`);
39420
+ let title = (conversation ? t("app.controller.discussion") : screenName(screen)).replace(/[&<>]/g, (character) => `&#${character.charCodeAt(0)};`);
39410
39421
  let logo = project?.config.logo;
39411
39422
  let shell = logo ? withLogoIcons(template, logo, (theme) => routes.logo.href({ theme })) : template;
39412
- 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" } });
39423
+ 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" } });
39413
39424
  }
39414
39425
  return createController(routes, { actions: {
39415
39426
  conversation({ params, request }) {
@@ -42541,6 +42552,19 @@ function previewAnchors(root) {
42541
42552
  };
42542
42553
  }
42543
42554
  //#endregion
42555
+ //#region app/server/locale.ts
42556
+ var requestLocale = new AsyncLocalStorage();
42557
+ useLocaleSource(() => requestLocale.getStore());
42558
+ /**
42559
+ * Runs a request's handling in its language, so what it renders speaks it.
42560
+ * @param value - The request's locale; English when it isn't one of Bud's.
42561
+ * @param handle - The request's handling.
42562
+ * @returns What the handling returns.
42563
+ */
42564
+ function inLocale(value, handle) {
42565
+ return requestLocale.run(isLocale(value) ? value : "en", handle);
42566
+ }
42567
+ //#endregion
42544
42568
  //#region app/server/hosted/contract.ts
42545
42569
  /**
42546
42570
  * The screens a hosted Bud shows: Pages as captured pictures, and no Files, which need a
@@ -42696,7 +42720,10 @@ function createHostedApp$1(options) {
42696
42720
  return made;
42697
42721
  };
42698
42722
  return {
42699
- fetch: async (request, context) => routerFor(hostedAuthor(context?.author)).fetch(request),
42723
+ fetch: async (request, context) => {
42724
+ let router = routerFor(hostedAuthor(context?.author));
42725
+ return inLocale(context?.locale ?? config.locale, () => router.fetch(request));
42726
+ },
42700
42727
  dispose: () => runtime.dispose()
42701
42728
  };
42702
42729
  }