@bud.sh/app 0.2.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 +159 -31
  28. package/dist/node/design.js +6235 -139
  29. package/dist/node/index.js +99 -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 +1765 -1630
  40. package/dist/server/assets/{branch-previews-C_TVt_HQ.js → branch-previews-ClkoJ0lM.js} +116 -5
  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-BVbFsnmX.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-DUBjmaBw.js → server-CYvuOSBA.js} +1 -1
  51. package/dist/server/assets/{services-Og5rcF50.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-BzzRLcL-.d.ts → config-DIC2cf_S.d.ts} +27 -1
  59. package/dist/types/assets/{contract-jA5pqK6h.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 +7 -1
  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-sj6mWw9-.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-9KxFcfyV.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 { bi as parseWorkQuery, pn as t, vi as parseWorkItemRef, y as routes, zn as createMultiMatcher } from "./comment-data-DsgJWq4n.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 RESYNC, $t as Button, Ai as parseWorkSchema, An as sanitizeUrlAttribute, Bi as boolean$2, Bn as equal, Ci as parseWorkSyncResult, Cn as resolveMixDescriptors, Ct as legacyConversationHref, Di as parseMessage, Dn as SELF_CLOSING_TAGS, Dt as taskHref, Ei as valueByRole, En as FRAMEWORK_PROPS, Et as selectedTask, Fi as parseWork, Fn as createComponent, G as mergeThreads, Gn as PREVIEW_TILE_HEIGHT, Gt as fileRoutes, Hi as defaulted, Hn as RoutePattern, Ii as record$1, In as createFrameHandle, J as conversationId, Ji as optional$3, Jn as parsePreviewCapture, K as stableJson, Ki as number$2, Kt as pageRoutes, Li as strict$2, Ln as jsx, Mi as WorkValidationError, Mn as shouldStringifyBooleanAttribute, Ni as identifier$3, Nn as Fragment, Oi as parseMessageAuthor, On as isAllowedHostPropName, Pi as nonempty$1, Pn as Frame, Q as text$6, Qn as parsePreviewViewport, Ri as any, Rn as Route, S as writeRows, Si as parseWorkSync, Sn as composeMixedProps, Ti as parseWorkValue, Tn as isEntry, Tt as readConversationLocation, Ui as enum_, Un as MAX_PREVIEW_HEIGHT, V as parseTaskDesign, Vi as createSchema, Vn as joinPatterns, W as CommentMergeError, Wi as fail$11, Wn as PREVIEW_THUMBNAIL_WIDTHS, X as parseCommentThread, Xi as parseSafe, Xn as parsePreviewId, Y as object$2, Yi as parse, Yn as parsePreviewHash, Z as revision$2, Zi as string$2, Zn as parsePreviewPins, Zt as Loader, _ as requestComments, _i as parseWorkItem, _n as ref$1, an as LiveStatus, ar as parseThreadCapabilities, at as FieldCardHeader, b as plainRow, bi as parseWorkQuery, bn as on, ci as MAX_CANVAS_Y, cr as parseThreadInput, ct as initialShellTabs, d as saveWorkspace, di as parseSectionData, dn as isLocale, dr as parseThreadSnapshot, dt as readTabsCookie, er as parseLiveId, fi as parseWorkActionInput, fn as locale, fr as parseThreadStatus, ft as stepShellTab, gi as parseWorkDetail, gt as visitShellTab, hi as parseWorkCreate, hn as Text, ht as tabsCookieValue, ir as parseThread, it as FieldCardContent, ji as statusCategory, jn as serializeStyleObject, jt as legacyFileHref, ki as fieldByRole, kn as normalizeAttributeName, l as projectStore, li as parseElementAnchor, ln as readPreference, lr as parseThreadLinkInput, lt as openShellTab, mi as parseWorkConnection, mn as useLocaleSource, mt as tabsCookieName, n as unavailableComments, nn as ShellTabBar, nr as MAX_THREADS, or as parseThreadFilter, ot as activeTab, pi as parseWorkActivity, pn as t, pr as parseThreadStatusInput, pt as tabHref, q as parseCommentInput$1, qi as object$1, qn as parsePreviewBranch, qt as taskRoutes, r as followThreads, rr as parseAnchor, rt as FieldCard, sn as AppShell, sr as parseThreadId, st as closeShellTab, t as commentsUnavailable, tr as parseWorkChange, ui as parseWorkWorkspace, un as writePreference, ur as parseThreadReplyInput, ut as parseShellTabs, v as projectLive, vi as parseWorkItemRef, vn as appendFlushMarker, wi as parseWorkUpdate, wn as clientEntry, xi as parseWorkSnapshot, xn as normalizeUnsafeHTMLProps, y as routes, yi as parseWorkMutationResult, yn as stripFlushMarkers, yt as conversationDestination, zi as array$2, zn as createMultiMatcher } from "./assets/comment-data-DsgJWq4n.js";
2
+ import { n as requestTasks } from "./assets/tasks-request-C89fN1dK.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
@@ -7493,7 +7494,7 @@ var logWithLevel$1 = (level) => (...message) => {
7493
7494
  return void_$2;
7494
7495
  });
