@bud.sh/app 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/client/assets/bud/design.js +1 -1
  2. package/dist/client/assets/canvas-C8hyPWoT.js +1 -0
  3. package/dist/client/assets/difference-worker-BfF4_N3e.js +8 -0
  4. package/dist/client/assets/files-D_Tm7gCK.js +1 -0
  5. package/dist/client/assets/i18n-BtfXum8P.js +1 -0
  6. package/dist/client/assets/index-Dpxjiok1.js +2 -0
  7. package/dist/client/assets/pages-B-_CKvDS.js +1 -0
  8. package/dist/client/assets/project-branches-DzXdaT9L.js +1 -0
  9. package/dist/client/assets/project-comments-CQkB2jbE.js +1 -0
  10. package/dist/client/assets/project-thread-links-BN7zL0AX.js +1 -0
  11. package/dist/client/assets/{project-threads-DezXiVK6.js → project-threads-CHGG-6fo.js} +104 -104
  12. package/dist/client/assets/{project-work-CB1JHqGW.js → project-work-L5fYMSOu.js} +1 -1
  13. package/dist/client/assets/task-design-DYqcqq1b.js +23 -0
  14. package/dist/client/assets/tasks-A2FKo3_h.js +1 -0
  15. package/dist/client/assets/tasks-request-DGigNdyK.js +1 -0
  16. package/dist/client/assets/workspace-CvWmU2RK.js +2 -0
  17. package/dist/client/index.html +2 -1
  18. package/dist/hosted/assets/{canvas-C69-96GR.js → canvas-MWtOwaWm.js} +69 -41
  19. package/dist/hosted/assets/{comment-data-CiDuvXi2.js → comment-data-DsgJWq4n.js} +7195 -1067
  20. package/dist/hosted/assets/{files-DQL5hzs3.js → files-Wvo11G2A.js} +92 -89
  21. package/dist/hosted/assets/{pages-DFs7jB6M.js → pages-BAmRFVtU.js} +31 -22
  22. package/dist/hosted/assets/{project-branches-CffV4IS1.js → project-branches-BNloVosX.js} +5 -5
  23. package/dist/hosted/assets/{project-thread-links-B1Y2bDUx.js → project-thread-links-BmY8HQXt.js} +12 -12
  24. package/dist/hosted/assets/{screen-BroBb1Z_.js → screen-QmqmI2Aq.js} +18 -18
  25. package/dist/hosted/assets/{tasks-mzmNhe2d.js → tasks-C8iWPrWg.js} +29 -29
  26. package/dist/hosted/assets/{tasks-request-Dcrpd2_8.js → tasks-request-C89fN1dK.js} +7 -7
  27. package/dist/hosted/hosted.js +48 -29
  28. package/dist/node/design.js +6235 -139
  29. package/dist/node/index.js +4 -2
  30. package/dist/node-host/assets/{canvas-BZhzizzt.js → canvas-D_F8SIlm.js} +70 -42
  31. package/dist/node-host/assets/{comment-data-nUlI0szN.js → comment-data-q_8INiZz.js} +7221 -1093
  32. package/dist/node-host/assets/{files-CTzcWGjb.js → files-xazOrNCX.js} +92 -89
  33. package/dist/node-host/assets/{pages-0aWH7z1j.js → pages-D6VkmdfQ.js} +31 -22
  34. package/dist/node-host/assets/{project-branches-1zs_0J_y.js → project-branches-C26hwmo3.js} +5 -5
  35. package/dist/node-host/assets/{project-thread-links-LpUAGcDr.js → project-thread-links-BOzjws5W.js} +12 -12
  36. package/dist/node-host/assets/{screen-DQoTOWYR.js → screen-Dog-YByu.js} +18 -18
  37. package/dist/node-host/assets/{tasks-C0WqnM7Q.js → tasks-A1_bpb3_.js} +30 -30
  38. package/dist/node-host/assets/{tasks-request-Bwky5ORf.js → tasks-request-Bie1PpQP.js} +7 -7
  39. package/dist/node-host/node-host.js +48 -29
  40. package/dist/server/assets/{branch-previews-BryeA_kA.js → branch-previews-ClkoJ0lM.js} +4 -2
  41. package/dist/server/assets/{canvas-D6tIkONI.js → canvas-Dn4RYoU2.js} +70 -42
  42. package/dist/server/assets/{comment-data-BuArfgcc.js → comment-data-B0OLHBGp.js} +7219 -1091
  43. package/dist/server/assets/{files-DkrEB48U.js → files-C5Vilfe4.js} +92 -89
  44. package/dist/server/assets/{listener-oKd5ETMj.js → listener-o1STuLju.js} +43 -29
  45. package/dist/server/assets/{pages-iEdmK_o2.js → pages-BNa0MHEc.js} +31 -22
  46. package/dist/server/assets/{project-branches-B7rtrvMl.js → project-branches-GtQYyOH5.js} +5 -5
  47. package/dist/server/assets/{project-thread-links-ue_ad9IX.js → project-thread-links-Nfz9NIny.js} +12 -12
  48. package/dist/server/assets/{project-work-6aOyja4v.js → project-work-Cy_BpHwR.js} +2 -2
  49. package/dist/server/assets/{screen-DMUB9a-K.js → screen-BtcJmZwj.js} +18 -18
  50. package/dist/server/assets/{server-DN5uwE4k.js → server-CYvuOSBA.js} +1 -1
  51. package/dist/server/assets/{services-Ber8opFE.js → services-DK4fqrlF.js} +2 -2
  52. package/dist/server/assets/{tasks-C2oa2wSC.js → tasks-B82tHwyQ.js} +31 -31
  53. package/dist/server/assets/{tasks-request-Bk5EoY_p.js → tasks-request-BtJBlkxV.js} +7 -6
  54. package/dist/server/capture.js +1 -1
  55. package/dist/server/migration.js +3 -3
  56. package/dist/server/runtime.js +4 -4
  57. package/dist/server/server.js +3 -3
  58. package/dist/types/assets/{config-Cuauq8xY.d.ts → config-DIC2cf_S.d.ts} +3 -0
  59. package/dist/types/assets/{contract-BsCQbbxD.d.ts → contract-CAT-XFNq.d.ts} +3 -1
  60. package/dist/types/companion.d.ts +1 -1
  61. package/dist/types/hosted.d.ts +2 -2
  62. package/dist/types/index.d.ts +1 -1
  63. package/dist/types/node-host.d.ts +2 -2
  64. package/dist/types/screen.d.ts +1 -1
  65. package/package.json +1 -1
  66. package/dist/client/assets/canvas-DxaIItAs.js +0 -1
  67. package/dist/client/assets/difference-worker-BpqPBs5g.js +0 -8
  68. package/dist/client/assets/files-C-7xoSt4.js +0 -1
  69. package/dist/client/assets/index-GPPa1XPO.js +0 -2
  70. package/dist/client/assets/pages-BVrTX_Eq.js +0 -1
  71. package/dist/client/assets/project-branches-DpxQx-T4.js +0 -1
  72. package/dist/client/assets/project-comments-D897aU5O.js +0 -1
  73. package/dist/client/assets/project-thread-links-Bp_TseD0.js +0 -1
  74. package/dist/client/assets/task-design-bktfn_Ok.js +0 -23
  75. package/dist/client/assets/tasks-Cqfj4fQN.js +0 -1
  76. package/dist/client/assets/tasks-request-mWMk9pBo.js +0 -1
  77. package/dist/client/assets/workspace-CznMN7F6.js +0 -2
