@cerbur/clutch-dsh-worktree 0.1.3 → 0.1.4

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 (73) hide show
  1. package/README.md +247 -107
  2. package/README.zh.md +297 -0
  3. package/lib/client/WorktreeSurface.d.ts +2 -30
  4. package/lib/client/WorktreeSurface.d.ts.map +1 -1
  5. package/lib/client/WorktreeSurface.js +31 -318
  6. package/lib/client/WorktreeSurface.js.map +1 -1
  7. package/lib/client/worktree-surface-dialogs.d.ts +7 -0
  8. package/lib/client/worktree-surface-dialogs.d.ts.map +1 -0
  9. package/lib/client/worktree-surface-dialogs.js +86 -0
  10. package/lib/client/worktree-surface-dialogs.js.map +1 -0
  11. package/lib/client/worktree-surface-rows.d.ts +9 -0
  12. package/lib/client/worktree-surface-rows.d.ts.map +1 -0
  13. package/lib/client/worktree-surface-rows.js +223 -0
  14. package/lib/client/worktree-surface-rows.js.map +1 -0
  15. package/lib/client/worktree-surface-selectors.d.ts +9 -0
  16. package/lib/client/worktree-surface-selectors.d.ts.map +1 -0
  17. package/lib/client/worktree-surface-selectors.js +28 -0
  18. package/lib/client/worktree-surface-selectors.js.map +1 -0
  19. package/lib/client/worktree-surface-types.d.ts +233 -0
  20. package/lib/client/worktree-surface-types.d.ts.map +1 -0
  21. package/lib/client/worktree-surface-types.js +2 -0
  22. package/lib/client/worktree-surface-types.js.map +1 -0
  23. package/lib/client/worktree-view-actions.d.ts +34 -0
  24. package/lib/client/worktree-view-actions.d.ts.map +1 -0
  25. package/lib/client/worktree-view-actions.js +69 -0
  26. package/lib/client/worktree-view-actions.js.map +1 -0
  27. package/lib/client/worktree-view-errors.d.ts +20 -0
  28. package/lib/client/worktree-view-errors.d.ts.map +1 -0
  29. package/lib/client/worktree-view-errors.js +65 -0
  30. package/lib/client/worktree-view-errors.js.map +1 -0
  31. package/lib/client/worktree-view-read.d.ts +48 -0
  32. package/lib/client/worktree-view-read.d.ts.map +1 -0
  33. package/lib/client/worktree-view-read.js +134 -0
  34. package/lib/client/worktree-view-read.js.map +1 -0
  35. package/lib/client/worktree-view.d.ts +4 -97
  36. package/lib/client/worktree-view.d.ts.map +1 -1
  37. package/lib/client/worktree-view.js +4 -263
  38. package/lib/client/worktree-view.js.map +1 -1
  39. package/lib/client.js +404 -324
  40. package/lib/client.js.map +1 -1
  41. package/lib/manage/manager-context.d.ts +11 -0
  42. package/lib/manage/manager-context.d.ts.map +1 -0
  43. package/lib/manage/manager-context.js +2 -0
  44. package/lib/manage/manager-context.js.map +1 -0
  45. package/lib/manage/manager-sessions.d.ts +18 -0
  46. package/lib/manage/manager-sessions.d.ts.map +1 -0
  47. package/lib/manage/manager-sessions.js +91 -0
  48. package/lib/manage/manager-sessions.js.map +1 -0
  49. package/lib/manage/manager-support.d.ts +18 -0
  50. package/lib/manage/manager-support.d.ts.map +1 -0
  51. package/lib/manage/manager-support.js +174 -0
  52. package/lib/manage/manager-support.js.map +1 -0
  53. package/lib/manage/manager-worktrees.d.ts +27 -0
  54. package/lib/manage/manager-worktrees.d.ts.map +1 -0
  55. package/lib/manage/manager-worktrees.js +259 -0
  56. package/lib/manage/manager-worktrees.js.map +1 -0
  57. package/lib/manage/manager.d.ts +23 -65
  58. package/lib/manage/manager.d.ts.map +1 -1
  59. package/lib/manage/manager.js +32 -565
  60. package/lib/manage/manager.js.map +1 -1
  61. package/lib/provider/sidecar-persistence.d.ts +18 -0
  62. package/lib/provider/sidecar-persistence.d.ts.map +1 -0
  63. package/lib/provider/sidecar-persistence.js +162 -0
  64. package/lib/provider/sidecar-persistence.js.map +1 -0
  65. package/lib/provider/sidecar-schema.d.ts +6 -0
  66. package/lib/provider/sidecar-schema.d.ts.map +1 -0
  67. package/lib/provider/sidecar-schema.js +123 -0
  68. package/lib/provider/sidecar-schema.js.map +1 -0
  69. package/lib/provider/sidecar.d.ts +10 -67
  70. package/lib/provider/sidecar.d.ts.map +1 -1
  71. package/lib/provider/sidecar.js +21 -346
  72. package/lib/provider/sidecar.js.map +1 -1
  73. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -811,7 +811,7 @@ window.__ModuleLoader__.load({
811
811
  return (summary?.displayTitle ?? sessionId).toLocaleLowerCase().includes(normalizedQuery);
812
812
  }
813
813
  //#endregion
814
- //#region lib/client/worktree-view.js
814
+ //#region lib/client/worktree-view-errors.js
815
815
  /** A DSH Session exists even when the external Worktree binding needs repair. */
816
816
  var WorktreeSessionBindingError = class extends Error {
817
817
  code;
@@ -832,6 +832,98 @@ window.__ModuleLoader__.load({
832
832
  this.cause = cause;
833
833
  }
834
834
  };
835
+ function recordDetails(value) {
836
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
837
+ const details = value.details;
838
+ if (typeof details !== "object" || details === null || Array.isArray(details)) return void 0;
839
+ return details;
840
+ }
841
+ /** Convert any adapter/Gateway failure into renderable, retry-aware UI data. */
842
+ function toWorktreeViewError(error) {
843
+ if (typeof error === "object" && error !== null) {
844
+ const candidate = error;
845
+ const details = error instanceof WorktreeSessionBindingError ? {
846
+ ...recordDetails(error) ?? {},
847
+ sessionId: error.sessionId
848
+ } : recordDetails(error);
849
+ return {
850
+ code: typeof candidate.code === "string" ? candidate.code : "WORKTREE_VIEW_FAILED",
851
+ message: typeof candidate.message === "string" ? candidate.message : "",
852
+ retryable: candidate.retryable !== false,
853
+ ...details === void 0 ? {} : { details }
854
+ };
855
+ }
856
+ return {
857
+ code: "WORKTREE_VIEW_FAILED",
858
+ message: "",
859
+ retryable: true
860
+ };
861
+ }
862
+ /** Keep ordering mutation/refresh failures reachable from the surface Retry control. */
863
+ function toRetryableWorktreeOrderError(error) {
864
+ return {
865
+ ...toWorktreeViewError(error),
866
+ retryable: true
867
+ };
868
+ }
869
+ //#endregion
870
+ //#region lib/client/worktree-view-actions.js
871
+ function browserRandomUuid() {
872
+ const randomUUID = globalThis.crypto?.randomUUID;
873
+ if (typeof randomUUID === "function") return randomUUID.call(globalThis.crypto);
874
+ return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
875
+ }
876
+ /** Generate a short, editable default branch name without colliding with known local names. */
877
+ function createDefaultWorktreeName(existingNames, randomUuid = browserRandomUuid) {
878
+ const usedNames = new Set([...existingNames].map((name) => name.trim()));
879
+ for (let attempt = 0; attempt < 100; attempt += 1) {
880
+ const suffix = randomUuid().replace(/[^a-z0-9]/gi, "").slice(0, 8).toLowerCase();
881
+ if (suffix.length < 8) continue;
882
+ const candidate = `dsh/${suffix}`;
883
+ if (!usedNames.has(candidate)) return candidate;
884
+ }
885
+ throw new Error("Unable to generate an available default Worktree name.");
886
+ }
887
+ /** Resolve a row-half drop target to native-style optional before-anchor semantics. */
888
+ function resolveWorktreeMove(worktreeIds, sourceWorktreeId, targetWorktreeId, half) {
889
+ const sourceIndex = worktreeIds.indexOf(sourceWorktreeId);
890
+ const targetIndex = worktreeIds.indexOf(targetWorktreeId);
891
+ if (sourceIndex === -1 || targetIndex === -1) return void 0;
892
+ const beforeWorktreeId = half === "before" ? targetWorktreeId : worktreeIds[targetIndex + 1];
893
+ if (beforeWorktreeId === sourceWorktreeId) return void 0;
894
+ const anchorIndex = beforeWorktreeId === void 0 ? worktreeIds.length : worktreeIds.indexOf(beforeWorktreeId);
895
+ if (anchorIndex === sourceIndex || anchorIndex === sourceIndex + 1) return void 0;
896
+ return beforeWorktreeId === void 0 ? {} : { beforeWorktreeId };
897
+ }
898
+ /** Keep mutation routing in the browser Consumer while leaving wire details to the adapter. */
899
+ async function executeWorktreeAction(manager, action) {
900
+ if (action.type === "createWorktree") return manager.createWorktree(action.input);
901
+ if (action.type === "removeWorktree") {
902
+ await manager.removeWorktree(action.input);
903
+ return;
904
+ }
905
+ }
906
+ /**
907
+ * Create a normal DSH Session at a Worktree cwd, then add the external binding.
908
+ * A binding failure deliberately leaves the DSH-created Session intact.
909
+ */
910
+ async function createSessionForWorktree(input) {
911
+ const sessionId = await input.createSession({ cwd: input.cwd });
912
+ try {
913
+ await input.manager.bindSession({
914
+ workspaceId: input.workspaceId,
915
+ worktreeId: input.worktreeId,
916
+ sessionId
917
+ });
918
+ } catch (error) {
919
+ throw new WorktreeSessionBindingError(sessionId, error);
920
+ }
921
+ input.beforeOpen?.(sessionId);
922
+ input.openSession(sessionId);
923
+ return sessionId;
924
+ }
925
+ //#endregion
926
+ //#region lib/client/worktree-view-read.js
835
927
  /** Reuse the previous ID snapshot when DSH republishes equivalent Workspace items. */
836
928
  function stableWorkspaceIds(previous, next) {
837
929
  if (previous.length !== next.length) return next;
@@ -895,26 +987,6 @@ window.__ModuleLoader__.load({
895
987
  case "ready": return [];
896
988
  }
897
989
  }
898
- function browserRandomUuid() {
899
- const randomUUID = globalThis.crypto?.randomUUID;
900
- if (typeof randomUUID === "function") return randomUUID.call(globalThis.crypto);
901
- return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
902
- }
903
- /** Generate a short, editable default branch name without colliding with known local names. */
904
- function createDefaultWorktreeName(existingNames, randomUuid = browserRandomUuid) {
905
- const usedNames = new Set([...existingNames].map((name) => name.trim()));
906
- for (let attempt = 0; attempt < 100; attempt += 1) {
907
- const suffix = randomUuid().replace(/[^a-z0-9]/gi, "").slice(0, 8).toLowerCase();
908
- if (suffix.length < 8) continue;
909
- const candidate = `dsh/${suffix}`;
910
- if (!usedNames.has(candidate)) return candidate;
911
- }
912
- throw new Error("Unable to generate an available default Worktree name.");
913
- }
914
- /** Hide DSH-archived Sessions from the browser-local Worktree projection. */
915
- function filterArchivedSessionIds(sessionIds, archivedSessionIds) {
916
- return sessionIds.filter((sessionId) => !archivedSessionIds.includes(sessionId));
917
- }
918
990
  function readinessFromBranchError(error) {
919
991
  const viewError = toWorktreeViewError(error);
920
992
  if (viewError.code === "WORKSPACE_NOT_GIT_REPOSITORY") return {
@@ -961,77 +1033,9 @@ window.__ModuleLoader__.load({
961
1033
  if (options.invalidateContext !== false) await options.invalidateWorktreeContext?.();
962
1034
  return views;
963
1035
  }
964
- /** Resolve a row-half drop target to native-style optional before-anchor semantics. */
965
- function resolveWorktreeMove(worktreeIds, sourceWorktreeId, targetWorktreeId, half) {
966
- const sourceIndex = worktreeIds.indexOf(sourceWorktreeId);
967
- const targetIndex = worktreeIds.indexOf(targetWorktreeId);
968
- if (sourceIndex === -1 || targetIndex === -1) return void 0;
969
- const beforeWorktreeId = half === "before" ? targetWorktreeId : worktreeIds[targetIndex + 1];
970
- if (beforeWorktreeId === sourceWorktreeId) return void 0;
971
- const anchorIndex = beforeWorktreeId === void 0 ? worktreeIds.length : worktreeIds.indexOf(beforeWorktreeId);
972
- if (anchorIndex === sourceIndex || anchorIndex === sourceIndex + 1) return void 0;
973
- return beforeWorktreeId === void 0 ? {} : { beforeWorktreeId };
974
- }
975
- /** Keep mutation routing in the browser Consumer while leaving wire details to the adapter. */
976
- async function executeWorktreeAction(manager, action) {
977
- if (action.type === "createWorktree") return manager.createWorktree(action.input);
978
- if (action.type === "removeWorktree") {
979
- await manager.removeWorktree(action.input);
980
- return;
981
- }
982
- }
983
- /**
984
- * Create a normal DSH Session at a Worktree cwd, then add the external binding.
985
- * A binding failure deliberately leaves the DSH-created Session intact.
986
- */
987
- async function createSessionForWorktree(input) {
988
- const sessionId = await input.createSession({ cwd: input.cwd });
989
- try {
990
- await input.manager.bindSession({
991
- workspaceId: input.workspaceId,
992
- worktreeId: input.worktreeId,
993
- sessionId
994
- });
995
- } catch (error) {
996
- throw new WorktreeSessionBindingError(sessionId, error);
997
- }
998
- input.beforeOpen?.(sessionId);
999
- input.openSession(sessionId);
1000
- return sessionId;
1001
- }
1002
- function recordDetails(value) {
1003
- if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1004
- const details = value.details;
1005
- if (typeof details !== "object" || details === null || Array.isArray(details)) return void 0;
1006
- return details;
1007
- }
1008
- /** Convert any adapter/Gateway failure into renderable, retry-aware UI data. */
1009
- function toWorktreeViewError(error) {
1010
- if (typeof error === "object" && error !== null) {
1011
- const candidate = error;
1012
- const details = error instanceof WorktreeSessionBindingError ? {
1013
- ...recordDetails(error) ?? {},
1014
- sessionId: error.sessionId
1015
- } : recordDetails(error);
1016
- return {
1017
- code: typeof candidate.code === "string" ? candidate.code : "WORKTREE_VIEW_FAILED",
1018
- message: typeof candidate.message === "string" ? candidate.message : "",
1019
- retryable: candidate.retryable !== false,
1020
- ...details === void 0 ? {} : { details }
1021
- };
1022
- }
1023
- return {
1024
- code: "WORKTREE_VIEW_FAILED",
1025
- message: "",
1026
- retryable: true
1027
- };
1028
- }
1029
- /** Keep ordering mutation/refresh failures reachable from the surface Retry control. */
1030
- function toRetryableWorktreeOrderError(error) {
1031
- return {
1032
- ...toWorktreeViewError(error),
1033
- retryable: true
1034
- };
1036
+ /** Hide DSH-archived Sessions from the browser-local Worktree projection. */
1037
+ function filterArchivedSessionIds(sessionIds, archivedSessionIds) {
1038
+ return sessionIds.filter((sessionId) => !archivedSessionIds.includes(sessionId));
1035
1039
  }
1036
1040
  //#endregion
1037
1041
  //#region lib/client/worktree-session.js
@@ -1186,7 +1190,7 @@ window.__ModuleLoader__.load({
1186
1190
  };
1187
1191
  }
1188
1192
  //#endregion
1189
- //#region lib/client/WorktreeSurface.js
1193
+ //#region lib/client/worktree-surface-dialogs.js
1190
1194
  function worktreeSetupMessage(status, t) {
1191
1195
  switch (status) {
1192
1196
  case "noRepository": return t("worktree.setup.noRepository");
@@ -1194,32 +1198,232 @@ window.__ModuleLoader__.load({
1194
1198
  case "noLocalBranch": return t("worktree.setup.noLocalBranch");
1195
1199
  }
1196
1200
  }
1197
- function toNativeWorktreeViewError(error) {
1198
- const viewError = toWorktreeViewError(error);
1199
- if (typeof error === "object" && error !== null) return viewError;
1200
- return {
1201
- ...viewError,
1202
- message: String(error)
1203
- };
1201
+ function WorktreeSessionRenameDialog({ t, target, draft, pending, error, onClose, onDraftChange, onSubmit }) {
1202
+ return (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1203
+ open: target !== void 0,
1204
+ onClose,
1205
+ closeLabel: t("dialog.close"),
1206
+ title: t("session.rename"),
1207
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1208
+ variant: "outline",
1209
+ disabled: pending,
1210
+ onClick: onClose,
1211
+ children: t("dialog.cancel")
1212
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1213
+ variant: "primary",
1214
+ disabled: pending || draft.trim().length === 0,
1215
+ onClick: () => {
1216
+ onSubmit();
1217
+ },
1218
+ children: t("dialog.rename")
1219
+ })] }),
1220
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1221
+ className: worktree_css_default.renameInput,
1222
+ value: draft,
1223
+ "aria-label": t("session.name"),
1224
+ autoFocus: true,
1225
+ disabled: pending,
1226
+ onFocus: (event) => {
1227
+ event.currentTarget.select();
1228
+ },
1229
+ onChange: (event) => {
1230
+ onDraftChange(event.currentTarget.value);
1231
+ },
1232
+ onKeyDown: (event) => {
1233
+ if (event.key === "Enter" && !event.nativeEvent.isComposing) {
1234
+ event.preventDefault();
1235
+ onSubmit();
1236
+ }
1237
+ }
1238
+ }), error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
1239
+ className: worktree_css_default.renameError,
1240
+ role: "alert",
1241
+ children: formatWorktreeViewError(error, t)
1242
+ })]
1243
+ });
1204
1244
  }