7495
7496
  };
7496
- var colors = {
7497
+ var colors$1 = {
7497
7498
  bold: "1",
7498
7499
  red: "31",
7499
7500
  green: "32",
@@ -7505,7 +7506,7 @@ var colors = {
7505
7506
  black: "30",
7506
7507
  bgBrightRed: "101"
7507
7508
  };
7508
- colors.gray, colors.blue, colors.green, colors.yellow, colors.red, colors.bgBrightRed, colors.black;
7509
+ colors$1.gray, colors$1.blue, colors$1.green, colors$1.yellow, colors$1.red, colors$1.bgBrightRed, colors$1.black;
7509
7510
  var defaultDateFormat = (date) => `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}.${date.getMilliseconds().toString().padStart(3, "0")}`;
7510
7511
  /** @internal */
7511
7512
  var defaultLogger = /*#__PURE__*/ loggerMake(({ cause, date, fiber, logLevel, message }) => {
@@ -36360,6 +36361,113 @@ var BudLogoSchema = createSchema((value, context) => {
36360
36361
  return fail$11(logoMessage, context.path);
36361
36362
  });
36362
36363
  //#endregion
36364
+ //#region ../project/src/lib/theme.ts
36365
+ /**
36366
+ * Bud's colors and shadows a theme may set, as `--bud-<name>` in its styles. Everything else
36367
+ * Bud derives from them.
36368
+ */
36369
+ var THEME_TOKENS = Object.freeze([
36370
+ "background",
36371
+ "shell",
36372
+ "main",
36373
+ "canvas",
36374
+ "panel",
36375
+ "board",
36376
+ "editor",
36377
+ "editor-tabs",
36378
+ "foreground",
36379
+ "muted",
36380
+ "primary",
36381
+ "primary-hover",
36382
+ "primary-foreground",
36383
+ "border",
36384
+ "tab-divider",
36385
+ "hover",
36386
+ "selected",
36387
+ "control",
36388
+ "control-active",
36389
+ "control-selected",
36390
+ "card",
36391
+ "menu",
36392
+ "menu-hover",
36393
+ "card-frame",
36394
+ "card-surface",
36395
+ "card-border",
36396
+ "card-frame-border",
36397
+ "card-badge",
36398
+ "overlay-shadow",
36399
+ "tooltip",
36400
+ "board-control",
36401
+ "board-control-hover",
36402
+ "board-card",
36403
+ "control-elevation",
36404
+ "board-elevation",
36405
+ "board-card-elevation",
36406
+ "card-elevation",
36407
+ "board-card-lift",
36408
+ "frame-control",
36409
+ "modal-elevation",
36410
+ "segment-elevation",
36411
+ "control-open-elevation",
36412
+ "board-segment",
36413
+ "scrollbar",
36414
+ "focus",
36415
+ "scrollbar-hover",
36416
+ "tooltip-border",
36417
+ "tooltip-shadow"
36418
+ ]);
36419
+ var themeMessage = `theme must be { light, dark }, each mapping Bud's tokens (${THEME_TOKENS.slice(0, 3).join(", ")}, …) to CSS colors or shadows.`;
36420
+ function cssValue(value) {
36421
+ if (typeof value !== "string") return null;
36422
+ let trimmed = value.trim();
36423
+ if (!trimmed || trimmed.length > 300) return null;
36424
+ if (!/^[#a-zA-Z0-9(),.%\s/+-]+$/.test(trimmed) || /url\s*\(/i.test(trimmed)) return null;
36425
+ return trimmed;
36426
+ }
36427
+ function colors(value) {
36428
+ if (value === void 0) return {};
36429
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
36430
+ let result = {};
36431
+ for (let [name, raw] of Object.entries(value)) {
36432
+ let checked = cssValue(raw);
36433
+ if (!THEME_TOKENS.includes(name) || checked === null) return null;
36434
+ result[name] = checked;
36435
+ }
36436
+ return Object.freeze(result);
36437
+ }
36438
+ /** A theme in its one resolved form; Bud's own colors when absent. */
36439
+ var BudThemeSchema = createSchema((value, context) => {
36440
+ if (value === void 0) return { value: {
36441
+ light: {},
36442
+ dark: {}
36443
+ } };
36444
+ if (!value || typeof value !== "object" || Array.isArray(value)) return fail$11(themeMessage, context.path);
36445
+ let { light, dark, ...rest } = value;
36446
+ let resolved = {
36447
+ light: colors(light),
36448
+ dark: colors(dark)
36449
+ };
36450
+ if (Object.keys(rest).length || !resolved.light || !resolved.dark) return fail$11(themeMessage, context.path);
36451
+ return { value: {
36452
+ light: resolved.light,
36453
+ dark: resolved.dark
36454
+ } };
36455
+ });
36456
+ /**
36457
+ * The theme as styles that follow Bud's own, so its tokens win in each color scheme.
36458
+ * @param theme - The resolved theme.
36459
+ * @returns A `<style>` element, or an empty string for Bud's own colors.
36460
+ */
36461
+ function themeStyle(theme) {
36462
+ let block = (selector, values) => {
36463
+ let entries = Object.entries(values);
36464
+ if (!entries.length) return "";
36465
+ return `${selector}{${entries.map(([name, value]) => `--bud-${name}:${value};`).join("")}}`;
36466
+ };
36467
+ let css = block(":root:not([data-theme='dark'])", theme.light) + block(":root[data-theme='dark']", theme.dark);
36468
+ return css ? `<style data-bud-theme>${css}</style>` : "";
36469
+ }
36470
+ //#endregion
36363
36471
  //#region ../project/src/lib/branches.ts
36364
36472
  var message = "branches must be { main?, build?: { install?, build?, serve? } } with a branch name and nonempty shell commands.";
36365
36473
  function branch(value) {
@@ -36700,6 +36808,8 @@ var ConfigFields = check(section("Bud config must export an object.", {
36700
36808
  previews: any(),
36701
36809
  files: any(),
36702
36810
  logo: any(),
36811
+ theme: any(),
36812
+ locale: any(),
36703
36813
  picker: any(),
36704
36814
  branches: any(),
36705
36815
  flows: any()
@@ -36740,7 +36850,7 @@ var BudRoutesSchema = check(list(RouteSchema, "bud.config must define at least o
36740
36850
  var port = (value) => value === void 0 || Number.isInteger(value) && value >= 0 && value <= 65535;
36741
36851
  var remote = (value) => typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value);
36742
36852
  /** A project's config, validated and normalized before Bud starts any listeners. */
36743
- var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(ConfigFields, (config) => ({
36853
+ var BudConfigSchema = delegate(check(delegate(check(check(delegate(check(delegate(ConfigFields, (config) => ({
36744
36854
  ...config,
36745
36855
  routes: parseConfig(BudRoutesSchema, config.routes)
36746
36856
  })), ({ port: companion, target }) => port(companion ?? 0) && port(target?.port) ? void 0 : "Ports must be integers between 0 and 65535."), ({ target, ...config }) => {
@@ -36749,7 +36859,7 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
36749
36859
  ...config,
36750
36860
  targetOrigin: origin ? normalizeOrigin(origin, "target.origin must be an HTTP(S) origin without a path or credentials.") : origin
36751
36861
  };
36752
- }), ({ 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) => {
36753
36863
  let flows = parseConfig(BudFlowsSchema, config.flows);
36754
36864
  let steps = flows.flatMap((flow) => flow.steps.map((step) => flowStepRoute(flow, step)));
36755
36865
  let home = config.routes.findIndex((route) => route.path === "/") + 1;
@@ -36775,6 +36885,8 @@ var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(Conf
36775
36885
  previews: parseConfig(BudPreviewsSchema, config.previews),
36776
36886
  files: parseConfig(BudFilesSchema, config.files),
36777
36887
  logo: parseConfig(BudLogoSchema, config.logo),
36888
+ theme: parseConfig(BudThemeSchema, config.theme),
36889
+ locale: config.locale === void 0 ? "en" : config.locale,
36778
36890
  picker: parseConfig(BudPickerSchema, config.picker),
36779
36891
  branches: parseConfig(BudBranchesSchema, config.branches)
36780
36892
  }));
@@ -38654,10 +38766,10 @@ function ProjectLiveStatus(handle) {
38654
38766
  //#endregion
38655
38767
  //#region app/client/screens.ts
38656
38768
  var builtIns = {
38657
- "bud:pages": () => import("./assets/pages-DFs7jB6M.js"),
38658
- "bud:canvas": () => import("./assets/canvas-C69-96GR.js"),
38659
- "bud:tasks": () => import("./assets/tasks-mzmNhe2d.js"),
38660
- "bud:files": () => import("./assets/files-DQL5hzs3.js")
38769
+ "bud:pages": () => import("./assets/pages-BAmRFVtU.js"),
38770
+ "bud:canvas": () => import("./assets/canvas-MWtOwaWm.js"),
38771
+ "bud:tasks": () => import("./assets/tasks-C8iWPrWg.js"),
38772
+ "bud:files": () => import("./assets/files-Wvo11G2A.js")
38661
38773
  };
38662
38774
  /**
38663
38775
  * Imports a screen module: a built-in by its key, any other by the URL the server gave it.
@@ -38743,7 +38855,7 @@ function ScreenHost(handle) {
38743
38855
  } catch (cause) {
38744
38856
  if (handle.signal.aborted) return;
38745
38857
  console.error(cause);
38746
- 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 });
38747
38859
  }
38748
38860
  handle.update();
38749
38861
  }
@@ -38755,7 +38867,7 @@ function ScreenHost(handle) {
38755
38867
  let context = opened?.context;
38756
38868
  let routeError = "";
38757
38869
  if (opened && context) try {
38758
- 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 }));
38759
38871
  context = matchScreenContext(opened.screen.routes, {
38760
38872
  ...context,
38761
38873
  active: handle.props.active,
@@ -38763,7 +38875,7 @@ function ScreenHost(handle) {
38763
38875
  url: new URL(location.href, context.url.origin)
38764
38876
  });
38765
38877
  } catch (cause) {
38766
- routeError = cause instanceof Error ? cause.message : "This screen route is invalid.";
38878
+ routeError = cause instanceof Error ? cause.message : t("app.client.screenHost.invalidRoute");
38767
38879
  }
38768
38880
  return /* @__PURE__ */ jsx("div", {
38769
38881
  class: "h-full min-h-0",
@@ -38780,7 +38892,7 @@ function ScreenHost(handle) {
38780
38892
  class: "max-w-2xl",
38781
38893
  role: "alert",
38782
38894
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
38783
- title: `${label} didn't open`,
38895
+ title: t("app.client.screenHost.didntOpen", { label }),
38784
38896
  subtitle: error
38785
38897
  }), /* @__PURE__ */ jsx(FieldCardContent, {
38786
38898
  class: "flex-col items-start gap-3",
@@ -38788,17 +38900,17 @@ function ScreenHost(handle) {
38788
38900
  as: "p",
38789
38901
  variant: "ui",
38790
38902
  tone: "muted",
38791
- children: "Fix the screen and retry. Details are in the browser console."
38903
+ children: t("app.client.screenHost.fixAndRetry")
38792
38904
  }), /* @__PURE__ */ jsx(Button, {
38793
38905
  mix: on("click", () => {
38794
38906
  if ("error" in module || handle.props.taskDesign && "error" in handle.props.taskDesign) window.location.reload();
38795
38907
  else open();
38796
38908
  }),
38797
- children: "Retry"
38909
+ children: t("app.client.screenHost.retry")
38798
38910
  })]
38799
38911
  })]
38800
38912
  })
38801
- }) : /* @__PURE__ */ jsx(Loader, { label: `Loading ${label}…` })
38913
+ }) : /* @__PURE__ */ jsx(Loader, { label: t("app.client.screenHost.loading", { label }) })
38802
38914
  });
38803
38915
  };
38804
38916
  }
@@ -38817,11 +38929,11 @@ async function resolveConversation(id, targets, token, signal, fetcher = fetch)
38817
38929
  id = parseThreadId(id);
38818
38930
  let thread = await requestComments(routes.comments.get.href({ id }), token, signal, parseThread, fetcher);
38819
38931
  signal.throwIfAborted();
38820
- 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"));
38821
38933
  let destination = conversationDestination(thread);
38822
38934
  let target = targets.find((target) => target.module === destination.module);
38823
- if (!target) throw new Error("Enable the screen that owns this discussion to open it.");
38824
- 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"));
38825
38937
  return {
38826
38938
  active: target.id,
38827
38939
  module: { url: target.module },
@@ -38896,14 +39008,14 @@ function Workspace(handle) {
38896
39008
  changed();
38897
39009
  try {
38898
39010
  let project = handle.props.project;
38899
- if (!project) throw new Error("Connect a project to open this discussion.");
39011
+ if (!project) throw new Error(t("app.client.workspace.connectProjectToOpenDiscussion"));
38900
39012
  let next = await resolveConversation(id, handle.props.conversationTargets ?? [], project.token, signal);
38901
39013
  if (!signal.aborted && conversation === id) {
38902
39014
  presented = next;
38903
39015
  hrefs.set(next.active, window.location.pathname);
38904
39016
  }
38905
39017
  } catch (cause) {
38906
- 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");
38907
39019
  } finally {
38908
39020
  if (request === controller && !signal.aborted) {
38909
39021
  loading = false;
@@ -38946,7 +39058,7 @@ function Workspace(handle) {
38946
39058
  let { pathname } = new URL(href, "http://bud.local");
38947
39059
  if (readConversationLocation(href)) return {
38948
39060
  href,
38949
- title: "Discussion",
39061
+ title: t("app.client.workspace.discussion"),
38950
39062
  icon: "message"
38951
39063
  };
38952
39064
  let match = handle.props.screens.map((item) => ({
@@ -39108,19 +39220,19 @@ function Workspace(handle) {
39108
39220
  children: project ? /* @__PURE__ */ jsx("div", {
39109
39221
  class: "h-full min-h-0",
39110
39222
  mix: mount,
39111
- 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", {
39112
39224
  class: "p-3 md:p-4",
39113
39225
  children: /* @__PURE__ */ jsx(FieldCard, {
39114
39226
  class: "max-w-2xl",
39115
39227
  role: "alert",
39116
39228
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
39117
- title: "Discussion didn't open",
39229
+ title: t("app.client.workspace.discussionDidntOpen"),
39118
39230
  subtitle: error
39119
39231
  }), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Button, {
39120
39232
  mix: on("click", () => {
39121
39233
  if (conversation) resolve(conversation);
39122
39234
  }),
39123
- children: "Retry"
39235
+ children: t("app.client.workspace.retry")
39124
39236
  }) })]
39125
39237
  })
39126
39238
  }) : null, [...opened].map(([id, { module, taskDesign, location }]) => /* @__PURE__ */ jsx("div", {
@@ -39138,12 +39250,12 @@ function Workspace(handle) {
39138
39250
  }) : /* @__PURE__ */ jsx(FieldCard, {
39139
39251
  class: "max-w-2xl",
39140
39252
  children: [/* @__PURE__ */ jsx(FieldCardHeader, {
39141
- title: "Your project, side by side.",
39142
- 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")
39143
39255
  }), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Text, {
39144
39256
  as: "p",
39145
39257
  variant: "ui",
39146
- 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")
39147
39259
  }) })]
39148
39260
  })
39149
39261
  });
@@ -39300,7 +39412,7 @@ function createAppController(template, project, runtime) {
39300
39412
  let title = (conversation ? "Discussion" : screen.label).replace(/[&<>]/g, (character) => `&#${character.charCodeAt(0)};`);
39301
39413
  let logo = project?.config.logo;
39302
39414
  let shell = logo ? withLogoIcons(template, logo, (theme) => routes.logo.href({ theme })) : template;
39303
- return new Response(shell.replace("<title>bud.sh</title>", () => `<title>${title} · bud.sh</title>`).replace("<!--app-html-->", () => content), { headers: { "Content-Type": "text/html; charset=utf-8" } });
39415
+ 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" } });
39304
39416
  }
39305
39417
  return createController(routes, { actions: {
39306
39418
  conversation({ params, request }) {
@@ -42432,6 +42544,19 @@ function previewAnchors(root) {
42432
42544
  };
42433
42545
  }
42434
42546
  //#endregion
42547
+ //#region app/server/locale.ts
42548
+ var requestLocale = new AsyncLocalStorage();
42549
+ useLocaleSource(() => requestLocale.getStore());
42550
+ /**
42551
+ * Runs a request's handling in its language, so what it renders speaks it.
42552
+ * @param value - The request's locale; English when it isn't one of Bud's.
42553
+ * @param handle - The request's handling.
42554
+ * @returns What the handling returns.
42555
+ */
42556
+ function inLocale(value, handle) {
42557
+ return requestLocale.run(isLocale(value) ? value : "en", handle);
42558
+ }
42559
+ //#endregion
42435
42560
  //#region app/server/hosted/contract.ts
42436
42561
  /**
42437
42562
  * The screens a hosted Bud shows: Pages as captured pictures, and no Files, which need a
@@ -42587,7 +42712,10 @@ function createHostedApp$1(options) {
42587
42712
  return made;
42588
42713
  };
42589
42714
  return {
42590
- fetch: async (request, context) => routerFor(hostedAuthor(context?.author)).fetch(request),
42715
+ fetch: async (request, context) => {
42716
+ let router = routerFor(hostedAuthor(context?.author));
42717
+ return inLocale(context?.locale ?? config.locale, () => router.fetch(request));
42718
+ },
42591
42719
  dispose: () => runtime.dispose()
42592
42720
  };
42593
42721
  }