@@ -1,10 +1,10 @@
1
1
  import { C as getFiletypeFromFileName, P as __toESM, S as getCustomExtensionsVersion, x as getCustomExtensionsMap } from "./cloneFileDiffMetadata-B-xwytg8.js";
2
2
  import { Dt as parseFileSearchQuery, Ft as parseFileChange, Ht as parseFilePath, I as parseFileUpdate, It as parseFileDocument, Lt as parseFileIndex, Ot as parseFileSearchResult, P as createCodeAnchor, Q as parseReviewActionRecoveries, R as reviewConflictIdentity, Z as parseReviewActionPlan, bt as parseReviewSnapshot, ct as parseRecoveries, et as parseReviewActionResult, gt as parseReviewHistory, h as fileRoutes, i as routes, mt as parseReviewDiff, nt as parseFileSaveResult, ot as parseMutationPlan, rt as maxFileSaveBytes, st as parseMutationResult, tt as parseFileSaveRequest, vt as parseReviewRepository, xt as parseReviewSource } from "./events-6z5v7Uca.js";
3
- import { $t as areFileRenderOptionsEqual, A as FileRecoveryDialog, At as fileHref, Cn as Text, Ct as readReviewLocation, D as FileEditorPanel, Dt as reviewHref, E as ReviewActionDialog, Et as reviewContextKey, F as defaultFileDestination, G as ConversationSidebar, Gt as areDiffTargetsEqual, Ht as renderDiffWithHighlighter, I as fileActionIntent, Jt as WorkerPoolWorkerError, Kt as WorkerPoolTaskCanceledError, L as mutationFileView, M as FileReviewPanel, Mt as readFileLocation, N as codeThemes, Nt as reconcileFileView, O as defaultEditorPreferences, Ot as closeFileTab, P as FileWorkspace, Pt as rememberFile, Qt as renderFileWithHighlighter, R as remainingMutationIntent, Sn as writePreference, St as parseReviewSession, T as FileConflictPanel, Tt as reviewComparison, U as FileSearchPanel, Ut as areDiffRenderOptionsEqual, Vt as isDiffPlainText, Wt as areFilesEqual, Xt as markWorkerErrorReported, Yt as isHandledWorkerPoolError, Zt as isFilePlainText, an as attachResolvedThemes, bn as toast, bt as defaultReviewView, cn as getResolvedOrResolveLanguage, ct as codeCommentCounts, dn as ResolvedLanguages, en as hasResolvedThemes, f as storeDocument, ft as isCodeConversation, ht as readConversationLocation, in as validateResolvedThemeName, j as FileMutationDialog, jt as parseFileView, k as parseEditorPreferences, kt as defaultFileView, l as projectStore, ln as resolveLanguage, m as storedFiles, mt as locateCodeConversations, nn as getSharedHighlighter, on as themeResolver, p as storedDocument, qt as WorkerPoolTerminatedError, rn as prepareThemeResolution, s as applyFileUpdate, sn as getThemes, t as commentsUnavailable, tn as areThemesEqual, u as saveFiles, un as areLanguagesAttached, ut as conversationHref, v as projectLive, wt as rememberReviewPosition, xn as readPreference, yt as defaultReviewPosition } from "./comment-data-BuArfgcc.js";
3
+ import { $t as areFileRenderOptionsEqual, A as FileRecoveryDialog, At as fileHref, Ct as readReviewLocation, D as FileEditorPanel, Dn as Text, Dt as reviewHref, E as ReviewActionDialog, Et as reviewContextKey, F as defaultFileDestination, G as ConversationSidebar, Gt as areDiffTargetsEqual, Ht as renderDiffWithHighlighter, I as fileActionIntent, Jt as WorkerPoolWorkerError, Kt as WorkerPoolTaskCanceledError, L as mutationFileView, M as FileReviewPanel, Mt as readFileLocation, N as codeThemes, Nt as reconcileFileView, O as defaultEditorPreferences, Ot as closeFileTab, P as FileWorkspace, Pt as rememberFile, Qt as renderFileWithHighlighter, R as remainingMutationIntent, Sn as writePreference, St as parseReviewSession, T as FileConflictPanel, Tn as t, Tt as reviewComparison, U as FileSearchPanel, Ut as areDiffRenderOptionsEqual, Vt as isDiffPlainText, Wt as areFilesEqual, Xt as markWorkerErrorReported, Yt as isHandledWorkerPoolError, Zt as isFilePlainText, an as attachResolvedThemes, bn as toast, bt as defaultReviewView, cn as getResolvedOrResolveLanguage, ct as codeCommentCounts, dn as ResolvedLanguages, en as hasResolvedThemes, f as storeDocument, ft as isCodeConversation, ht as readConversationLocation, in as validateResolvedThemeName, j as FileMutationDialog, jt as parseFileView, k as parseEditorPreferences, kt as defaultFileView, l as projectStore, ln as resolveLanguage, m as storedFiles, mt as locateCodeConversations, nn as getSharedHighlighter, on as themeResolver, p as storedDocument, qt as WorkerPoolTerminatedError, rn as prepareThemeResolution, s as applyFileUpdate, sn as getThemes, tn as areThemesEqual, u as saveFiles, un as areLanguagesAttached, ut as conversationHref, v as projectLive, wt as rememberReviewPosition, xn as readPreference, yt as defaultReviewPosition } from "./comment-data-B0OLHBGp.js";
4
4
  import { l as DEFAULT_THEMES } from "./constants-C7Y3SJGj.js";
5
- import { n as createProjectComments, t as defineScreen } from "./screen-DMUB9a-K.js";
5
+ import { n as createProjectComments, t as defineScreen } from "./screen-BtcJmZwj.js";
6
6
  import { t as require_lru } from "./lru-C4Rh6JYD.js";
