@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,9 +1,9 @@
1
1
  import { i as __toESM } from "./rolldown-runtime-DktXgydw.js";
2
- import { $ as FileSearchPanel, $r as parseFileSearchResult, A as parseEditorPreferences, Ar as validateResolvedThemeName, At as defaultReviewView, Br as parseReviewActionResult, Bt as fileHref, Cr as isFilePlainText, D as ReviewActionDialog, Dr as areThemesEqual, E as FileConflictPanel, Er as hasResolvedThemes, F as FileWorkspace, Fr as resolveLanguage, Ft as reviewComparison, Gr as parseMutationResult, Gt as fileRoutes, Hr as parseFileSaveResult, Ht as readFileLocation, I as defaultFileDestination, Ir as areLanguagesAttached, It as reviewContextKey, Jr as parseReviewHistory, Kr as parseRecoveries, L as fileActionIntent, Lr as ResolvedLanguages, Lt as reviewHref, M as FileMutationDialog, Mr as themeResolver, Mt as parseReviewSession, N as FileReviewPanel, Nn as jsx, Nr as getThemes, Nt as readReviewLocation, O as FileEditorPanel, Or as getSharedHighlighter, P as codeThemes, Pr as getResolvedOrResolveLanguage, Pt as rememberReviewPosition, Qr as parseFileSearchQuery, R as mutationFileView, Rr as parseReviewActionPlan, Rt as closeFileTab, Sr as markWorkerErrorReported, St as isCodeConversation, Tr as areFileRenderOptionsEqual, Tt as readConversationLocation, Ur as maxFileSaveBytes, Ut as reconcileFileView, Vr as parseFileSaveRequest, Vt as parseFileView, Wr as parseMutationPlan, Wt as rememberFile, Xr as parseReviewSnapshot, Yr as parseReviewRepository, Zr as parseReviewSource, _r as areDiffTargetsEqual, br as WorkerPoolWorkerError, bt as conversationHref, cn as toast, dn as Text, dr as parseFileUpdate, ei as parseFileChange, f as storeDocument, fr as reviewConflictIdentity, gr as areFilesEqual, hr as areDiffRenderOptionsEqual, j as FileRecoveryDialog, jr as attachResolvedThemes, k as defaultEditorPreferences, kr as prepareThemeResolution, kt as defaultReviewPosition, l as projectStore, ln as readPreference, m as storedFiles, mr as renderDiffWithHighlighter, ni as parseFileIndex, p as storedDocument, pn as ref, pr as isDiffPlainText, qr as parseReviewDiff, ri as parseFilePath, s as applyFileUpdate, t as commentsUnavailable, ti as parseFileDocument, tt as ConversationSidebar, u as saveFiles, un as writePreference, ur as createCodeAnchor, v as projectLive, vr as WorkerPoolTaskCanceledError, vt as codeCommentCounts, wr as renderFileWithHighlighter, wt as locateCodeConversations, xr as isHandledWorkerPoolError, y as routes, yr as WorkerPoolTerminatedError, z as remainingMutationIntent, zr as parseReviewActionRecoveries, zt as defaultFileView } from "./comment-data-CiDuvXi2.js";
2
+ import { $ as FileSearchPanel, $r as parseReviewHistory, A as parseEditorPreferences, Ar as areFileRenderOptionsEqual, At as defaultReviewView, Br as resolveLanguage, Bt as fileHref, Cr as WorkerPoolTaskCanceledError, D as ReviewActionDialog, Dr as markWorkerErrorReported, E as FileConflictPanel, Er as isHandledWorkerPoolError, F as FileWorkspace, Fr as validateResolvedThemeName, Ft as reviewComparison, Gr as parseReviewActionResult, Gt as fileRoutes, Hr as ResolvedLanguages, Ht as readFileLocation, I as defaultFileDestination, Ir as attachResolvedThemes, It as reviewContextKey, Jr as maxFileSaveBytes, Kr as parseFileSaveRequest, L as fileActionIntent, Lr as themeResolver, Lt as reviewHref, M as FileMutationDialog, Mr as areThemesEqual, Mt as parseReviewSession, N as FileReviewPanel, Nr as getSharedHighlighter, Nt as readReviewLocation, O as FileEditorPanel, Or as isFilePlainText, P as codeThemes, Pr as prepareThemeResolution, Pt as rememberReviewPosition, Qr as parseReviewDiff, R as mutationFileView, Rn as jsx, Rr as getThemes, Rt as closeFileTab, Sr as areDiffTargetsEqual, St as isCodeConversation, Tr as WorkerPoolWorkerError, Tt as readConversationLocation, Ur as parseReviewActionPlan, Ut as reconcileFileView, Vr as areLanguagesAttached, Vt as parseFileView, Wr as parseReviewActionRecoveries, Wt as rememberFile, Xr as parseMutationResult, Yr as parseMutationPlan, Zr as parseRecoveries, _r as reviewConflictIdentity, ai as parseFileChange, br as areDiffRenderOptionsEqual, bt as conversationHref, ci as parseFilePath, cn as toast, ei as parseReviewRepository, f as storeDocument, gn as Text, gr as parseFileUpdate, hr as createCodeAnchor, ii as parseFileSearchResult, j as FileRecoveryDialog, jr as hasResolvedThemes, k as defaultEditorPreferences, kr as renderFileWithHighlighter, kt as defaultReviewPosition, l as projectStore, ln as readPreference, m as storedFiles, mn as t, ni as parseReviewSource, oi as parseFileDocument, p as storedDocument, qr as parseFileSaveResult, ri as parseFileSearchQuery, s as applyFileUpdate, si as parseFileIndex, ti as parseReviewSnapshot, tt as ConversationSidebar, u as saveFiles, un as writePreference, v as projectLive, vn as ref, vr as isDiffPlainText, vt as codeCommentCounts, wr as WorkerPoolTerminatedError, wt as locateCodeConversations, xr as areFilesEqual, y as routes, yr as renderDiffWithHighlighter, z as remainingMutationIntent, zr as getResolvedOrResolveLanguage, zt as defaultFileView } from "./comment-data-C-rNCB1n.js";
3
3
  import { B as DEFAULT_THEMES, C as getFiletypeFromFileName, S as getCustomExtensionsVersion, x as getCustomExtensionsMap } from "./cloneFileDiffMetadata-C1BcNpxN.js";
4
- import { n as createProjectComments, t as defineScreen } from "./screen-BroBb1Z_.js";
4
+ import { n as createProjectComments, t as defineScreen } from "./screen-D4D9nh4V.js";
5
5
  import { t as require_lru } from "./lru-C0_oUet4.js";