1205
- const EMPTY_READ_STATE = {
1206
- status: "idle",
1207
- views: []
1208
- };
1209
- function useStableWorkspaceIds(workspaces) {
1210
- const next = workspaces.map((workspace) => workspace.workspaceId);
1211
- const previousRef = (0, react.useRef)([]);
1212
- const stable = stableWorkspaceIds(previousRef.current, next);
1213
- if (stable !== previousRef.current) previousRef.current = stable;
1214
- return previousRef.current;
1245
+ function WorktreeWorkspaceRenameDialog({ t, target, draft, pending, duplicate, error, onClose, onDraftChange, onSubmit }) {
1246
+ if (target === void 0) return null;
1247
+ return (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1248
+ open: true,
1249
+ onClose,
1250
+ closeLabel: t("dialog.closeWorkspaceRename"),
1251
+ title: t("workspace.renameTitle"),
1252
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1253
+ variant: "outline",
1254
+ disabled: pending,
1255
+ onClick: onClose,
1256
+ children: t("dialog.cancel")
1257
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1258
+ variant: "primary",
1259
+ disabled: pending || draft.trim().length === 0 || draft.trim() === target.currentTitle || duplicate,
1260
+ onClick: () => {
1261
+ onSubmit();
1262
+ },
1263
+ children: t("dialog.rename")
1264
+ })] }),
1265
+ children: [
1266
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1267
+ className: worktree_css_default.renameInput,
1268
+ value: draft,
1269
+ "aria-label": t("workspace.name"),
1270
+ autoFocus: true,
1271
+ disabled: pending,
1272
+ onFocus: (event) => {
1273
+ event.currentTarget.select();
1274
+ },
1275
+ onChange: (event) => {
1276
+ onDraftChange(event.currentTarget.value);
1277
+ },
1278
+ onKeyDown: (event) => {
1279
+ if (event.key === "Enter" && !event.nativeEvent.isComposing) {
1280
+ event.preventDefault();
1281
+ onSubmit();
1282
+ }
1283
+ }
1284
+ }),
1285
+ duplicate && (0, react_jsx_runtime.jsx)("p", {
1286
+ className: worktree_css_default.renameError,
1287
+ role: "alert",
1288
+ children: t("workspace.duplicate")
1289
+ }),
1290
+ error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
1291
+ className: worktree_css_default.renameError,
1292
+ role: "alert",
1293
+ children: formatWorktreeViewError(error, t)
1294
+ })
1295
+ ]
1296
+ });
1297
+ }
1298
+ function WorktreeWorkspaceDeleteDialog({ t, target, pending, error, onClose, onSubmit }) {
1299
+ if (target === void 0) return null;
1300
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1301
+ open: true,
1302
+ onClose,
1303
+ closeLabel: t("dialog.closeWorkspaceDelete"),
1304
+ title: t("workspace.deleteTitle"),
1305
+ description: t("workspace.deleteDescription", { name: target.title }),
1306
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1307
+ variant: "outline",
1308
+ disabled: pending,
1309
+ onClick: onClose,
1310
+ children: t("dialog.cancel")
1311
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1312
+ variant: "outline",
1313
+ disabled: pending,
1314
+ onClick: () => {
1315
+ onSubmit();
1316
+ },
1317
+ children: t("dialog.delete")
1318
+ })] }),
1319
+ children: error !== void 0 && (0, react_jsx_runtime.jsx)("p", {
1320
+ className: worktree_css_default.renameError,
1321
+ role: "alert",
1322
+ children: formatWorktreeViewError(error, t)
1323
+ })
1324
+ });
1325
+ }
1326
+ function WorktreeCreateDialog({ t, workspace, view, setupStatus, canCreate, selectedBranch, newBranch, actionPending, onClose, onSelectedBranchChange, onNewBranchChange, onSubmit }) {
1327
+ if (workspace === void 0) return null;
1328
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1329
+ open: true,
1330
+ onClose: () => {
1331
+ if (actionPending) return;
1332
+ onClose();
1333
+ },
1334
+ closeLabel: t("dialog.closeWorktreeCreate"),
1335
+ title: t("worktree.createTitle"),
1336
+ description: t("worktree.createDescription", { name: workspace.title }),
1337
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1338
+ variant: "outline",
1339
+ disabled: actionPending,
1340
+ onClick: onClose,
1341
+ children: t("dialog.cancel")
1342
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1343
+ variant: "primary",
1344
+ disabled: actionPending || selectedBranch.length === 0 || newBranch.trim().length === 0,
1345
+ onClick: () => {
1346
+ onSubmit();
1347
+ },
1348
+ children: t("worktree.create")
1349
+ })] }),
1350
+ children: canCreate ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("label", {
1351
+ className: worktree_css_default.modalField,
1352
+ children: [t("worktree.baseBranch"), (0, react_jsx_runtime.jsx)("select", {
1353
+ className: worktree_css_default.actionSelect,
1354
+ "aria-label": t("worktree.baseBranch"),
1355
+ value: selectedBranch,
1356
+ disabled: actionPending,
1357
+ onChange: (event) => {
1358
+ onSelectedBranchChange(event.currentTarget.value);
1359
+ },
1360
+ children: view?.branches.map((branch) => (0, react_jsx_runtime.jsxs)("option", {
1361
+ value: branch.name,
1362
+ children: [
1363
+ branch.name,
1364
+ branch.isCurrent ? t("branch.current") : "",
1365
+ branch.checkedOut ? t("branch.checkedOut") : ""
1366
+ ]
1367
+ }, branch.name))
1368
+ })]
1369
+ }), (0, react_jsx_runtime.jsxs)("label", {
1370
+ className: worktree_css_default.modalField,
1371
+ children: [t("worktree.name"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1372
+ className: worktree_css_default.renameInput,
1373
+ "aria-label": t("worktree.name"),
1374
+ value: newBranch,
1375
+ placeholder: "dsh/12345678",
1376
+ disabled: actionPending,
1377
+ onChange: (event) => {
1378
+ onNewBranchChange(event.currentTarget.value);
1379
+ }
1380
+ })]
1381
+ })] }) : (0, react_jsx_runtime.jsxs)("div", {
1382
+ className: worktree_css_default.gitReadiness,
1383
+ "data-worktree-readiness": setupStatus ?? "loading",
1384
+ role: "alert",
1385
+ children: [(0, react_jsx_runtime.jsx)("p", {
1386
+ className: worktree_css_default.message,
1387
+ children: setupStatus === void 0 ? t("status.loading") : worktreeSetupMessage(setupStatus, t)
1388
+ }), setupStatus !== void 0 && (0, react_jsx_runtime.jsx)("pre", {
1389
+ className: worktree_css_default.commandBlock,
1390
+ "aria-label": t("worktree.setup.commands"),
1391
+ children: worktreeSetupCommands(setupStatus).join("\n")
1392
+ })]
1393
+ })
1394
+ });
1215
1395
  }