7
- import { t as ProjectThreadLinks } from "./project-thread-links-ue_ad9IX.js";
7
+ import { t as ProjectThreadLinks } from "./project-thread-links-Nfz9NIny.js";
8
8
  import { ref } from "remix/component";
9
9
  import { jsx, jsxs } from "remix/component/jsx-runtime";
10
10
  //#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
@@ -1040,7 +1040,7 @@ var FilesRequestError = class extends Error {
1040
1040
  async function requestFiles(url, token, signal, parse, fetcher = fetch, action) {
1041
1041
  let parsed = new URL(url, "http://bud.invalid");
1042
1042
  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";
1043
- 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");
1043
+ 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");
1044
1044
  signal.throwIfAborted();
1045
1045
  let response = await fetcher(url, {
1046
1046
  method: mutation || parsed.pathname === "/api/files/refresh" ? "POST" : "GET",
@@ -1055,7 +1055,7 @@ async function requestFiles(url, token, signal, parse, fetcher = fetch, action)
1055
1055
  signal
1056
1056
  });
1057
1057
  let reader = response.body?.getReader();
1058
- if (!reader) throw new FilesRequestError("The file service returned an empty response.", "response", response.status);
1058
+ if (!reader) throw new FilesRequestError(t("app.client.filesRequest.emptyResponse"), "response", response.status);
1059
1059
  let chunks = [];
1060
1060
  let size = 0;
1061
1061
  let cancel = () => {
@@ -1069,7 +1069,7 @@ async function requestFiles(url, token, signal, parse, fetcher = fetch, action)
1069
1069
  signal.throwIfAborted();
1070
1070
  if (part.done) break;
1071
1071
  size += part.value.byteLength;
1072
- if (size > 16777216) throw new FilesRequestError("This file response exceeds the display limit. Narrow the file view and try again.", "limit", response.status);
1072
+ if (size > 16777216) throw new FilesRequestError(t("app.client.filesRequest.responseExceedsDisplayLimit"), "limit", response.status);
1073
1073
  chunks.push(part.value);
1074
1074
  }
1075
1075
  } catch (error) {
@@ -1089,13 +1089,13 @@ async function requestFiles(url, token, signal, parse, fetcher = fetch, action)
1089
1089
  try {
1090
1090
  value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
1091
1091
  } catch {
1092
- throw new FilesRequestError("The file service returned invalid data. Refresh and try again.", "response", response.status);
1092
+ throw new FilesRequestError(t("app.client.filesRequest.invalidData"), "response", response.status);
1093
1093
  }
1094
- 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);
1094
+ 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);
1095
1095
  try {
1096
1096
  return parse(value);
1097
1097
  } catch {
1098
- throw new FilesRequestError("The file service returned unexpected data. Refresh and try again.", "response", response.status);
1098
+ throw new FilesRequestError(t("app.client.filesRequest.unexpectedData"), "response", response.status);
1099
1099
  }
1100
1100
  }
1101
1101
  //#endregion
@@ -1126,17 +1126,17 @@ function createFileMutationGuards() {
1126
1126
  };
1127
1127
  }
1128
1128
  function message$4(cause) {
1129
- return cause instanceof Error ? cause.message : "The file action could not be completed.";
1129
+ return cause instanceof Error ? cause.message : t("app.client.fileMutationController.actionCouldNotBeCompleted");
1130
1130
  }
1131
1131
  function assertPlan(intent, plan) {
1132
- if (intent.kind !== plan.kind) throw new Error("The service returned a different file action.");
1132
+ if (intent.kind !== plan.kind) throw new Error(t("app.client.fileMutationController.differentFileAction"));
1133
1133
  if (intent.kind === "restore") return;
1134
1134
  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]];
1135
1135
  let actual = plan.items.map((item) => [item.source, item.destination]);
1136
- if (JSON.stringify(expected) !== JSON.stringify(actual)) throw new Error("The service returned different file paths. Refresh before continuing.");
1136
+ if (JSON.stringify(expected) !== JSON.stringify(actual)) throw new Error(t("app.client.fileMutationController.differentFilePaths"));
1137
1137
  }
1138
1138
  function assertResult(plan, result) {
1139
- 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.");
1139
+ 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"));
1140
1140
  }
1141
1141
  /**
1142
1142
  * Coordinates validated plans without hiding partial results or retrying writes.
@@ -1215,7 +1215,7 @@ function createFileMutationController(options) {
1215
1215
  ...state,
1216
1216
  phase: "editing",
1217
1217
  plan: null,
1218
- error: "This confirmation expired. Review the current files again."
1218
+ error: t("app.client.fileMutationController.confirmationExpired")
1219
1219
  };
1220
1220
  changed();
1221
1221
  return;
@@ -1249,7 +1249,7 @@ function createFileMutationController(options) {
1249
1249
  try {
1250
1250
  await options.guards.afterApply(current, result);
1251
1251
  } catch (cause) {
1252
- draftError = `Files changed, but retained draft metadata could not be updated. ${message$4(cause)}`;
1252
+ draftError = t("app.client.fileMutationController.draftMetadataCouldNotBeUpdated", { error: message$4(cause) });
1253
1253
  }
1254
1254
  await options.onApplied(current, result);
1255
1255
  if (turn !== sequence || !state) return;
@@ -1274,7 +1274,7 @@ function createFileMutationController(options) {
1274
1274
  await options.onUncertain().catch(() => {});
1275
1275
  if (state && turn === sequence) state = {
1276
1276
  ...state,
1277
- error: `${message$4(cause)} Refresh and check Recovery before retrying.`
1277
+ error: t("app.client.fileMutationController.refreshAndCheckRecovery", { error: message$4(cause) })
1278
1278
  };
1279
1279
  }
1280
1280
  changed();
@@ -1418,7 +1418,7 @@ function comparisonQuery(comparison) {
1418
1418
  * @returns Exact source snapshots plus explicit per-file failures.
1419
1419
  */