6
- import { t as ProjectThreadLinks } from "./project-thread-links-B1Y2bDUx.js";
6
+ import { t as ProjectThreadLinks } from "./project-thread-links-B7uzThL9.js";
7
7
  //#region ../../node_modules/.pnpm/@pierre+diffs@1.5.2_@shikijs+themes@4.5.0_react-dom@19.3.1_react@19.3.1__react@19.3.1/node_modules/@pierre/diffs/dist/highlighter/languages/getResolvedLanguages.js
8
8
  function getResolvedLanguages(languages) {
9
9
  const resolvedLanguages = [];
@@ -1037,7 +1037,7 @@ var FilesRequestError = class extends Error {
1037
1037
  async function requestFiles(url, token, signal, parse, fetcher = fetch, action) {
1038
1038
  let parsed = new URL(url, "http://bud.invalid");
1039
1039
  let mutation = parsed.pathname === "/api/files/mutations/prepare" || parsed.pathname === "/api/files/mutations/apply" || parsed.pathname === "/api/files/review-actions/prepare" || parsed.pathname === "/api/files/review-actions/apply" || parsed.pathname === "/api/files/save";
1040
- if (!url.startsWith("/api/files") || url.startsWith("//") || parsed.origin !== "http://bud.invalid" || parsed.hash || !endpoints.has(parsed.pathname) || (parsed.pathname === "/api/files/refresh" || parsed.pathname === "/api/files/save" || parsed.pathname.startsWith("/api/files/review-actions/") || parsed.pathname.startsWith("/api/files/mutations/")) && parsed.search || mutation !== Boolean(action)) throw new FilesRequestError("Invalid file endpoint.", "input");
1040
+ if (!url.startsWith("/api/files") || url.startsWith("//") || parsed.origin !== "http://bud.invalid" || parsed.hash || !endpoints.has(parsed.pathname) || (parsed.pathname === "/api/files/refresh" || parsed.pathname === "/api/files/save" || parsed.pathname.startsWith("/api/files/review-actions/") || parsed.pathname.startsWith("/api/files/mutations/")) && parsed.search || mutation !== Boolean(action)) throw new FilesRequestError(t("app.client.filesRequest.invalidEndpoint"), "input");
1041
1041
  signal.throwIfAborted();
1042
1042
  let response = await fetcher(url, {
1043
1043
  method: mutation || parsed.pathname === "/api/files/refresh" ? "POST" : "GET",
@@ -1052,7 +1052,7 @@ async function requestFiles(url, token, signal, parse, fetcher = fetch, action)
1052
1052
  signal
1053
1053
  });
1054
1054
  let reader = response.body?.getReader();
1055
- if (!reader) throw new FilesRequestError("The file service returned an empty response.", "response", response.status);
1055
+ if (!reader) throw new FilesRequestError(t("app.client.filesRequest.emptyResponse"), "response", response.status);
1056
1056
  let chunks = [];
1057
1057
  let size = 0;
1058
1058
  let cancel = () => {
@@ -1066,7 +1066,7 @@ async function requestFiles(url, token, signal, parse, fetcher = fetch, action)
1066
1066
  signal.throwIfAborted();
1067
1067
  if (part.done) break;
1068
1068
  size += part.value.byteLength;
1069
- if (size > 16777216) throw new FilesRequestError("This file response exceeds the display limit. Narrow the file view and try again.", "limit", response.status);
1069
+ if (size > 16777216) throw new FilesRequestError(t("app.client.filesRequest.responseExceedsDisplayLimit"), "limit", response.status);
1070
1070
  chunks.push(part.value);
1071
1071
  }
1072
1072
  } catch (error) {
@@ -1086,13 +1086,13 @@ async function requestFiles(url, token, signal, parse, fetcher = fetch, action)
1086
1086
  try {
1087
1087
  value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
1088
1088
  } catch {
1089
- throw new FilesRequestError("The file service returned invalid data. Refresh and try again.", "response", response.status);
1089
+ throw new FilesRequestError(t("app.client.filesRequest.invalidData"), "response", response.status);
1090
1090
  }
1091
- if (!response.ok) throw new FilesRequestError(value && typeof value === "object" && "error" in value && typeof value.error === "string" ? value.error.slice(0, 1e3) : "Could not load files. Try again.", value && typeof value === "object" && "code" in value && typeof value.code === "string" ? value.code.slice(0, 100) : "request", response.status);
1091
+ if (!response.ok) throw new FilesRequestError(value && typeof value === "object" && "error" in value && typeof value.error === "string" ? value.error.slice(0, 1e3) : t("app.client.filesRequest.couldNotLoadFiles"), value && typeof value === "object" && "code" in value && typeof value.code === "string" ? value.code.slice(0, 100) : "request", response.status);
1092
1092
  try {
1093
1093
  return parse(value);
1094
1094
  } catch {
1095
- throw new FilesRequestError("The file service returned unexpected data. Refresh and try again.", "response", response.status);
1095
+ throw new FilesRequestError(t("app.client.filesRequest.unexpectedData"), "response", response.status);
1096
1096
  }
1097
1097
  }
1098
1098
  //#endregion
@@ -1123,17 +1123,17 @@ function createFileMutationGuards() {
1123
1123
  };
1124
1124
  }
1125
1125
  function message$4(cause) {
1126
- return cause instanceof Error ? cause.message : "The file action could not be completed.";
1126
+ return cause instanceof Error ? cause.message : t("app.client.fileMutationController.actionCouldNotBeCompleted");
1127
1127
  }
1128
1128
  function assertPlan(intent, plan) {
1129
- if (intent.kind !== plan.kind) throw new Error("The service returned a different file action.");
1129
+ if (intent.kind !== plan.kind) throw new Error(t("app.client.fileMutationController.differentFileAction"));
1130
1130
  if (intent.kind === "restore") return;
1131
1131
  let expected = "entries" in intent ? intent.entries.map((item) => [item.path, item.destination]) : intent.kind === "delete" ? intent.paths.map((source) => [source, null]) : [[null, intent.path]];
1132
1132
  let actual = plan.items.map((item) => [item.source, item.destination]);
1133
- if (JSON.stringify(expected) !== JSON.stringify(actual)) throw new Error("The service returned different file paths. Refresh before continuing.");
1133
+ if (JSON.stringify(expected) !== JSON.stringify(actual)) throw new Error(t("app.client.fileMutationController.differentFilePaths"));
1134
1134
  }
1135
1135
  function assertResult(plan, result) {
1136
- if (result.id !== plan.id || result.items.length !== plan.items.length || result.items.some((item, index) => item.source !== plan.items[index].source || item.destination !== plan.items[index].destination)) throw new Error("The file result does not match its confirmed plan. Refresh and check Recovery before retrying.");
1136
+ if (result.id !== plan.id || result.items.length !== plan.items.length || result.items.some((item, index) => item.source !== plan.items[index].source || item.destination !== plan.items[index].destination)) throw new Error(t("app.client.fileMutationController.resultDoesNotMatchPlan"));
1137
1137
  }
1138
1138
  /**
1139
1139
  * Coordinates validated plans without hiding partial results or retrying writes.
@@ -1212,7 +1212,7 @@ function createFileMutationController(options) {
1212
1212
  ...state,
1213
1213
  phase: "editing",
1214
1214
  plan: null,
1215
- error: "This confirmation expired. Review the current files again."
1215
+ error: t("app.client.fileMutationController.confirmationExpired")
1216
1216
  };
1217
1217
  changed();
1218
1218
  return;
@@ -1246,7 +1246,7 @@ function createFileMutationController(options) {
1246
1246
  try {
1247
1247
  await options.guards.afterApply(current, result);
1248
1248
  } catch (cause) {
1249
- draftError = `Files changed, but retained draft metadata could not be updated. ${message$4(cause)}`;
1249
+ draftError = t("app.client.fileMutationController.draftMetadataCouldNotBeUpdated", { error: message$4(cause) });
1250
1250
  }
1251
1251
  await options.onApplied(current, result);
1252
1252
  if (turn !== sequence || !state) return;
@@ -1271,7 +1271,7 @@ function createFileMutationController(options) {
1271
1271
  await options.onUncertain().catch(() => {});
1272
1272
  if (state && turn === sequence) state = {
1273
1273
  ...state,
1274
- error: `${message$4(cause)} Refresh and check Recovery before retrying.`
1274
+ error: t("app.client.fileMutationController.refreshAndCheckRecovery", { error: message$4(cause) })
1275
1275
  };
1276
1276
  }
1277
1277
  changed();
@@ -1415,7 +1415,7 @@ function comparisonQuery(comparison) {
1415
1415
  * @returns Exact source snapshots plus explicit per-file failures.
1416
1416
  */
1417
1417
  async function readReviewPage(snapshot, paths, token, signal, fetcher = fetch) {
1418
- if (paths.length > 20 || paths.some((path) => !snapshot.entries.some((entry) => entry.path === path))) throw new FilesRequestError("The selected files are outside this comparison page.", "input");
1418
+ if (paths.length > 20 || paths.some((path) => !snapshot.entries.some((entry) => entry.path === path))) throw new FilesRequestError(t("app.client.projectFileReviewData.filesOutsidePage"), "input");
1419
1419
  let result = /* @__PURE__ */ new Map();
1420
1420
  let failures = {};
1421
1421
  let cursor = 0;
@@ -1433,7 +1433,7 @@ async function readReviewPage(snapshot, paths, token, signal, fetcher = fetch) {
1433
1433
  query.set("expectedRevision", snapshot.revision);
1434
1434
  try {
1435
1435
  let diff = await requestFiles(`${routes.fileReviewApi.diff.href()}?${query}`, token, lifetime, parseReviewDiff, fetcher);
1436
- if (diff.entry.path !== path || diff.metadataRevision !== snapshot.revision || JSON.stringify(diff.comparison) !== JSON.stringify(snapshot.comparison)) throw new FilesRequestError("The comparison changed while reading. Refresh before continuing.", "changed", 409);
1436
+ if (diff.entry.path !== path || diff.metadataRevision !== snapshot.revision || JSON.stringify(diff.comparison) !== JSON.stringify(snapshot.comparison)) throw new FilesRequestError(t("app.client.projectFileReviewData.comparisonChanged"), "changed", 409);
1437
1437
  let bytes = [
1438
1438
  diff.oldFile,
1439
1439
  diff.newFile,
@@ -1441,14 +1441,14 @@ async function readReviewPage(snapshot, paths, token, signal, fetcher = fetch) {
1441
1441
  diff.conflict?.ours,
1442
1442
  diff.conflict?.theirs
1443
1443
  ].reduce((total, file) => total + (file?.contents?.length ?? 0) * 2, 0);
1444
- if (retainedBytes + bytes > 33554432) failures[path] = "This page reached its memory limit. Open this file individually.";
1444
+ if (retainedBytes + bytes > 33554432) failures[path] = t("app.client.projectFileReviewData.memoryLimit");
1445
1445
  else {
1446
1446
  retainedBytes += bytes;
1447
1447
  result.set(path, diff);
1448
1448
  }
1449
1449
  } catch (cause) {
1450
1450
  if (lifetime.aborted || cause instanceof FilesRequestError && (cause.status === 409 || cause.status === 401 || cause.status === 403)) throw cause;
1451
- failures[path] = cause instanceof Error ? cause.message : "Could not read this comparison.";
1451
+ failures[path] = cause instanceof Error ? cause.message : t("app.client.projectFileReviewData.couldNotReadComparison");
1452
1452
  }
1453
1453
  }
1454
1454
  } catch (cause) {
@@ -1500,19 +1500,19 @@ function within(path, parent) {
1500
1500
  */
1501
1501
  function parseDrafts(value, projectId) {
1502
1502
  if (!value) return [];
1503
- if (value.length > maxStoredCharacters) throw new Error("The local draft record exceeds its limit.");
1503
+ if (value.length > maxStoredCharacters) throw new Error(t("app.client.fileDrafts.recordExceedsLimit"));
1504
1504
  let parsed = JSON.parse(value);
1505
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Invalid local draft record.");
1505
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(t("app.client.fileDrafts.invalidRecord"));
1506
1506
  let record = parsed;
1507
1507
  if (record.version !== 1 || record.projectId !== projectId || !Array.isArray(record.drafts) || record.drafts.length > maxDrafts || Object.keys(record).some((key) => ![
1508
1508
  "version",
1509
1509
  "projectId",
1510
1510
  "drafts"
1511
- ].includes(key))) throw new Error("Invalid local draft record.");
1511
+ ].includes(key))) throw new Error(t("app.client.fileDrafts.invalidRecord"));
1512
1512
  let seen = /* @__PURE__ */ new Set();
1513
1513
  let total = 0;
1514
1514
  return record.drafts.map((input) => {
1515
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("Invalid local draft.");
1515
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(t("app.client.fileDrafts.invalidDraft"));
1516
1516
  let item = input;
1517
1517
  if (Object.keys(item).some((key) => ![
1518
1518
  "id",
@@ -1521,7 +1521,7 @@ function parseDrafts(value, projectId) {
1521
1521
  "baseContents",
1522
1522
  "contents",
1523
1523
  "updatedAt"
1524
- ].includes(key)) || typeof item.id !== "string" || !/^[a-zA-Z0-9-]{1,80}$/.test(item.id) || !Number.isSafeInteger(item.updatedAt) || Number(item.updatedAt) < 0) throw new Error("Invalid local draft.");
1524
+ ].includes(key)) || typeof item.id !== "string" || !/^[a-zA-Z0-9-]{1,80}$/.test(item.id) || !Number.isSafeInteger(item.updatedAt) || Number(item.updatedAt) < 0) throw new Error(t("app.client.fileDrafts.invalidDraft"));
1525
1525
  parseFileSaveRequest({
1526
1526
  path: item.path,
1527
1527
  expectedRevision: item.baseRevision,
@@ -1533,10 +1533,10 @@ function parseDrafts(value, projectId) {
1533
1533
  contents: item.contents
1534
1534
  });
1535
1535
  let draft = item;
1536
- if (seen.has(draft.path)) throw new Error("Duplicate local draft path.");
1536
+ if (seen.has(draft.path)) throw new Error(t("app.client.fileDrafts.duplicateDraftPath"));
1537
1537
  seen.add(draft.path);
1538
1538
  total += bytes(draft);
1539
- if (total > maxDraftBytes) throw new Error("The local drafts exceed their memory limit.");
1539
+ if (total > maxDraftBytes) throw new Error(t("app.client.fileDrafts.draftsExceedMemoryLimit"));
1540
1540
  return { ...draft };
1541
1541
  });
1542
1542
  }