1396
+ function WorktreeRemovalDialog({ t, worktree, actionPending, onClose, onSubmit }) {
1397
+ if (worktree === void 0) return null;
1398
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
1399
+ open: true,
1400
+ onClose: () => {
1401
+ if (actionPending) return;
1402
+ onClose();
1403
+ },
1404
+ closeLabel: t("dialog.closeWorktreeRemove"),
1405
+ title: t("worktree.remove"),
1406
+ description: t("worktree.removeDescription", { name: worktree.branch }),
1407
+ footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1408
+ variant: "outline",
1409
+ disabled: actionPending,
1410
+ onClick: onClose,
1411
+ children: t("dialog.cancel")
1412
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1413
+ variant: "primary",
1414
+ disabled: actionPending,
1415
+ onClick: () => {
1416
+ onSubmit();
1417
+ },
1418
+ children: t("worktree.remove")
1419
+ })] })
1420
+ });
1421
+ }
1422
+ //#endregion
1423
+ //#region lib/client/worktree-surface-selectors.js
1216
1424
  function sessionLabel(sessionId, sessions, t) {
1217
1425
  return sessionDisplayLabel(sessionId, sessions, t("session.new"));
1218
1426
  }
1219
- function rowHalf(event) {
1220
- const rect = event.currentTarget.getBoundingClientRect();
1221
- return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
1222
- }
1223
1427
  function bindingIdsFor(bindings, worktreeId) {
1224
1428
  return bindings.filter((binding) => binding.worktreeId === worktreeId).map((binding) => binding.sessionId);
1225
1429
  }