1420
1420
  async function readReviewPage(snapshot, paths, token, signal, fetcher = fetch) {
1421
- 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");
1421
+ if (paths.length > 20 || paths.some((path) => !snapshot.entries.some((entry) => entry.path === path))) throw new FilesRequestError(t("app.client.projectFileReviewData.filesOutsidePage"), "input");
1422
1422
  let result = /* @__PURE__ */ new Map();
1423
1423
  let failures = {};
1424
1424
  let cursor = 0;
@@ -1436,7 +1436,7 @@ async function readReviewPage(snapshot, paths, token, signal, fetcher = fetch) {
1436
1436
  query.set("expectedRevision", snapshot.revision);
1437
1437
  try {
1438
1438
  let diff = await requestFiles(`${routes.fileReviewApi.diff.href()}?${query}`, token, lifetime, parseReviewDiff, fetcher);
1439
- 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);
1439
+ 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);
1440
1440
  let bytes = [
1441
1441
  diff.oldFile,
1442
1442
  diff.newFile,
@@ -1444,14 +1444,14 @@ async function readReviewPage(snapshot, paths, token, signal, fetcher = fetch) {
1444
1444
  diff.conflict?.ours,
1445
1445
  diff.conflict?.theirs
1446
1446
  ].reduce((total, file) => total + (file?.contents?.length ?? 0) * 2, 0);
1447
- if (retainedBytes + bytes > 33554432) failures[path] = "This page reached its memory limit. Open this file individually.";
1447
+ if (retainedBytes + bytes > 33554432) failures[path] = t("app.client.projectFileReviewData.memoryLimit");
1448
1448
  else {
1449
1449
  retainedBytes += bytes;
1450
1450
  result.set(path, diff);
1451
1451
  }
1452
1452
  } catch (cause) {
1453
1453
  if (lifetime.aborted || cause instanceof FilesRequestError && (cause.status === 409 || cause.status === 401 || cause.status === 403)) throw cause;
1454
- failures[path] = cause instanceof Error ? cause.message : "Could not read this comparison.";
1454
+ failures[path] = cause instanceof Error ? cause.message : t("app.client.projectFileReviewData.couldNotReadComparison");
1455
1455
  }
1456
1456
  }
1457
1457
  } catch (cause) {
@@ -1503,19 +1503,19 @@ function within(path, parent) {
1503
1503
  */
1504
1504
  function parseDrafts(value, projectId) {
1505
1505
  if (!value) return [];
1506
- if (value.length > maxStoredCharacters) throw new Error("The local draft record exceeds its limit.");
1506
+ if (value.length > maxStoredCharacters) throw new Error(t("app.client.fileDrafts.recordExceedsLimit"));
1507
1507
  let parsed = JSON.parse(value);
1508
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Invalid local draft record.");
1508
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(t("app.client.fileDrafts.invalidRecord"));
1509
1509
  let record = parsed;
1510
1510
  if (record.version !== 1 || record.projectId !== projectId || !Array.isArray(record.drafts) || record.drafts.length > maxDrafts || Object.keys(record).some((key) => ![
1511
1511
  "version",
1512
1512
  "projectId",
1513
1513
  "drafts"
1514
- ].includes(key))) throw new Error("Invalid local draft record.");
1514
+ ].includes(key))) throw new Error(t("app.client.fileDrafts.invalidRecord"));
1515
1515
  let seen = /* @__PURE__ */ new Set();
1516
1516
  let total = 0;