@@ -1554,7 +1554,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1554
1554
  try {
1555
1555
  for (let draft of parseDrafts(storage?.getItem(key) ?? null, projectId)) drafts.set(draft.path, draft);
1556
1556
  } catch {
1557
- warning = "Local drafts could not be read. The stored record has been left untouched.";
1557
+ warning = t("app.client.fileDrafts.readFailed");
1558
1558
  }
1559
1559
  let unreadable = Boolean(warning);
1560
1560
  function persist() {
@@ -1570,16 +1570,16 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1570
1570
  warning = "";
1571
1571
  return true;
1572
1572
  } catch {
1573
- warning = "This draft is kept in this tab only. Browser storage is unavailable or full; save or download it before closing the tab.";
1573
+ warning = t("app.client.fileDrafts.tabOnlyWarning");
1574
1574
  return false;
1575
1575
  }
1576
1576
  }
1577
1577
  function put(draft) {
1578
- if (encoder.encode(draft.contents).length > 2097152 || !draft.contents.isWellFormed() || draft.contents.includes("\0")) throw new Error("This draft exceeds the text editor limit or contains unsupported text. Undo the last edit before saving.");
1578
+ if (encoder.encode(draft.contents).length > 2097152 || !draft.contents.isWellFormed() || draft.contents.includes("\0")) throw new Error(t("app.client.fileDrafts.unsupportedDraftText"));
1579
1579
  let clean = [...drafts.values()].filter((item) => item.path !== draft.path && !draftDirty(item));
1580
1580
  for (let item of clean) drafts.delete(item.path);
1581
1581
  let others = [...drafts.values()].filter((item) => item.path !== draft.path);
1582
- if (others.length >= maxDrafts || others.reduce((sum, item) => sum + bytes(item), bytes(draft)) > maxDraftBytes) throw new Error("Save or discard another draft before editing more files. Your existing drafts have been kept.");
1582
+ if (others.length >= maxDrafts || others.reduce((sum, item) => sum + bytes(item), bytes(draft)) > maxDraftBytes) throw new Error(t("app.client.fileDrafts.draftLimitReached"));
1583
1583
  drafts.set(draft.path, draft);
1584
1584
  persist();
1585
1585
  return draft;
@@ -1613,7 +1613,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1613
1613
  },