@@ -1234,6 +1438,12 @@ window.__ModuleLoader__.load({
1234
1438
  if (view.worktrees.some((record) => includesText(record.branch, query) || includesText(record.absolutePath, query))) return true;
1235
1439
  return view.bindings.some((binding) => sessionMatchesQuery(binding.sessionId, sessions, query));
1236
1440
  }
1441
+ //#endregion
1442
+ //#region lib/client/worktree-surface-rows.js
1443
+ function rowHalf(event) {
1444
+ const rect = event.currentTarget.getBoundingClientRect();
1445
+ return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
1446
+ }
1237
1447
  /** Workspace row using the native DSH menu, fixed action column, and drag contract. */
1238
1448
  function WorktreeWorkspaceRow({ t, workspace, expanded, actionPending, menuOpen, drag, onToggle, onCreateWorktree, onRename, onDelete, onMenuOpenChange }) {
1239
1449
  const workspaceMenuItems = [{
@@ -1596,6 +1806,27 @@ window.__ModuleLoader__.load({
1596
1806
  children: expanded ? t("session.collapse") : t("session.expandMore", { count: sessionIds.length - 5 })
1597
1807
  })] });
1598
1808
  }
1809
+ //#endregion
1810
+ //#region lib/client/WorktreeSurface.js
1811
+ function toNativeWorktreeViewError(error) {
1812
+ const viewError = toWorktreeViewError(error);
1813
+ if (typeof error === "object" && error !== null) return viewError;
1814
+ return {
1815
+ ...viewError,
1816
+ message: String(error)
1817
+ };
1818
+ }
1819
+ const EMPTY_READ_STATE = {
1820
+ status: "idle",
1821
+ views: []
1822
+ };
1823
+ function useStableWorkspaceIds(workspaces) {
1824
+ const next = workspaces.map((workspace) => workspace.workspaceId);
1825
+ const previousRef = (0, react.useRef)([]);
1826
+ const stable = stableWorkspaceIds(previousRef.current, next);
1827
+ if (stable !== previousRef.current) previousRef.current = stable;
1828
+ return previousRef.current;
1829
+ }
1599
1830
  /**
1600
1831
  * Peer Worktree navigation surface. It keeps the original DSH Workspace mode
1601
1832
  * intact and renders a browser-local Workspace → Worktree → Session projection.
@@ -2457,230 +2688,79 @@ window.__ModuleLoader__.load({
2457
2688
  children: "WT"
2458
2689
  })
2459
2690
  }),
2460
- (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2461
- open: sessionRenameTarget !== void 0,
2691
+ (0, react_jsx_runtime.jsx)(WorktreeSessionRenameDialog, {
2692
+ t,
2693
+ target: sessionRenameTarget,
2694
+ draft: sessionRenameDraft,
2695
+ pending: sessionRenamePending,
2696
+ error: sessionRenameError,
2462
2697
  onClose: closeSessionRename,
2463
- closeLabel: t("dialog.close"),
2464
- title: t("session.rename"),
2465
- footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2466
- variant: "outline",
2467
- disabled: sessionRenamePending,
2468
- onClick: closeSessionRename,
2469
- children: t("dialog.cancel")
2470
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2471
- variant: "primary",
2472
- disabled: sessionRenamePending || sessionRenameDraft.trim().length === 0,
2473
- onClick: () => {
2474
- confirmSessionRename();
2475
- },
2476
- children: t("dialog.rename")
2477
- })] }),
2478
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2479
- className: worktree_css_default.renameInput,
2480
- value: sessionRenameDraft,
2481
- "aria-label": t("session.name"),
2482
- autoFocus: true,
2483
- disabled: sessionRenamePending,
2484
- onFocus: (event) => {
2485
- event.currentTarget.select();
2486
- },
2487
- onChange: (event) => {
2488
- setSessionRenameDraft(event.currentTarget.value);
2489
- setSessionRenameError(void 0);
2490
- },
2491
- onKeyDown: (event) => {
2492
- if (event.key === "Enter" && !event.nativeEvent.isComposing) {
2493
- event.preventDefault();
2494
- confirmSessionRename();
2495
- }
2496
- }
2497
- }), sessionRenameError !== void 0 && (0, react_jsx_runtime.jsx)("p", {
2498
- className: worktree_css_default.renameError,
2499
- role: "alert",
2500
- children: formatWorktreeViewError(sessionRenameError, t)
2501
- })]
2698
+ onDraftChange: (draft) => {
2699
+ setSessionRenameDraft(draft);
2700
+ setSessionRenameError(void 0);
2701
+ },
2702
+ onSubmit: confirmSessionRename
2502
2703
  }),
2503
- workspaceRenameTarget !== void 0 && (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2504
- open: workspaceRenameTarget !== void 0,
2704
+ (0, react_jsx_runtime.jsx)(WorktreeWorkspaceRenameDialog, {
2705
+ t,
2706
+ target: workspaceRenameTarget,
2707
+ draft: workspaceRenameDraft,
2708
+ pending: workspaceRenamePending,
2709
+ duplicate: workspaceRenameDuplicate,
2710
+ error: workspaceRenameError,
2505
2711
  onClose: closeWorkspaceRename,
2506
- closeLabel: t("dialog.closeWorkspaceRename"),
2507
- title: t("workspace.renameTitle"),
2508
- footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2509
- variant: "outline",
2510
- disabled: workspaceRenamePending,
2511
- onClick: closeWorkspaceRename,
2512
- children: t("dialog.cancel")
2513
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2514
- variant: "primary",
2515
- disabled: workspaceRenameBlocked,
2516
- onClick: () => {
2517
- confirmWorkspaceRename();
2518
- },
2519
- children: t("dialog.rename")
2520
- })] }),
2521
- children: [
2522
- (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2523
- className: worktree_css_default.renameInput,
2524
- value: workspaceRenameDraft,
2525
- "aria-label": t("workspace.name"),
2526
- autoFocus: true,
2527
- disabled: workspaceRenamePending,
2528
- onFocus: (event) => {
2529
- event.currentTarget.select();
2530
- },
2531
- onChange: (event) => {
2532
- setWorkspaceRenameDraft(event.currentTarget.value);
2533
- setWorkspaceRenameError(void 0);
2534
- },
2535
- onKeyDown: (event) => {
2536
- if (event.key === "Enter" && !event.nativeEvent.isComposing) {
2537
- event.preventDefault();
2538
- confirmWorkspaceRename();
2539
- }
2540
- }
2541
- }),
2542
- workspaceRenameDuplicate && (0, react_jsx_runtime.jsx)("p", {
2543
- className: worktree_css_default.renameError,
2544
- role: "alert",
2545
- children: t("workspace.duplicate")
2546
- }),
2547
- workspaceRenameError !== void 0 && (0, react_jsx_runtime.jsx)("p", {
2548
- className: worktree_css_default.renameError,
2549
- role: "alert",
2550
- children: formatWorktreeViewError(workspaceRenameError, t)
2551
- })
2552
- ]
2712
+ onDraftChange: (draft) => {
2713
+ setWorkspaceRenameDraft(draft);
2714
+ setWorkspaceRenameError(void 0);
2715
+ },
2716
+ onSubmit: confirmWorkspaceRename
2553
2717
  }),
2554
- workspaceDeleteTarget !== void 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2555
- open: workspaceDeleteTarget !== void 0,
2718
+ (0, react_jsx_runtime.jsx)(WorktreeWorkspaceDeleteDialog, {
2719
+ t,
2720
+ target: workspaceDeleteTarget,
2721
+ pending: workspaceDeletePending,
2722
+ error: workspaceDeleteError,
2556
2723
  onClose: closeWorkspaceDelete,
2557
- closeLabel: t("dialog.closeWorkspaceDelete"),
2558
- title: t("workspace.deleteTitle"),
2559
- description: t("workspace.deleteDescription", { name: workspaceDeleteTarget.title }),
2560
- footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2561
- variant: "outline",
2562
- disabled: workspaceDeletePending,
2563
- onClick: closeWorkspaceDelete,
2564
- children: t("dialog.cancel")
2565
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2566
- variant: "outline",
2567
- disabled: workspaceDeletePending,
2568
- onClick: () => {
2569
- confirmWorkspaceDelete();
2570
- },
2571
- children: t("dialog.delete")
2572
- })] }),
2573
- children: workspaceDeleteError !== void 0 && (0, react_jsx_runtime.jsx)("p", {
2574
- className: worktree_css_default.renameError,
2575
- role: "alert",
2576
- children: formatWorktreeViewError(workspaceDeleteError, t)
2577
- })
2724
+ onSubmit: confirmWorkspaceDelete
2578
2725
  }),
2579
- worktreeModalWorkspaceId !== void 0 && modalWorkspace !== void 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2580
- open: worktreeModalWorkspaceId !== void 0,
2726
+ (0, react_jsx_runtime.jsx)(WorktreeCreateDialog, {
2727
+ t,
2728
+ workspace: modalWorkspace,
2729
+ view: modalView,
2730
+ setupStatus: modalSetupStatus,
2731
+ canCreate: modalCanCreate,
2732
+ selectedBranch,
2733
+ newBranch,
2734
+ actionPending,
2581
2735
  onClose: () => {
2582
- if (actionPending) return;
2583
2736
  setWorktreeModalWorkspaceId(void 0);
2584
2737
  },
2585
- closeLabel: t("dialog.closeWorktreeCreate"),
2586
- title: t("worktree.createTitle"),
2587
- description: t("worktree.createDescription", { name: modalWorkspace.title }),
2588
- footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2589
- variant: "outline",
2590
- disabled: actionPending,
2591
- onClick: () => {
2592
- setWorktreeModalWorkspaceId(void 0);
2593
- },
2594
- children: t("dialog.cancel")
2595
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2596
- variant: "primary",
2597
- disabled: actionPending || selectedBranch.length === 0 || newBranch.trim().length === 0,
2598
- onClick: () => {
2599
- submitWorktree();
2600
- },
2601
- children: t("worktree.create")
2602
- })] }),
2603
- children: modalCanCreate ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("label", {
2604
- className: worktree_css_default.modalField,
2605
- children: [t("worktree.baseBranch"), (0, react_jsx_runtime.jsx)("select", {
2606
- className: worktree_css_default.actionSelect,
2607
- "aria-label": t("worktree.baseBranch"),
2608
- value: selectedBranch,
2609
- disabled: actionPending,
2610
- onChange: (event) => {
2611
- setSelectedBranch(event.currentTarget.value);
2612
- },
2613
- children: modalView?.branches.map((branch) => (0, react_jsx_runtime.jsxs)("option", {
2614
- value: branch.name,
2615
- children: [
2616
- branch.name,
2617
- branch.isCurrent ? t("branch.current") : "",
2618
- branch.checkedOut ? t("branch.checkedOut") : ""
2619
- ]
2620
- }, branch.name))
2621
- })]
2622
- }), (0, react_jsx_runtime.jsxs)("label", {
2623
- className: worktree_css_default.modalField,
2624
- children: [t("worktree.name"), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
2625
- className: worktree_css_default.renameInput,
2626
- "aria-label": t("worktree.name"),
2627
- value: newBranch,
2628
- placeholder: "dsh/12345678",
2629
- disabled: actionPending,
2630
- onChange: (event) => {
2631
- setNewBranch(event.currentTarget.value);
2632
- }
2633
- })]
2634
- })] }) : (0, react_jsx_runtime.jsxs)("div", {
2635
- className: worktree_css_default.gitReadiness,
2636
- "data-worktree-readiness": modalSetupStatus ?? "loading",
2637
- role: "alert",
2638
- children: [(0, react_jsx_runtime.jsx)("p", {
2639
- className: worktree_css_default.message,
2640
- children: modalSetupStatus === void 0 ? t("status.loading") : worktreeSetupMessage(modalSetupStatus, t)
2641
- }), modalSetupStatus !== void 0 && (0, react_jsx_runtime.jsx)("pre", {
2642
- className: worktree_css_default.commandBlock,
2643
- "aria-label": t("worktree.setup.commands"),
2644
- children: worktreeSetupCommands(modalSetupStatus).join("\n")
2645
- })]
2646
- })
2738
+ onSelectedBranchChange: setSelectedBranch,
2739
+ onNewBranchChange: setNewBranch,
2740
+ onSubmit: submitWorktree
2647
2741
  }),
2648
- worktreeRemoval !== void 0 && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2649
- open: worktreeRemoval !== void 0,
2742
+ (0, react_jsx_runtime.jsx)(WorktreeRemovalDialog, {
2743
+ t,
2744
+ worktree: worktreeRemoval,
2745
+ actionPending,
2650
2746
  onClose: () => {
2651
- if (actionPending) return;
2652
2747
  setWorktreeRemoval(void 0);
2653
2748
  },
2654
- closeLabel: t("dialog.closeWorktreeRemove"),
2655
- title: t("worktree.remove"),
2656
- description: t("worktree.removeDescription", { name: worktreeRemoval.branch }),
2657
- footer: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2658
- variant: "outline",
2659
- disabled: actionPending,
2660
- onClick: () => {
2661
- setWorktreeRemoval(void 0);
2662
- },
2663
- children: t("dialog.cancel")
2664
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2665
- variant: "primary",
2666
- disabled: actionPending,
2667
- onClick: () => {
2668
- if (manager === void 0) return;
2669
- const target = worktreeRemoval;
2670
- runMutation(async () => {
2671
- await executeWorktreeAction(manager, {
2672
- type: "removeWorktree",
2673
- input: {
2674
- workspaceId: target.workspaceId,
2675
- worktreeId: target.worktreeId
2676
- }
2677
- });
2678
- await invalidateWorktreeContext?.(target.workspaceId);
2679
- setWorktreeRemoval(void 0);
2749
+ onSubmit: () => {
2750
+ if (manager === void 0 || worktreeRemoval === void 0) return;
2751
+ const target = worktreeRemoval;
2752
+ runMutation(async () => {
2753
+ await executeWorktreeAction(manager, {
2754
+ type: "removeWorktree",
2755
+ input: {
2756
+ workspaceId: target.workspaceId,
2757
+ worktreeId: target.worktreeId
2758
+ }
2680
2759
  });
2681
- },
2682
- children: t("worktree.remove")
2683
- })] })
2760
+ await invalidateWorktreeContext?.(target.workspaceId);
2761
+ setWorktreeRemoval(void 0);
2762
+ });
2763
+ }
2684
2764
  })
2685
2765
  ]
2686
2766
  });