1517
1517
  return record.drafts.map((input) => {
1518
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("Invalid local draft.");
1518
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(t("app.client.fileDrafts.invalidDraft"));
1519
1519
  let item = input;
1520
1520
  if (Object.keys(item).some((key) => ![
1521
1521
  "id",
@@ -1524,7 +1524,7 @@ function parseDrafts(value, projectId) {
1524
1524
  "baseContents",
1525
1525
  "contents",
1526
1526
  "updatedAt"
1527
- ].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.");
1527
+ ].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"));
1528
1528
  parseFileSaveRequest({
1529
1529
  path: item.path,
1530
1530
  expectedRevision: item.baseRevision,
@@ -1536,10 +1536,10 @@ function parseDrafts(value, projectId) {
1536
1536
  contents: item.contents
1537
1537
  });
1538
1538
  let draft = item;
1539
- if (seen.has(draft.path)) throw new Error("Duplicate local draft path.");
1539
+ if (seen.has(draft.path)) throw new Error(t("app.client.fileDrafts.duplicateDraftPath"));
1540
1540
  seen.add(draft.path);
1541
1541
  total += bytes(draft);
1542
- if (total > maxDraftBytes) throw new Error("The local drafts exceed their memory limit.");
1542
+ if (total > maxDraftBytes) throw new Error(t("app.client.fileDrafts.draftsExceedMemoryLimit"));
1543
1543
  return { ...draft };
1544
1544
  });
1545
1545
  }
@@ -1557,7 +1557,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1557
1557
  try {
1558
1558
  for (let draft of parseDrafts(storage?.getItem(key) ?? null, projectId)) drafts.set(draft.path, draft);
1559
1559
  } catch {
1560
- warning = "Local drafts could not be read. The stored record has been left untouched.";
1560
+ warning = t("app.client.fileDrafts.readFailed");
1561
1561
  }
1562
1562
  let unreadable = Boolean(warning);
1563
1563
  function persist() {
@@ -1573,16 +1573,16 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1573
1573
  warning = "";
1574
1574
  return true;
1575
1575
  } catch {
1576
- warning = "This draft is kept in this tab only. Browser storage is unavailable or full; save or download it before closing the tab.";
1576
+ warning = t("app.client.fileDrafts.tabOnlyWarning");
1577
1577
  return false;
1578
1578
  }
1579
1579
  }
1580
1580
  function put(draft) {
1581
- 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.");
1581
+ if (encoder.encode(draft.contents).length > 2097152 || !draft.contents.isWellFormed() || draft.contents.includes("\0")) throw new Error(t("app.client.fileDrafts.unsupportedDraftText"));
1582
1582
  let clean = [...drafts.values()].filter((item) => item.path !== draft.path && !draftDirty(item));
1583
1583
  for (let item of clean) drafts.delete(item.path);
1584
1584
  let others = [...drafts.values()].filter((item) => item.path !== draft.path);
1585
- 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.");
1585
+ if (others.length >= maxDrafts || others.reduce((sum, item) => sum + bytes(item), bytes(draft)) > maxDraftBytes) throw new Error(t("app.client.fileDrafts.draftLimitReached"));
1586
1586
  drafts.set(draft.path, draft);
1587
1587
  persist();
1588
1588
  return draft;
@@ -1616,7 +1616,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1616
1616
  },
1617
1617
  change(path, contents) {
1618
1618
  let draft = drafts.get(path);
1619
- if (!draft) throw new Error("This draft is no longer open.");
1619
+ if (!draft) throw new Error(t("app.client.fileDrafts.draftNoLongerOpen"));
1620
1620
  return put({
1621
1621
  ...draft,
1622
1622
  contents,
@@ -1635,7 +1635,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1635
1635
  },
1636
1636
  rebase(path, revision, contents) {
1637
1637
  let draft = drafts.get(path);
1638
- if (!draft) throw new Error("This draft is no longer open.");
1638
+ if (!draft) throw new Error(t("app.client.fileDrafts.draftNoLongerOpen"));
1639
1639
  return put({
1640
1640
  ...draft,
1641
1641
  id: crypto.randomUUID(),
@@ -1651,11 +1651,11 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1651
1651
  persist,
1652
1652
  beforeMutation(intent, plan) {
1653
1653
  let dirty = [...drafts.values()].filter(draftDirty);
1654
- 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.";
1655
- 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.";
1654
+ if (intent.kind === "delete" && dirty.some((draft) => intent.paths.some((path) => within(draft.path, path)))) return t("app.client.fileDrafts.deleteWithUnsavedDrafts");
1655
+ if (intent.kind === "restore" && dirty.some((draft) => plan.items.some((item) => item.destination && within(draft.path, item.destination)))) return t("app.client.fileDrafts.restoreWithUnsavedDrafts");
1656
1656
  if (intent.kind === "move") {
1657
- 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.";
1658
- 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.";
1657
+ if (dirty.some((draft) => intent.entries.some((entry) => within(draft.path, entry.destination) && !within(draft.path, entry.path)))) return t("app.client.fileDrafts.destinationHasDraft");
1658
+ if (dirty.some((draft) => intent.entries.some((entry) => within(draft.path, entry.path))) && !persist()) return t("app.client.fileDrafts.storageUnavailableForMove");
1659
1659
  }
1660
1660
  return null;
1661
1661
  },
@@ -1676,7 +1676,7 @@ function createFileDrafts(projectId, storage, now = Date.now) {
1676
1676
  }
1677
1677
  for (let { draft, path } of moved) {
1678
1678
  let destination = next.get(path);
1679
- 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.");
1679
+ if (destination && draftDirty(destination)) throw new Error(t("app.client.fileDrafts.destinationDraftConflict"));
1680
1680
  next.set(path, {
1681
1681
  ...draft,
1682
1682
  id: crypto.randomUUID(),
@@ -1711,7 +1711,7 @@ function projectFileDrafts(projectId, storage) {
1711
1711
  let existing = openProjects.get(projectId);
1712
1712
  if (existing) return existing;
1713
1713
  for (let [id, drafts] of openProjects) if (!drafts.dirty.length) openProjects.delete(id);
1714
- if (openProjects.size >= 4) throw new Error("Save or discard drafts in another project before opening more editors.");
1714
+ if (openProjects.size >= 4) throw new Error(t("app.client.fileDrafts.projectDraftLimitReached"));
1715
1715
  let drafts = createFileDrafts(projectId, storage);
1716
1716
  openProjects.set(projectId, drafts);
1717
1717
  return drafts;
@@ -1719,11 +1719,11 @@ function projectFileDrafts(projectId, storage) {
1719
1719
  //#endregion
1720
1720
  //#region app/client/file-editor-controller.ts
1721
1721
  function message$3(cause) {
1722
- return cause instanceof Error ? cause.message : "The file could not be saved. Your draft has been kept.";
1722
+ return cause instanceof Error ? cause.message : t("app.client.fileEditorController.saveFailed");
1723
1723
  }
1724
1724
  function readable(document, path) {
1725
- if (document.entry.path !== path) throw new Error("The source response belongs to another file.");
1726
- if (document.contents === null || !document.revision) throw new Error("Only regular UTF-8 text files within the editor size limit can be edited.");
1725
+ if (document.entry.path !== path) throw new Error(t("app.client.fileEditorController.wrongFileResponse"));
1726
+ if (document.contents === null || !document.revision) throw new Error(t("app.client.fileEditorController.unsupportedFile"));
1727
1727
  return {
1728
1728
  revision: document.revision,
1729
1729
  contents: document.contents
@@ -1767,7 +1767,7 @@ function createFileEditorController(options) {
1767
1767
  state = {
1768
1768
  ...state,
1769
1769
  conflict: true,
1770
- error: "This file changed outside the editor. Your draft has been kept. Compare with the latest file before saving."
1770
+ error: t("app.client.fileEditorController.changedOutsideEditor")
1771
1771
  };
1772
1772
  changed();
1773
1773
  }
@@ -1801,12 +1801,12 @@ function createFileEditorController(options) {
1801
1801
  contents: draft.baseContents
1802
1802
  },
1803
1803
  mode,
1804
- baselineLabel: useIndex ? "Index" : "Opened version",
1804
+ baselineLabel: useIndex ? t("app.client.fileEditorController.index") : t("app.client.fileEditorController.openedVersion"),
1805
1805
  saving: saves.has(path),
1806
1806
  conflict,
1807
1807
  latest: null,
1808
- error: conflict ? "This recovered draft belongs to an older file version. Compare it with the latest file before saving." : "",
1809
- status: draftDirty(draft) ? "Recovered unsaved draft." : ""
1808
+ error: conflict ? t("app.client.fileEditorController.recoveredDraftOldVersion") : "",
1809
+ status: draftDirty(draft) ? t("app.client.fileEditorController.recoveredUnsavedDraft") : ""
1810
1810
  };
1811
1811
  } catch (cause) {
1812
1812
  if (!signal.aborted) {
@@ -1823,12 +1823,12 @@ function createFileEditorController(options) {
1823
1823
  contents: retained.baseContents
1824
1824
  },
1825
1825
  mode: "file",
1826
- baselineLabel: "Opened version",
1826
+ baselineLabel: t("app.client.fileEditorController.openedVersion"),
1827
1827
  saving: saves.has(path),
1828
1828
  conflict: true,
1829
1829
  latest: null,
1830
- error: `The current project file could not be read. ${message$3(cause)}`,
1831
- status: "Your unsaved draft is available below. Download it, or restore the project file and compare with its latest contents before saving."
1830
+ error: t("app.client.fileEditorController.projectFileUnreadable", { detail: message$3(cause) }),
1831
+ status: t("app.client.fileEditorController.unsavedDraftAvailable")
1832
1832
  };
1833
1833
  else openError = message$3(cause);
1834
1834
  }
@@ -1874,7 +1874,7 @@ function createFileEditorController(options) {
1874
1874
  expectedRevision: draft.baseRevision,
1875
1875
  contents: draft.contents
1876
1876
  }, options.signal);
1877
- 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.");
1877
+ if (result.path !== draft.path || result.previousRevision !== draft.baseRevision) throw new Error(t("app.client.fileEditorController.saveRevisionMismatch"));
1878
1878
  let next = options.drafts.acknowledge(draft.path, draft.contents, result.revision);
1879
1879
  if (state?.draft.path === draft.path && next) {
1880
1880
  state = {
@@ -1883,9 +1883,9 @@ function createFileEditorController(options) {
1883
1883
  status: "",
1884
1884
  error: ""
1885
1885
  };
1886
- if (result.refresh === "failed") toast.error("Saved, but the file browser could not refresh", { description: "Refresh it before continuing." });
1887
- else if (draftDirty(next)) toast.success("Saved to your project", { description: "Newer edits are still unsaved." });
1888
- else toast.success("Saved to your project");
1886
+ if (result.refresh === "failed") toast.error(t("app.client.fileEditorController.savedRefreshFailed"), { description: t("app.client.fileEditorController.refreshBeforeContinuing") });
1887
+ else if (draftDirty(next)) toast.success(t("app.client.fileEditorController.savedToProject"), { description: t("app.client.fileEditorController.newerEditsUnsaved") });
1888
+ else toast.success(t("app.client.fileEditorController.savedToProject"));
1889
1889
  }
1890
1890
  options.onSaved();
1891
1891
  } catch (cause) {
@@ -1929,11 +1929,11 @@ function createFileEditorController(options) {
1929
1929
  contents: current.contents
1930
1930
  },
1931
1931
  mode: "diff",
1932
- baselineLabel: "Latest file",
1932
+ baselineLabel: t("app.client.fileEditorController.latestFile"),
1933
1933
  latest: document,
1934
1934
  conflict: true,
1935
1935
  error: "",
1936
- status: "Your draft is on the new side. No file is changed until you review it and save."
1936
+ status: t("app.client.fileEditorController.draftOnNewSide")
1937
1937
  };
1938
1938
  } catch (cause) {
1939
1939
  if (!signal.aborted && state) state = {
@@ -1954,7 +1954,7 @@ function createFileEditorController(options) {
1954
1954
  latest: null,
1955
1955
  conflict: false,
1956
1956
  error: "",
1957
- status: "Reviewed against the latest file. Save when your merged draft is ready."
1957
+ status: t("app.client.fileEditorController.reviewedAgainstLatest")
1958
1958
  };
1959
1959
  changed();
1960
1960
  }
@@ -1987,7 +1987,7 @@ function createFileEditorController(options) {
1987
1987
  guard: {
1988
1988
  async beforeApply(intent, plan, signal) {
1989
1989
  signal.throwIfAborted();
1990
- 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.";
1990
+ 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");
1991
1991
  return options.drafts.beforeMutation(intent, plan);
1992
1992
  },
1993
1993
  afterApply(intent, result) {
@@ -2001,7 +2001,7 @@ function createFileEditorController(options) {
2001
2001
  //#endregion
2002
2002
  //#region app/client/file-review-actions-controller.ts
2003
2003
  function message$2(cause) {
2004
- return cause instanceof Error ? cause.message : "The review action could not be completed. Refresh and inspect retained undo before trying again.";
2004
+ return cause instanceof Error ? cause.message : t("app.client.fileReviewActionsController.actionCouldNotBeCompleted");
2005
2005
  }
2006
2006
  /**
2007
2007
  * Runs review actions, such as staging or discarding changes, one at a time: plans one, applies
@@ -2045,7 +2045,7 @@ function createFileReviewActionsController(options) {
2045
2045
  if (intent.kind !== "undo") options.guard([intent.path], intent.kind === "discard" ? "worktree" : "index");
2046
2046
  let plan = await options.prepare(intent, signal);
2047
2047
  signal.throwIfAborted();
2048
- 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.");
2048
+ if (plan.kind !== intent.kind || intent.kind !== "undo" && !plan.paths.includes(intent.path)) throw new Error(t("app.client.fileReviewActionsController.preparedReviewMismatch"));
2049
2049
  options.guard(plan.paths, plan.target);
2050
2050
  if (turn === generation) state = {
2051
2051
  phase: "ready",
@@ -2082,7 +2082,7 @@ function createFileReviewActionsController(options) {
2082
2082
  revision: plan.revision
2083
2083
  }, signal);
2084
2084
  signal.throwIfAborted();
2085
- 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.");
2085
+ 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"));
2086
2086
  if (turn === generation) {
2087
2087
  let refreshError = "";
2088
2088
  try {
@@ -2211,11 +2211,11 @@ function reviewCommentSources(document, diffs, source) {
2211
2211
  * @returns The code anchor.
2212
2212
  */
2213
2213
  function captureCodeSelection(selection, sources, expectedRevision) {
2214
- if (selection.endSide && selection.endSide !== selection.side) throw new Error("Select consecutive lines on one side before commenting.");
2214
+ if (selection.endSide && selection.endSide !== selection.side) throw new Error(t("app.client.projectCodeComments.selectConsecutiveLines"));
2215
2215
  let side = selection.side === "deletions" ? "old" : "new";
2216
2216
  let source = sources.find((source) => (source.viewPath ?? source.path) === selection.path && (!source.side || source.side === side));
2217
- if (!source || source.contents === null || !source.revision) throw new Error("Comments require readable text with an exact source revision.");
2218
- if (expectedRevision && source.revision !== expectedRevision) throw new Error("Those lines belong to an older source. Select them again before commenting.");
2217
+ if (!source || source.contents === null || !source.revision) throw new Error(t("app.client.projectCodeComments.commentsRequireReadableText"));
2218
+ if (expectedRevision && source.revision !== expectedRevision) throw new Error(t("app.client.projectCodeComments.linesBelongToOlderSource"));
2219
2219
  return createCodeAnchor({
2220
2220
  path: source.path,
2221
2221
  revision: source.revision,
@@ -2263,7 +2263,7 @@ function createFileSearchController(options) {
2263
2263
  function notify() {
2264
2264
  if (!options.signal.aborted) options.onChange();
2265
2265
  }
2266
- function cancel(notice = "Search cancelled.") {
2266
+ function cancel(notice = t("app.client.fileSearchController.searchCancelled")) {
2267
2267
  active?.abort();
2268
2268
  active = void 0;
2269
2269
  state = {
@@ -2295,7 +2295,7 @@ function createFileSearchController(options) {
2295
2295
  try {
2296
2296
  let query = parseFileSearchQuery(input);
2297
2297
  let result = await options.request(query, signal);
2298
- if (fileSearchParameters(result.query).toString() !== fileSearchParameters(query).toString()) throw new FilesRequestError("The service returned results for a different search.", "response");
2298
+ if (fileSearchParameters(result.query).toString() !== fileSearchParameters(query).toString()) throw new FilesRequestError(t("app.client.fileSearchController.differentSearchResults"), "response");
2299
2299
  if (signal.aborted || active !== owner) return;
2300
2300
  state = {
2301
2301
  loading: false,
@@ -2308,7 +2308,7 @@ function createFileSearchController(options) {
2308
2308
  state = {
2309
2309
  loading: false,
2310
2310
  result: null,
2311
- error: cause instanceof Error ? cause.message : "Could not search this project.",
2311
+ error: cause instanceof Error ? cause.message : t("app.client.fileSearchController.searchFailed"),
2312
2312
  notice: ""
2313
2313
  };
2314
2314
  } finally {
@@ -2328,7 +2328,7 @@ function createFileSearchController(options) {
2328
2328
  * @returns A verified source-review position, or an explicit outdated-result error.
2329
2329
  */
2330
2330
  function searchMatchPosition(document, match) {
2331
- 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.");
2331
+ 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"));
2332
2332
  let position = (offset) => {
2333
2333
  let line = 1, start = 0;
2334
2334
  for (let ending of document.contents.matchAll(/\r\n|\r|\n/g)) {
@@ -2343,7 +2343,7 @@ function searchMatchPosition(document, match) {
2343
2343
  };
2344
2344
  };
2345
2345
  let before = position(match.start), end = position(match.end);
2346
- 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.");
2346
+ 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"));
2347
2347
  return {
2348
2348
  page: 0,
2349
2349
  scroll: 0,
@@ -2358,7 +2358,7 @@ function searchMatchPosition(document, match) {
2358
2358
  //#endregion
2359
2359
  //#region app/client/project-file-review.tsx
2360
2360
  function message$1(cause) {
2361
- return cause instanceof Error ? cause.message : "Could not read the project.";
2361
+ return cause instanceof Error ? cause.message : t("app.client.projectFileReview.readProjectError");
2362
2362
  }
2363
2363
  /**
2364
2364
  * Owns validated review requests, the worker pool and bounded project view persistence.
@@ -2517,7 +2517,7 @@ function ProjectFileReview(handle) {
2517
2517
  try {
2518
2518
  if (immutable) {
2519
2519
  let next = await requestFiles(`${routes.fileReviewApi.source.href()}?${new URLSearchParams(immutable)}`, project.token, signal, parseReviewSource);
2520
- if (next.path !== immutable.path || next.commit !== immutable.commit) throw new FilesRequestError("The source response belongs to another commit or file.", "response");
2520
+ if (next.path !== immutable.path || next.commit !== immutable.commit) throw new FilesRequestError(t("app.client.projectFileReview.sourceResponseCommitMismatch"), "response");
2521
2521
  if (!signal.aborted) {
2522
2522
  source = next;
2523
2523
  displayedKey = nextDisplay;
@@ -2532,7 +2532,7 @@ function ProjectFileReview(handle) {
2532
2532
  changed();
2533
2533
  }
2534
2534
  let next = await requestFiles(`${routes.filesApi.read.href()}?${new URLSearchParams({ path: selected })}`, project.token, signal, parseFileDocument);
2535
- if (next.entry.path !== selected) throw new FilesRequestError("The source response belongs to another file.", "response");
2535
+ if (next.entry.path !== selected) throw new FilesRequestError(t("app.client.projectFileReview.sourceResponseFileMismatch"), "response");
2536
2536
  if (store) storeDocument(store, next).catch(() => {});
2537
2537
  if (!signal.aborted) {
2538
2538
  document = document?.revision === next.revision && document.entry.path === next.entry.path ? document : next;
@@ -2543,7 +2543,10 @@ function ProjectFileReview(handle) {
2543
2543
  pendingSearch = void 0;
2544
2544
  try {
2545
2545
  savePosition(searchMatchPosition(next, match), true);
2546
- searchNotice = `Search match at ${match.startLine}:${match.startColumn}. Any unsaved draft is retained.`;
2546
+ searchNotice = t("app.client.projectFileReview.searchMatchNotice", {
2547
+ line: match.startLine,
2548
+ column: match.startColumn
2549
+ });
2547
2550
  } catch (cause) {
2548
2551
  searchNotice = message$1(cause);
2549
2552
  }
@@ -2552,7 +2555,7 @@ function ProjectFileReview(handle) {
2552
2555
  }
2553
2556
  } else for (let attempt = 0; attempt < 2; attempt++) {
2554
2557
  let next = await requestFiles(`${routes.fileReviewApi.compare.href()}?${comparisonQuery(comparison)}`, project.token, signal, parseReviewSnapshot);
2555
- if (next.comparison.mode !== comparison.mode) throw new FilesRequestError("The service returned a different comparison.", "response");
2558
+ if (next.comparison.mode !== comparison.mode) throw new FilesRequestError(t("app.client.projectFileReview.comparisonMismatch"), "response");
2556
2559
  let page = Math.min(position().page, Math.max(0, Math.ceil(next.entries.length / 20) - 1));
2557
2560
  let restoredPath = position().selection?.path;
2558
2561
  if (restoredPath && settings.multiple && next.entries.some((entry) => entry.path === restoredPath)) page = Math.floor(next.entries.findIndex((entry) => entry.path === restoredPath) / 20);
@@ -2612,7 +2615,7 @@ function ProjectFileReview(handle) {
2612
2615
  changed();
2613
2616
  try {
2614
2617
  let next = await requestFiles(`${routes.fileReviewApi.history.href()}?${query}`, project.token, signal, parseReviewHistory);
2615
- if (next.path !== path) throw new FilesRequestError("The history response belongs to another path.", "response");
2618
+ if (next.path !== path) throw new FilesRequestError(t("app.client.projectFileReview.historyPathMismatch"), "response");
2616
2619
  if (!signal.aborted) history = next;
2617
2620
  } catch (cause) {
2618
2621
  if (!signal.aborted) historyError = message$1(cause);
@@ -2752,7 +2755,7 @@ function ProjectFileReview(handle) {
2752
2755
  commentError = "";
2753
2756
  try {
2754
2757
  let editing = editor?.state;
2755
- 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.");
2758
+ if (fromEditor && editing && (draftDirty(editing.draft) || editing.conflict || editing.saving)) throw new Error(t("app.client.projectFileReview.saveDraftBeforeComment"));
2756
2759
  draftAnchor = captureCodeSelection(selection, fromEditor && editing && selection.side !== "deletions" ? [{
2757
2760
  path: editing.draft.path,
2758
2761
  revision: editing.draft.baseRevision,
@@ -2766,8 +2769,8 @@ function ProjectFileReview(handle) {
2766
2769
  changed();
2767
2770
  }
2768
2771
  async function createComment(body) {
2769
- if (!draftAnchor) throw new Error("Select code lines again before commenting.");
2770
- if (editor?.state && draftDirty(editor.state.draft)) throw new Error("Save your draft before sending this code comment. The message is kept.");
2772
+ if (!draftAnchor) throw new Error(t("app.client.projectFileReview.selectCodeLinesAgain"));
2773
+ if (editor?.state && draftDirty(editor.state.draft)) throw new Error(t("app.client.projectFileReview.saveDraftBeforeSendingComment"));
2771
2774
  selectedThread = (await handle.props.comments.write({
2772
2775
  anchor: draftAnchor,
2773
2776
  body
@@ -2805,11 +2808,11 @@ function ProjectFileReview(handle) {
2805
2808
  apply: (body, signal) => requestFiles(routes.fileReviewActionsApi.apply.href(), project.token, signal, parseReviewActionResult, fetch, { body }),
2806
2809
  recoveries: (signal) => requestFiles(routes.fileReviewActionsApi.recoveries.href(), project.token, signal, parseReviewActionRecoveries),
2807
2810
  guard(paths) {
2808
- if (editor?.state?.saving) throw new Error("Wait for the current save to finish before reviewing changes.");
2811
+ if (editor?.state?.saving) throw new Error(t("app.client.projectFileReview.waitForSaveBeforeReview"));
2809
2812
  if (paths.some((path) => {
2810
2813
  let draft = drafts?.get(path);
2811
2814
  return draft && draftDirty(draft);
2812
- })) throw new Error("Save or explicitly discard the unsaved draft for this file before applying review actions. Your draft has been kept.");
2815
+ })) throw new Error(t("app.client.projectFileReview.saveOrDiscardDraftBeforeActions"));
2813
2816
  },
2814
2817
  async onApplied(result) {
2815
2818
  if (result.target === "worktree") editor?.close();
@@ -2830,7 +2833,7 @@ function ProjectFileReview(handle) {
2830
2833
  if (handle.signal.aborted || reading === active || !reading) break;
2831
2834
  current = reading;
2832
2835
  }
2833
- if (error) throw new Error(`The write completed, but the review could not refresh: ${error}`);
2836
+ if (error) throw new Error(t("app.client.projectFileReview.writeCompletedRefreshError", { error }));
2834
2837
  },
2835
2838
  onChange: changed
2836
2839
  });
@@ -2966,7 +2969,7 @@ function ProjectFileReview(handle) {
2966
2969
  handle.queueTask(() => {
2967
2970
  owned.pool.setRenderOptions({ lineDiffType: session.view.highlights }).catch((cause) => {
2968
2971
  if (!handle.signal.aborted) {
2969
- error = `Could not update highlighting. ${message$1(cause)}`;
2972
+ error = t("app.client.projectFileReview.updateHighlightingError", { error: message$1(cause) });
2970
2973
  changed();
2971
2974
  }
2972
2975
  });
@@ -3065,7 +3068,7 @@ function ProjectFileReview(handle) {
3065
3068
  tone: "muted",
3066
3069
  role: "status",
3067
3070
  class: "shrink-0 border-b border-line px-3 py-1.5",
3068
- children: commentsUnavailable
3071
+ children: t("app.client.projectFileReview.commentsUnavailable")
3069
3072
  }) : null,
3070
3073
  searchNotice ? /* @__PURE__ */ jsx(Text, {
3071
3074
  as: "p",
@@ -3086,7 +3089,7 @@ function ProjectFileReview(handle) {
3086
3089
  variant: "ui-caption",
3087
3090
  role: "status",
3088
3091
  class: "sr-only",
3089
- children: "Reading current file for editing…"
3092
+ children: t("app.client.projectFileReview.readingFileForEditing")
3090
3093
  }) : null,
3091
3094
  editing && editor && merge?.path === editing.draft.path ? /* @__PURE__ */ jsx(FileConflictPanel, {
3092
3095
  chrome: handle.props.chrome,
@@ -3196,7 +3199,7 @@ function ProjectFileReview(handle) {
3196
3199
  onResolve: (diff) => {
3197
3200
  resolveConflict(diff);
3198
3201
  },
3199
- editLabel: handle.props.path && drafts?.get(handle.props.path) && draftDirty(drafts.get(handle.props.path)) ? "Resume draft" : "Edit file",
3202
+ 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"),
3200
3203
  onChange: changeView,
3201
3204
  onPosition: savePosition,
3202
3205
  onSelect: selectPath,
@@ -3307,7 +3310,7 @@ function ProjectFileReview(handle) {
3307
3310
  //#endregion
3308
3311
  //#region app/client/project-files.tsx
3309
3312
  function message(error) {
3310
- return error instanceof Error ? error.message : "Could not load project files.";
3313
+ return error instanceof Error ? error.message : t("app.client.projectFiles.couldNotLoadProjectFiles");
3311
3314
  }
3312
3315
  function requestProjectFiles(token, view, signal) {
3313
3316
  let params = new URLSearchParams({
@@ -3460,8 +3463,8 @@ function ProjectFiles(handle) {
3460
3463
  async onApplied(intent, result) {
3461
3464
  view = mutationFileView(view, intent.kind, result);
3462
3465
  let count = result.items.filter((item) => item.status === "applied").length;
3463
- if (count) toast.success(`${count} file action${count === 1 ? "" : "s"} completed`, { description: result.recoveryId ? "Original contents are available in Recovery." : void 0 });
3464
- else toast.error("The file action could not be applied", { description: "Your project was refreshed." });
3466
+ if (count) toast.success(t("app.client.projectFiles.fileActionsCompleted", { count }), { description: result.recoveryId ? t("app.client.projectFiles.originalContentsAvailableInRecovery") : void 0 });
3467
+ else toast.error(t("app.client.projectFiles.fileActionCouldNotBeApplied"), { description: t("app.client.projectFiles.projectWasRefreshed") });
3465
3468
  location(true);
3466
3469
  persist();
3467
3470
  pendingContentRefresh = true;
@@ -3524,7 +3527,7 @@ function ProjectFiles(handle) {
3524
3527
  function closeTab(path) {
3525
3528
  if (dirty.includes(path)) {
3526
3529
  select(path, [path]);
3527
- toast("Save or discard this draft to close its tab");
3530
+ toast(t("app.client.projectFiles.saveOrDiscardDraftToCloseTab"));
3528
3531
  changed();
3529
3532
  return;
3530
3533
  }
@@ -3636,7 +3639,7 @@ function ProjectFiles(handle) {
3636
3639
  }, { once: true });
3637
3640
  });
3638
3641
  function foreignNotice(foreign) {
3639
- notice = foreign ? "This file link belongs to another project. Choose a file from this project to continue." : "";
3642
+ notice = foreign ? t("app.client.projectFiles.fileLinkBelongsToAnotherProject") : "";
3640
3643
  }
3641
3644
  return () => {
3642
3645
  if (renderedHref !== handle.props.href) {