1614
1614
  change(path, contents) {
1615
1615
  let draft = drafts.get(path);
1616
- if (!draft) throw new Error("This draft is no longer open.");
1616
+ if (!draft) throw new Error(t("app.client.fileDrafts.draftNoLongerOpen"));
1617
1617
  return put({
1618
1618
  ...draft,
1619
1619
  contents,
@@ -1632,7 +1632,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1632
1632
  },
1633
1633
  rebase(path, revision, contents) {
1634
1634
  let draft = drafts.get(path);
1635
- if (!draft) throw new Error("This draft is no longer open.");
1635
+ if (!draft) throw new Error(t("app.client.fileDrafts.draftNoLongerOpen"));
1636
1636
  return put({
1637
1637
  ...draft,
1638
1638
  id: crypto.randomUUID(),
@@ -1648,11 +1648,11 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1648
1648
  persist,
1649
1649
  beforeMutation(intent, plan) {
1650
1650
  let dirty = [...drafts.values()].filter(draftDirty);
1651
- if (intent.kind === "delete" && dirty.some((draft) => intent.paths.some((path) => within(draft.path, path)))) return "Save or discard the unsaved drafts in this selection before deleting it.";
1652
- if (intent.kind === "restore" && dirty.some((draft) => plan.items.some((item) => item.destination && within(draft.path, item.destination)))) return "Save or discard the unsaved drafts at these original paths before restoring them.";
1651
+ if (intent.kind === "delete" && dirty.some((draft) => intent.paths.some((path) => within(draft.path, path)))) return t("app.client.fileDrafts.deleteWithUnsavedDrafts");
1652
+ if (intent.kind === "restore" && dirty.some((draft) => plan.items.some((item) => item.destination && within(draft.path, item.destination)))) return t("app.client.fileDrafts.restoreWithUnsavedDrafts");
1653
1653
  if (intent.kind === "move") {
1654
- if (dirty.some((draft) => intent.entries.some((entry) => within(draft.path, entry.destination) && !within(draft.path, entry.path)))) return "A destination has an unsaved draft. Save or discard it before moving files here.";
1655
- if (dirty.some((draft) => intent.entries.some((entry) => within(draft.path, entry.path))) && !persist()) return "Save or download your drafts before moving these files; local draft storage is unavailable.";
1654
+ if (dirty.some((draft) => intent.entries.some((entry) => within(draft.path, entry.destination) && !within(draft.path, entry.path)))) return t("app.client.fileDrafts.destinationHasDraft");
1655
+ if (dirty.some((draft) => intent.entries.some((entry) => within(draft.path, entry.path))) && !persist()) return t("app.client.fileDrafts.storageUnavailableForMove");
1656
1656
  }
1657
1657
  return null;
1658
1658
  },
@@ -1673,7 +1673,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1673
1673
  }
1674
1674
  for (let { draft, path } of moved) {
1675
1675
  let destination = next.get(path);
1676
- if (destination && draftDirty(destination)) throw new Error("The destination acquired an unsaved draft while the move was running. Both drafts remain available at their original paths; download or review them before continuing.");
1676
+ if (destination && draftDirty(destination)) throw new Error(t("app.client.fileDrafts.destinationDraftConflict"));
1677
1677
  next.set(path, {
1678
1678
  ...draft,
1679
1679
  id: crypto.randomUUID(),
@@ -1708,7 +1708,7 @@ function projectFileDrafts(projectId, storage) {
1708
1708
  let existing = openProjects.get(projectId);
1709
1709
  if (existing) return existing;
1710
1710
  for (let [id, drafts] of openProjects) if (!drafts.dirty.length) openProjects.delete(id);
1711
- if (openProjects.size >= 4) throw new Error("Save or discard drafts in another project before opening more editors.");
1711
+ if (openProjects.size >= 4) throw new Error(t("app.client.fileDrafts.projectDraftLimitReached"));
1712
1712
  let drafts = createFileDrafts(projectId, storage);
1713
1713
  openProjects.set(projectId, drafts);
1714
1714
  return drafts;
@@ -1716,11 +1716,11 @@ function projectFileDrafts(projectId, storage) {
1716
1716
  //#endregion
1717
1717
  //#region app/client/file-editor-controller.ts
1718
1718
  function message$3(cause) {
1719
- return cause instanceof Error ? cause.message : "The file could not be saved. Your draft has been kept.";
1719
+ return cause instanceof Error ? cause.message : t("app.client.fileEditorController.saveFailed");
1720
1720
  }
1721
1721
  function readable(document, path) {
1722
- if (document.entry.path !== path) throw new Error("The source response belongs to another file.");
1723
- if (document.contents === null || !document.revision) throw new Error("Only regular UTF-8 text files within the editor size limit can be edited.");
1722
+ if (document.entry.path !== path) throw new Error(t("app.client.fileEditorController.wrongFileResponse"));
1723
+ if (document.contents === null || !document.revision) throw new Error(t("app.client.fileEditorController.unsupportedFile"));
1724
1724
  return {
1725
1725
  revision: document.revision,
1726
1726
  contents: document.contents
@@ -1764,7 +1764,7 @@ function createFileEditorController(options) {
1764
1764
  state = {
1765
1765
  ...state,
1766
1766
  conflict: true,
1767
- error: "This file changed outside the editor. Your draft has been kept. Compare with the latest file before saving."
1767
+ error: t("app.client.fileEditorController.changedOutsideEditor")
1768
1768
  };
1769
1769
  changed();
1770
1770
  }
@@ -1798,12 +1798,12 @@ function createFileEditorController(options) {
1798
1798
  contents: draft.baseContents
1799
1799
  },
1800
1800
  mode,
1801
- baselineLabel: useIndex ? "Index" : "Opened version",
1801
+ baselineLabel: useIndex ? t("app.client.fileEditorController.index") : t("app.client.fileEditorController.openedVersion"),
1802
1802
  saving: saves.has(path),
1803
1803
  conflict,
1804
1804
  latest: null,
1805
- error: conflict ? "This recovered draft belongs to an older file version. Compare it with the latest file before saving." : "",
1806
- status: draftDirty(draft) ? "Recovered unsaved draft." : ""
1805
+ error: conflict ? t("app.client.fileEditorController.recoveredDraftOldVersion") : "",
1806
+ status: draftDirty(draft) ? t("app.client.fileEditorController.recoveredUnsavedDraft") : ""
1807
1807
  };
1808
1808
  } catch (cause) {
1809
1809
  if (!signal.aborted) {
@@ -1820,12 +1820,12 @@ function createFileEditorController(options) {
1820
1820
  contents: retained.baseContents
1821
1821
  },
1822
1822
  mode: "file",
1823
- baselineLabel: "Opened version",
1823
+ baselineLabel: t("app.client.fileEditorController.openedVersion"),
1824
1824
  saving: saves.has(path),
1825
1825
  conflict: true,
1826
1826
  latest: null,
1827
- error: `The current project file could not be read. ${message$3(cause)}`,
1828
- status: "Your unsaved draft is available below. Download it, or restore the project file and compare with its latest contents before saving."
1827
+ error: t("app.client.fileEditorController.projectFileUnreadable", { detail: message$3(cause) }),
1828
+ status: t("app.client.fileEditorController.unsavedDraftAvailable")
1829
1829
  };
1830
1830
  else openError = message$3(cause);
1831
1831
  }
@@ -1871,7 +1871,7 @@ function createFileEditorController(options) {
1871
1871
  expectedRevision: draft.baseRevision,
1872
1872
  contents: draft.contents
1873
1873
  }, options.signal);
1874
- if (result.path !== draft.path || result.previousRevision !== draft.baseRevision) throw new Error("The save response belongs to a different file revision. Check the latest file before retrying.");
1874
+ if (result.path !== draft.path || result.previousRevision !== draft.baseRevision) throw new Error(t("app.client.fileEditorController.saveRevisionMismatch"));
1875
1875
  let next = options.drafts.acknowledge(draft.path, draft.contents, result.revision);
1876
1876
  if (state?.draft.path === draft.path && next) {
1877
1877
  state = {
@@ -1880,9 +1880,9 @@ function createFileEditorController(options) {
1880
1880
  status: "",
1881
1881
  error: ""
1882
1882
  };
1883
- if (result.refresh === "failed") toast.error("Saved, but the file browser could not refresh", { description: "Refresh it before continuing." });
1884
- else if (draftDirty(next)) toast.success("Saved to your project", { description: "Newer edits are still unsaved." });
1885
- else toast.success("Saved to your project");
1883
+ if (result.refresh === "failed") toast.error(t("app.client.fileEditorController.savedRefreshFailed"), { description: t("app.client.fileEditorController.refreshBeforeContinuing") });
1884
+ else if (draftDirty(next)) toast.success(t("app.client.fileEditorController.savedToProject"), { description: t("app.client.fileEditorController.newerEditsUnsaved") });
1885
+ else toast.success(t("app.client.fileEditorController.savedToProject"));
1886
1886
  }
1887
1887
  options.onSaved();
1888
1888
  } catch (cause) {
@@ -1926,11 +1926,11 @@ function createFileEditorController(options) {
1926
1926
  contents: current.contents
1927
1927
  },
1928
1928
  mode: "diff",
1929
- baselineLabel: "Latest file",
1929
+ baselineLabel: t("app.client.fileEditorController.latestFile"),
1930
1930
  latest: document,
1931
1931
  conflict: true,
1932
1932
  error: "",
1933
- status: "Your draft is on the new side. No file is changed until you review it and save."
1933
+ status: t("app.client.fileEditorController.draftOnNewSide")
1934
1934
  };
1935
1935
  } catch (cause) {
1936
1936
  if (!signal.aborted && state) state = {
@@ -1951,7 +1951,7 @@ function createFileEditorController(options) {
1951
1951
  latest: null,
1952
1952
  conflict: false,
1953
1953
  error: "",
1954
- status: "Reviewed against the latest file. Save when your merged draft is ready."
1954
+ status: t("app.client.fileEditorController.reviewedAgainstLatest")
1955
1955
  };
1956
1956
  changed();
1957
1957
  }
@@ -1984,7 +1984,7 @@ function createFileEditorController(options) {
1984
1984
  guard: {
1985
1985
  async beforeApply(intent, plan, signal) {
1986
1986
  signal.throwIfAborted();
1987
- if ([...saves].some((path) => plan.items.some((item) => item.source && inside(path, item.source) || item.destination && inside(path, item.destination)))) return "Wait for the file save to finish before changing these paths.";
1987
+ if ([...saves].some((path) => plan.items.some((item) => item.source && inside(path, item.source) || item.destination && inside(path, item.destination)))) return t("app.client.fileEditorController.waitForSave");
1988
1988
  return options.drafts.beforeMutation(intent, plan);
1989
1989
  },
1990
1990
  afterApply(intent, result) {
@@ -1998,7 +1998,7 @@ function createFileEditorController(options) {
1998
1998
  //#endregion
1999
1999
  //#region app/client/file-review-actions-controller.ts
2000
2000
  function message$2(cause) {
2001
- return cause instanceof Error ? cause.message : "The review action could not be completed. Refresh and inspect retained undo before trying again.";
2001
+ return cause instanceof Error ? cause.message : t("app.client.fileReviewActionsController.actionCouldNotBeCompleted");
2002
2002
  }
2003
2003
  /**
2004
2004
  * Runs review actions, such as staging or discarding changes, one at a time: plans one, applies
@@ -2042,7 +2042,7 @@ function createFileReviewActionsController(options) {
2042
2042
  if (intent.kind !== "undo") options.guard([intent.path], intent.kind === "discard" ? "worktree" : "index");
2043
2043
  let plan = await options.prepare(intent, signal);
2044
2044
  signal.throwIfAborted();
2045
- if (plan.kind !== intent.kind || intent.kind !== "undo" && !plan.paths.includes(intent.path)) throw new Error("The prepared review belongs to a different action or file.");
2045
+ if (plan.kind !== intent.kind || intent.kind !== "undo" && !plan.paths.includes(intent.path)) throw new Error(t("app.client.fileReviewActionsController.preparedReviewMismatch"));
2046
2046
  options.guard(plan.paths, plan.target);
2047
2047
  if (turn === generation) state = {
2048
2048
  phase: "ready",
@@ -2079,7 +2079,7 @@ function createFileReviewActionsController(options) {
2079
2079
  revision: plan.revision
2080
2080
  }, signal);
2081
2081
  signal.throwIfAborted();
2082
- if (result.id !== plan.id || result.target !== plan.target || result.changedPaths.some((path) => !plan.paths.includes(path))) throw new Error("The write response could not be confirmed. Refresh and inspect retained undo.");
2082
+ if (result.id !== plan.id || result.target !== plan.target || result.changedPaths.some((path) => !plan.paths.includes(path))) throw new Error(t("app.client.fileReviewActionsController.writeResponseUnconfirmed"));
2083
2083
  if (turn === generation) {
2084
2084
  let refreshError = "";
2085
2085
  try {
@@ -2208,11 +2208,11 @@ function reviewCommentSources(document, diffs, source) {
2208
2208
  * @returns The code anchor.
2209
2209
  */
2210
2210
  function captureCodeSelection(selection, sources, expectedRevision) {
2211
- if (selection.endSide && selection.endSide !== selection.side) throw new Error("Select consecutive lines on one side before commenting.");
2211
+ if (selection.endSide && selection.endSide !== selection.side) throw new Error(t("app.client.projectCodeComments.selectConsecutiveLines"));
2212
2212
  let side = selection.side === "deletions" ? "old" : "new";
2213
2213
  let source = sources.find((source) => (source.viewPath ?? source.path) === selection.path && (!source.side || source.side === side));
2214
- if (!source || source.contents === null || !source.revision) throw new Error("Comments require readable text with an exact source revision.");
2215
- if (expectedRevision && source.revision !== expectedRevision) throw new Error("Those lines belong to an older source. Select them again before commenting.");
2214
+ if (!source || source.contents === null || !source.revision) throw new Error(t("app.client.projectCodeComments.commentsRequireReadableText"));
2215
+ if (expectedRevision && source.revision !== expectedRevision) throw new Error(t("app.client.projectCodeComments.linesBelongToOlderSource"));
2216
2216
  return createCodeAnchor({
2217
2217
  path: source.path,
2218
2218
  revision: source.revision,
@@ -2260,7 +2260,7 @@ function createFileSearchController(options) {
2260
2260
  function notify() {
2261
2261
  if (!options.signal.aborted) options.onChange();
2262
2262
  }
2263
- function cancel(notice = "Search cancelled.") {
2263
+ function cancel(notice = t("app.client.fileSearchController.searchCancelled")) {
2264
2264
  active?.abort();
2265
2265
  active = void 0;
2266
2266
  state = {
@@ -2292,7 +2292,7 @@ function createFileSearchController(options) {
2292
2292
  try {
2293
2293
  let query = parseFileSearchQuery(input);
2294
2294
  let result = await options.request(query, signal);
2295
- if (fileSearchParameters(result.query).toString() !== fileSearchParameters(query).toString()) throw new FilesRequestError("The service returned results for a different search.", "response");
2295
+ if (fileSearchParameters(result.query).toString() !== fileSearchParameters(query).toString()) throw new FilesRequestError(t("app.client.fileSearchController.differentSearchResults"), "response");
2296
2296
  if (signal.aborted || active !== owner) return;
2297
2297
  state = {
2298
2298
  loading: false,
@@ -2305,7 +2305,7 @@ function createFileSearchController(options) {
2305
2305
  state = {
2306
2306
  loading: false,
2307
2307
  result: null,
2308
- error: cause instanceof Error ? cause.message : "Could not search this project.",
2308
+ error: cause instanceof Error ? cause.message : t("app.client.fileSearchController.searchFailed"),
2309
2309
  notice: ""
2310
2310
  };
2311
2311
  } finally {
@@ -2325,7 +2325,7 @@ function createFileSearchController(options) {
2325
2325
  * @returns A verified source-review position, or an explicit outdated-result error.
2326
2326
  */
2327
2327
  function searchMatchPosition(document, match) {
2328
- if (document.entry.path !== match.path || document.revision !== match.revision || document.entry.content !== "text" || typeof document.contents !== "string" || match.start < 0 || match.end < match.start || match.end > document.contents.length) throw new Error("This search result is outdated. Search again to locate the current text; any unsaved draft is retained.");
2328
+ if (document.entry.path !== match.path || document.revision !== match.revision || document.entry.content !== "text" || typeof document.contents !== "string" || match.start < 0 || match.end < match.start || match.end > document.contents.length) throw new Error(t("app.client.fileSearchController.outdatedResult"));
2329
2329
  let position = (offset) => {
2330
2330
  let line = 1, start = 0;
2331
2331
  for (let ending of document.contents.matchAll(/\r\n|\r|\n/g)) {
@@ -2340,7 +2340,7 @@ function searchMatchPosition(document, match) {
2340
2340
  };
2341
2341
  };
2342
2342
  let before = position(match.start), end = position(match.end);
2343
- if (before.line !== match.startLine || before.column !== match.startColumn || end.line !== match.endLine || end.column !== match.endColumn) throw new Error("This search result has invalid coordinates. Search again.");
2343
+ if (before.line !== match.startLine || before.column !== match.startColumn || end.line !== match.endLine || end.column !== match.endColumn) throw new Error(t("app.client.fileSearchController.invalidCoordinates"));
2344
2344
  return {
2345
2345
  page: 0,
2346
2346
  scroll: 0,
@@ -2355,7 +2355,7 @@ function searchMatchPosition(document, match) {
2355
2355
  //#endregion
2356
2356
  //#region app/client/project-file-review.tsx
2357
2357
  function message$1(cause) {
2358
- return cause instanceof Error ? cause.message : "Could not read the project.";
2358
+ return cause instanceof Error ? cause.message : t("app.client.projectFileReview.readProjectError");
2359
2359
  }
2360
2360
  /**
2361
2361
  * Owns validated review requests, the worker pool and bounded project view persistence.
@@ -2514,7 +2514,7 @@ function ProjectFileReview(handle) {
2514
2514
  try {
2515
2515
  if (immutable) {
2516
2516
  let next = await requestFiles(`${routes.fileReviewApi.source.href()}?${new URLSearchParams(immutable)}`, project.token, signal, parseReviewSource);
2517
- if (next.path !== immutable.path || next.commit !== immutable.commit) throw new FilesRequestError("The source response belongs to another commit or file.", "response");
2517
+ if (next.path !== immutable.path || next.commit !== immutable.commit) throw new FilesRequestError(t("app.client.projectFileReview.sourceResponseCommitMismatch"), "response");
2518
2518
  if (!signal.aborted) {
2519
2519
  source = next;
2520
2520
  displayedKey = nextDisplay;
@@ -2529,7 +2529,7 @@ function ProjectFileReview(handle) {
2529
2529
  changed();
2530
2530
  }
2531
2531
  let next = await requestFiles(`${routes.filesApi.read.href()}?${new URLSearchParams({ path: selected })}`, project.token, signal, parseFileDocument);
2532
- if (next.entry.path !== selected) throw new FilesRequestError("The source response belongs to another file.", "response");
2532
+ if (next.entry.path !== selected) throw new FilesRequestError(t("app.client.projectFileReview.sourceResponseFileMismatch"), "response");
2533
2533
  if (store) storeDocument(store, next).catch(() => {});
2534
2534
  if (!signal.aborted) {
2535
2535
  document = document?.revision === next.revision && document.entry.path === next.entry.path ? document : next;
@@ -2540,7 +2540,10 @@ function ProjectFileReview(handle) {
2540
2540
  pendingSearch = void 0;
2541
2541
  try {
2542
2542
  savePosition(searchMatchPosition(next, match), true);
2543
- searchNotice = `Search match at ${match.startLine}:${match.startColumn}. Any unsaved draft is retained.`;
2543
+ searchNotice = t("app.client.projectFileReview.searchMatchNotice", {
2544
+ line: match.startLine,
2545
+ column: match.startColumn
2546
+ });
2544
2547
  } catch (cause) {
2545
2548
  searchNotice = message$1(cause);
2546
2549
  }
@@ -2549,7 +2552,7 @@ function ProjectFileReview(handle) {
2549
2552
  }
2550
2553
  } else for (let attempt = 0; attempt < 2; attempt++) {
2551
2554
  let next = await requestFiles(`${routes.fileReviewApi.compare.href()}?${comparisonQuery(comparison)}`, project.token, signal, parseReviewSnapshot);
2552
- if (next.comparison.mode !== comparison.mode) throw new FilesRequestError("The service returned a different comparison.", "response");
2555
+ if (next.comparison.mode !== comparison.mode) throw new FilesRequestError(t("app.client.projectFileReview.comparisonMismatch"), "response");
2553
2556
  let page = Math.min(position().page, Math.max(0, Math.ceil(next.entries.length / 20) - 1));
2554
2557
  let restoredPath = position().selection?.path;
2555
2558
  if (restoredPath && settings.multiple && next.entries.some((entry) => entry.path === restoredPath)) page = Math.floor(next.entries.findIndex((entry) => entry.path === restoredPath) / 20);
@@ -2609,7 +2612,7 @@ function ProjectFileReview(handle) {
2609
2612
  changed();
2610
2613
  try {
2611
2614
  let next = await requestFiles(`${routes.fileReviewApi.history.href()}?${query}`, project.token, signal, parseReviewHistory);
2612
- if (next.path !== path) throw new FilesRequestError("The history response belongs to another path.", "response");
2615
+ if (next.path !== path) throw new FilesRequestError(t("app.client.projectFileReview.historyPathMismatch"), "response");
2613
2616
  if (!signal.aborted) history = next;
2614
2617
  } catch (cause) {
2615
2618
  if (!signal.aborted) historyError = message$1(cause);
@@ -2749,7 +2752,7 @@ function ProjectFileReview(handle) {
2749
2752
  commentError = "";
2750
2753
  try {
2751
2754
  let editing = editor?.state;
2752
- if (fromEditor && editing && (draftDirty(editing.draft) || editing.conflict || editing.saving)) throw new Error("Save this draft before creating a code comment. Existing discussions remain available, and your draft is kept.");
2755
+ if (fromEditor && editing && (draftDirty(editing.draft) || editing.conflict || editing.saving)) throw new Error(t("app.client.projectFileReview.saveDraftBeforeComment"));
2753
2756
  draftAnchor = captureCodeSelection(selection, fromEditor && editing && selection.side !== "deletions" ? [{
2754
2757
  path: editing.draft.path,
2755
2758
  revision: editing.draft.baseRevision,
@@ -2763,8 +2766,8 @@ function ProjectFileReview(handle) {
2763
2766
  changed();
2764
2767
  }
2765
2768
  async function createComment(body) {
2766
- if (!draftAnchor) throw new Error("Select code lines again before commenting.");
2767
- if (editor?.state && draftDirty(editor.state.draft)) throw new Error("Save your draft before sending this code comment. The message is kept.");
2769
+ if (!draftAnchor) throw new Error(t("app.client.projectFileReview.selectCodeLinesAgain"));
2770
+ if (editor?.state && draftDirty(editor.state.draft)) throw new Error(t("app.client.projectFileReview.saveDraftBeforeSendingComment"));
2768
2771
  selectedThread = (await handle.props.comments.write({
2769
2772
  anchor: draftAnchor,
2770
2773
  body
@@ -2802,11 +2805,11 @@ function ProjectFileReview(handle) {
2802
2805
  apply: (body, signal) => requestFiles(routes.fileReviewActionsApi.apply.href(), project.token, signal, parseReviewActionResult, fetch, { body }),
2803
2806
  recoveries: (signal) => requestFiles(routes.fileReviewActionsApi.recoveries.href(), project.token, signal, parseReviewActionRecoveries),
2804
2807
  guard(paths) {
2805
- if (editor?.state?.saving) throw new Error("Wait for the current save to finish before reviewing changes.");
2808
+ if (editor?.state?.saving) throw new Error(t("app.client.projectFileReview.waitForSaveBeforeReview"));
2806
2809
  if (paths.some((path) => {
2807
2810
  let draft = drafts?.get(path);
2808
2811
  return draft && draftDirty(draft);
2809
- })) throw new Error("Save or explicitly discard the unsaved draft for this file before applying review actions. Your draft has been kept.");
2812
+ })) throw new Error(t("app.client.projectFileReview.saveOrDiscardDraftBeforeActions"));
2810
2813
  },
2811
2814
  async onApplied(result) {
2812
2815
  if (result.target === "worktree") editor?.close();
@@ -2827,7 +2830,7 @@ function ProjectFileReview(handle) {
2827
2830
  if (handle.signal.aborted || reading === active || !reading) break;
2828
2831
  current = reading;
2829
2832
  }
2830
- if (error) throw new Error(`The write completed, but the review could not refresh: ${error}`);
2833
+ if (error) throw new Error(t("app.client.projectFileReview.writeCompletedRefreshError", { error }));
2831
2834
  },
2832
2835
  onChange: changed
2833
2836
  });
@@ -2963,7 +2966,7 @@ function ProjectFileReview(handle) {
2963
2966
  handle.queueTask(() => {
2964
2967
  owned.pool.setRenderOptions({ lineDiffType: session.view.highlights }).catch((cause) => {
2965
2968
  if (!handle.signal.aborted) {
2966
- error = `Could not update highlighting. ${message$1(cause)}`;
2969
+ error = t("app.client.projectFileReview.updateHighlightingError", { error: message$1(cause) });
2967
2970
  changed();
2968
2971
  }
2969
2972
  });
@@ -3062,7 +3065,7 @@ function ProjectFileReview(handle) {
3062
3065
  tone: "muted",
3063
3066
  role: "status",
3064
3067
  class: "shrink-0 border-b border-line px-3 py-1.5",
3065
- children: commentsUnavailable
3068
+ children: t("app.client.projectFileReview.commentsUnavailable")
3066
3069
  }) : null,
3067
3070
  searchNotice ? /* @__PURE__ */ jsx(Text, {
3068
3071
  as: "p",
@@ -3083,7 +3086,7 @@ function ProjectFileReview(handle) {
3083
3086
  variant: "ui-caption",
3084
3087
  role: "status",
3085
3088
  class: "sr-only",
3086
- children: "Reading current file for editing…"
3089
+ children: t("app.client.projectFileReview.readingFileForEditing")
3087
3090
  }) : null,
3088
3091
  editing && editor && merge?.path === editing.draft.path ? /* @__PURE__ */ jsx(FileConflictPanel, {
3089
3092
  chrome: handle.props.chrome,
@@ -3193,7 +3196,7 @@ function ProjectFileReview(handle) {
3193
3196
  onResolve: (diff) => {
3194
3197
  resolveConflict(diff);
3195
3198
  },
3196
- editLabel: handle.props.path && drafts?.get(handle.props.path) && draftDirty(drafts.get(handle.props.path)) ? "Resume draft" : "Edit file",
3199
+ editLabel: handle.props.path && drafts?.get(handle.props.path) && draftDirty(drafts.get(handle.props.path)) ? t("app.client.projectFileReview.resumeDraft") : t("app.client.projectFileReview.editFile"),
3197
3200
  onChange: changeView,
3198
3201
  onPosition: savePosition,
3199
3202
  onSelect: selectPath,
@@ -3304,7 +3307,7 @@ function ProjectFileReview(handle) {
3304
3307
  //#endregion
3305
3308
  //#region app/client/project-files.tsx
3306
3309
  function message(error) {
3307
- return error instanceof Error ? error.message : "Could not load project files.";
3310
+ return error instanceof Error ? error.message : t("app.client.projectFiles.couldNotLoadProjectFiles");
3308
3311
  }
3309
3312
  function requestProjectFiles(token, view, signal) {
3310
3313
  let params = new URLSearchParams({
@@ -3457,8 +3460,8 @@ function ProjectFiles(handle) {
3457
3460
  async onApplied(intent, result) {
3458
3461
  view = mutationFileView(view, intent.kind, result);
3459
3462
  let count = result.items.filter((item) => item.status === "applied").length;
3460
- if (count) toast.success(`${count} file action${count === 1 ? "" : "s"} completed`, { description: result.recoveryId ? "Original contents are available in Recovery." : void 0 });
3461
- else toast.error("The file action could not be applied", { description: "Your project was refreshed." });
3463
+ if (count) toast.success(t("app.client.projectFiles.fileActionsCompleted", { count }), { description: result.recoveryId ? t("app.client.projectFiles.originalContentsAvailableInRecovery") : void 0 });
3464
+ else toast.error(t("app.client.projectFiles.fileActionCouldNotBeApplied"), { description: t("app.client.projectFiles.projectWasRefreshed") });
3462
3465
  location(true);
3463
3466
  persist();
3464
3467
  pendingContentRefresh = true;
@@ -3521,7 +3524,7 @@ function ProjectFiles(handle) {
3521
3524
  function closeTab(path) {
3522
3525
  if (dirty.includes(path)) {
3523
3526
  select(path, [path]);
3524
- toast("Save or discard this draft to close its tab");
3527
+ toast(t("app.client.projectFiles.saveOrDiscardDraftToCloseTab"));
3525
3528
  changed();
3526
3529
  return;
3527
3530
  }
@@ -3633,7 +3636,7 @@ function ProjectFiles(handle) {
3633
3636
  }, { once: true });
3634
3637
  });
3635
3638
  function foreignNotice(foreign) {
3636
- notice = foreign ? "This file link belongs to another project. Choose a file from this project to continue." : "";
3639
+ notice = foreign ? t("app.client.projectFiles.fileLinkBelongsToAnotherProject") : "";
3637
3640
  }
3638
3641
  return () => {
3639
3642
  if (renderedHref !== handle.props.href) {