@norman-else/dsh-claude 0.1.42 → 0.1.44

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.
package/lib/client.d.ts CHANGED
@@ -292,6 +292,24 @@ declare const en: {
292
292
  readonly diffUpdateBranchCompleted: "Updated and pushed commit {commit}";
293
293
  readonly diffUpdateBranchConflicts: "The update left conflicts in these files:";
294
294
  readonly diffUpdateBranchResolve: "Have Claude resolve the conflicts";
295
+ readonly conflictTitle: "Resolve conflicts";
296
+ readonly conflictOperation_rebase: "Rebase";
297
+ readonly conflictOperation_merge: "Merge";
298
+ readonly 'conflictOperation_cherry-pick': "Cherry-pick";
299
+ readonly conflictOperation_revert: "Revert";
300
+ readonly conflictBadge: "{operation}: {count} conflict(s)";
301
+ readonly conflictBadgeReady: "Continue {operation}";
302
+ readonly conflictDescription: "The {operation} is unfinished. Resolve the conflicts and continue, or abort to return to the state before it.";
303
+ readonly conflictFiles: "These files conflict and must be resolved and staged:";
304
+ readonly conflictReady: "Every conflict is resolved. The operation can continue.";
305
+ readonly conflictResolve: "Have Claude resolve the conflicts";
306
+ readonly conflictContinue: "Continue {operation}";
307
+ readonly conflictAbort: "Abort {operation}";
308
+ readonly conflictAbortConfirm: "Press \"Abort {operation}\" again to confirm: work done during the {operation} is discarded.";
309
+ readonly conflictPush: "Push once it finishes";
310
+ readonly conflictContinued: "{operation} finished";
311
+ readonly conflictPushed: "{operation} finished and pushed";
312
+ readonly conflictAborted: "Aborted the {operation}";
295
313
  readonly repositoryChecksOpen: "Show failing checks";
296
314
  readonly checksCardTitle: "Failing checks";
297
315
  readonly checksCardLoading: "Loading check details…";
package/lib/client.js CHANGED
@@ -352,6 +352,22 @@ window.__ModuleLoader__.load({
352
352
  function nativelyRenderedStep(activities, turn, step) {
353
353
  return activities.some((activity) => activity.turn === turn && activity.step === step && activity.renderer === "native");
354
354
  }
355
+ /** The turn's own accounting, drawn as a footer.
356
+ *
357
+ * It hangs off the turn rather than the step that reported it: a turn waiting
358
+ * on background tasks reports usage per settled segment, and the task badge
359
+ * the plugin draws at the turn's foot would otherwise sit *under* a line that
360
+ * reads as a closing total. Every report is cumulative, so the newest one
361
+ * supersedes the ones before it. Natively drawn steps keep the Host's footer.
362
+ */
363
+ function latestTurnUsage(activities, turn) {
364
+ let latest;
365
+ for (const activity of activities) {
366
+ if (activity.turn !== turn || activity.kind !== "usage" || activity.renderer === "native") continue;
367
+ if (activity.usage !== void 0) latest = activity.usage;
368
+ }
369
+ return latest;
370
+ }
355
371
  /** Fold one step's shared ordinal stream into Claude Code-style prose and tool groups. */
356
372
  function transcriptItemsForStep(activities, turn, step, tasks = []) {
357
373
  if (nativelyRenderedStep(activities, turn, step)) return [];
@@ -413,15 +429,6 @@ window.__ModuleLoader__.load({
413
429
  });
414
430
  continue;
415
431
  }
416
- if (activity.kind === "usage" && activity.usage !== void 0) {
417
- flushGroup();
418
- items.push({
419
- kind: "usage",
420
- ordinal: activity.ordinal,
421
- usage: activity.usage
422
- });
423
- continue;
424
- }
425
432
  if (activity.kind === "tool-call" && activity.toolUseId !== void 0 && activity.toolName !== void 0) {
426
433
  group ??= {
427
434
  ordinal: activity.ordinal,
@@ -2234,6 +2241,14 @@ window.__ModuleLoader__.load({
2234
2241
  whiteSpace: "nowrap",
2235
2242
  cursor: "pointer"
2236
2243
  };
2244
+ /** A stopped rebase is the one bar control that reports a blocked checkout
2245
+ * rather than an available action, so it carries the warning tone. */
2246
+ const repositoryConflictTrigger = {
2247
+ ...repositoryUpdateTrigger,
2248
+ borderColor: "color-mix(in srgb, var(--dsw-alias-state-warning-primary, #d69e2e) 55%, transparent)",
2249
+ background: "color-mix(in srgb, var(--dsw-alias-state-warning-primary, #d69e2e) 14%, transparent)",
2250
+ color: "var(--dsw-alias-state-warning-primary, #d69e2e)"
2251
+ };
2237
2252
  const repositoryChecksFrame = {
2238
2253
  position: "relative",
2239
2254
  display: "inline-flex",
@@ -4350,6 +4365,7 @@ window.__ModuleLoader__.load({
4350
4365
  const MAX_COMMANDS = 2e3;
4351
4366
  const MAX_REPOSITORY_TEXT_CHARS = 1024;
4352
4367
  const MAX_DIFF_CHARS = 262144;
4368
+ const MAX_CONFLICT_PATHS = 100;
4353
4369
  const MAX_REVIEW_COMMENTS = 50;
4354
4370
  const MAX_REVIEW_COMMENT_CHARS = 2e3;
4355
4371
  const MAX_TRANSCRIPT_CHARS = 64e3;
@@ -4368,7 +4384,12 @@ window.__ModuleLoader__.load({
4368
4384
  "ready",
4369
4385
  "not-repository",
4370
4386
  "unavailable"
4371
- ].includes(String(repository.status)) || typeof repository.cwd !== "string" || repository.cwd.length > MAX_REPOSITORY_TEXT_CHARS || !optionalBoundedString(repository.root) || !optionalBoundedString(repository.branch) || !optionalBoundedString(repository.remote) || repository.detached !== void 0 && typeof repository.detached !== "boolean" || repository.worktree !== void 0 && typeof repository.worktree !== "boolean" || repository.dirty !== void 0 && typeof repository.dirty !== "boolean" || repository.upstream !== void 0 && typeof repository.upstream !== "boolean" || repository.ahead !== void 0 && !nonNegativeInteger(repository.ahead) || repository.behind !== void 0 && !nonNegativeInteger(repository.behind)) return false;
4387
+ ].includes(String(repository.status)) || typeof repository.cwd !== "string" || repository.cwd.length > MAX_REPOSITORY_TEXT_CHARS || !optionalBoundedString(repository.root) || !optionalBoundedString(repository.branch) || !optionalBoundedString(repository.remote) || repository.detached !== void 0 && typeof repository.detached !== "boolean" || repository.worktree !== void 0 && typeof repository.worktree !== "boolean" || repository.dirty !== void 0 && typeof repository.dirty !== "boolean" || repository.upstream !== void 0 && typeof repository.upstream !== "boolean" || repository.ahead !== void 0 && !nonNegativeInteger(repository.ahead) || repository.behind !== void 0 && !nonNegativeInteger(repository.behind) || repository.operation !== void 0 && ![
4388
+ "rebase",
4389
+ "merge",
4390
+ "cherry-pick",
4391
+ "revert"
4392
+ ].includes(String(repository.operation)) || repository.conflicts !== void 0 && (!Array.isArray(repository.conflicts) || repository.conflicts.length > MAX_CONFLICT_PATHS || repository.conflicts.some((path) => typeof path !== "string" || path.length > MAX_REPOSITORY_TEXT_CHARS))) return false;
4372
4393
  if (repository.diff !== void 0) {
4373
4394
  const diff = record$7(repository.diff);
4374
4395
  if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
@@ -5379,8 +5400,10 @@ window.__ModuleLoader__.load({
5379
5400
  return parts;
5380
5401
  }
5381
5402
  /** The footer the Host draws under its own assistant message, drawn here for
5382
- * the steps the Host never had a message for. */
5403
+ * the steps the Host never had a message for. Mounted at the turn's foot, so
5404
+ * it closes the turn under the task badge rather than above it. */
5383
5405
  function ClaudeTurnUsage({ usage, t }) {
5406
+ ensureCss$4();
5384
5407
  const parts = turnUsageParts(usage, t);
5385
5408
  if (parts.length === 0) return null;
5386
5409
  return /* @__PURE__ */ jsxs("div", {
@@ -5422,10 +5445,7 @@ window.__ModuleLoader__.load({
5422
5445
  }, `text:${item.ordinal}`) : item.kind === "compaction" ? /* @__PURE__ */ jsx(ClaudeCompactionDivider, {
5423
5446
  compaction: item.compaction,
5424
5447
  t
5425
- }, `compaction:${item.ordinal}`) : item.kind === "usage" ? /* @__PURE__ */ jsx(ClaudeTurnUsage, {
5426
- usage: item.usage,
5427
- t
5428
- }, `usage:${item.ordinal}`) : item.kind === "tools" ? /* @__PURE__ */ jsx(ClaudeTranscriptToolGroup, {
5448
+ }, `compaction:${item.ordinal}`) : item.kind === "tools" ? /* @__PURE__ */ jsx(ClaudeTranscriptToolGroup, {
5429
5449
  tools: item.tools,
5430
5450
  ...item.additions === void 0 ? {} : { additions: item.additions },
5431
5451
  ...item.deletions === void 0 ? {} : { deletions: item.deletions },
@@ -5720,6 +5740,7 @@ window.__ModuleLoader__.load({
5720
5740
  //#endregion
5721
5741
  //#region src/client/ClaudeActivityTail.tsx
5722
5742
  const MAX_HOVER_TASKS = 6;
5743
+ const EMPTY_TASKS = [];
5723
5744
  function taskGlyph(status) {
5724
5745
  if (status === "failed") return {
5725
5746
  glyph: "×",
@@ -5842,24 +5863,36 @@ window.__ModuleLoader__.load({
5842
5863
  })
5843
5864
  });
5844
5865
  }
5866
+ /** Everything that closes a turn, in the order it reads: what the turn is
5867
+ * still doing, then what it cost. */
5868
+ function ClaudeTurnFooter({ turn, useClaudeProjection, t, openTasks }) {
5869
+ const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS);
5870
+ const usage = useClaudeProjection((value) => latestTurnUsage(value.activities, turn));
5871
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(ClaudeTaskLauncher, {
5872
+ turn,
5873
+ tasks,
5874
+ t,
5875
+ openTasks
5876
+ }), usage === void 0 ? null : /* @__PURE__ */ jsx(ClaudeTurnUsage, {
5877
+ usage,
5878
+ t
5879
+ })] });
5880
+ }
5845
5881
  function ClaudeActivityTail({ matched, useClaudeProjection, t, openTasks }) {
5846
- const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? []);
5847
- return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
5882
+ return /* @__PURE__ */ jsx(ClaudeTurnFooter, {
5848
5883
  turn: matched.turn,
5849
- tasks,
5884
+ useClaudeProjection,
5850
5885
  t,
5851
5886
  openTasks
5852
5887
  });
5853
5888
  }
5854
5889
  //#endregion
5855
5890
  //#region src/client/ClaudeActiveTasksNode.tsx
5856
- const EMPTY_TASKS = [];
5857
- /** Render the task launcher while the owning DSH turn is still open. */
5891
+ /** Render the turn footer while the owning DSH turn is still open. */
5858
5892
  function ClaudeActiveTasksNode({ node, useClaudeProjection, t, openTasks }) {
5859
- const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS);
5860
- return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
5893
+ return /* @__PURE__ */ jsx(ClaudeTurnFooter, {
5861
5894
  turn: node.data.turn,
5862
- tasks,
5895
+ useClaudeProjection,
5863
5896
  t,
5864
5897
  openTasks
5865
5898
  });
@@ -5977,6 +6010,22 @@ window.__ModuleLoader__.load({
5977
6010
  }
5978
6011
  return checks;
5979
6012
  }
6013
+ /** Review bots sign a comment with an attribution line and an actions checklist
6014
+ * aimed at the bot itself; in a prompt both are noise, and "apply fix" reads as
6015
+ * an instruction Claude cannot follow. */
6016
+ function commentText(body) {
6017
+ const lines = body.replaceAll(/<!--[\s\S]*?-->/g, "").split("\n").filter((line) => !/^\s*<sup>[\s\S]*<\/sup>\s*$/.test(line));
6018
+ let end = lines.length;
6019
+ while (end > 0 && /^\s*(?:-{3,}|\*\*[^*]+\*\*|[-*] \[[ xX]\].*)?\s*$/.test(lines[end - 1] ?? "")) end -= 1;
6020
+ return (lines.slice(end).some((line) => /^\s*[-*] \[[ xX]\]/.test(line)) ? lines.slice(0, end) : lines).join("\n").trim();
6021
+ }
6022
+ /** A one-line comment sits after the author; anything longer starts on its own
6023
+ * line, so its headings and lists keep meaning instead of running into ours. */
6024
+ function attributed(prefix, body) {
6025
+ const text = commentText(body);
6026
+ const indented = text.split("\n").map((line) => line.length === 0 ? line : ` ${line}`).join("\n");
6027
+ return text.includes("\n") ? `${prefix}\n\n${indented}` : `${prefix} ${text}`;
6028
+ }
5980
6029
  /** Draft handed to Claude when the user forwards GitHub review comments. A
5981
6030
  * resolved thread is a settled conversation: forwarding it would ask Claude to
5982
6031
  * redo work the reviewers already signed off. */
@@ -5986,8 +6035,8 @@ window.__ModuleLoader__.load({
5986
6035
  return `Please address the following GitHub pull request review comments. Make the requested changes, or explain briefly when a comment should not be applied.\n\n${open.map((thread) => {
5987
6036
  const [first, ...rest] = thread.comments;
5988
6037
  if (first === void 0) return "";
5989
- return [`- ${`${thread.path}${thread.line === void 0 ? "" : `:${thread.line}`}`} (@${first.author}): ${first.body.replaceAll("\n", "\n ")}`, ...rest.map((reply) => ` (@${reply.author}): ${reply.body.replaceAll("\n", "\n ")}`)].join("\n");
5990
- }).filter((block) => block.length > 0).join("\n")}`;
6038
+ return [attributed(`- ${`${thread.path}${thread.line === void 0 ? "" : `:${thread.line}`}`} (@${first.author}):`, first.body), ...rest.map((reply) => attributed(` (@${reply.author}):`, reply.body))].join("\n");
6039
+ }).filter((block) => block.length > 0).join("\n\n")}`;
5991
6040
  }
5992
6041
  /** Draft handed to Claude when the user forwards failing CI checks. */
5993
6042
  function composeChecksPrompt(checks) {
@@ -5995,11 +6044,13 @@ window.__ModuleLoader__.load({
5995
6044
  return `${`## ${check.name}${check.link === void 0 ? "" : ` (${check.link})`}`}${check.description === void 0 ? "" : `\n${check.description}`}${check.log === void 0 ? "" : `\n\n\`\`\`\n${check.log}\n\`\`\``}`;
5996
6045
  }).join("\n\n")}`;
5997
6046
  }
5998
- /** Draft handed to Claude after an update-branch merge left conflicts behind. */
5999
- function composeConflictsPrompt(baseBranch, conflicts, method = "merge") {
6047
+ /** Draft handed to Claude for a stopped merge, rebase, cherry-pick or revert --
6048
+ * from the update-branch dialog that caused one, or from the repository bar
6049
+ * for one already in the tree, where the base branch is not always known. */
6050
+ function composeConflictsPrompt(conflicts, operation = "merge", baseBranch) {
6000
6051
  const list = conflicts.map((file) => `- ${file}`).join("\n");
6001
- if (method === "rebase") return `Rebasing the current branch onto origin/${baseBranch} stopped on conflicts in the files below. Resolve each conflict preserving the intent of both sides, stage the files, run \`git rebase --continue\` until the rebase finishes, then push with \`git push --force-with-lease\`.\n\n${list}`;
6002
- return `Merging origin/${baseBranch} into the current branch left merge conflicts in the files below. Resolve each conflict preserving the intent of both sides, then commit the merge.\n\n${list}`;
6052
+ const base = baseBranch === void 0 ? void 0 : `origin/${baseBranch}`;
6053
+ return `${operation === "rebase" ? `Rebasing the current branch${base === void 0 ? "" : ` onto ${base}`} stopped on conflicts in the files below.` : operation === "merge" ? `Merging ${base ?? "the base branch"} into the current branch left conflicts in the files below.` : `A ${operation} stopped on conflicts in the files below.`} Resolve each conflict preserving the intent of both sides, stage the resolved files, then run \`git ${operation} --continue\` until the ${operation} finishes.${operation === "rebase" && baseBranch !== void 0 ? " Once it finishes, push with `git push --force-with-lease`." : ""}\n\n${list}`;
6003
6054
  }
6004
6055
  //#endregion
6005
6056
  //#region src/client/auto-fix.ts
@@ -6054,6 +6105,15 @@ window.__ModuleLoader__.load({
6054
6105
  };
6055
6106
  }
6056
6107
  //#endregion
6108
+ //#region src/client/branch-label.ts
6109
+ /** `Detached HEAD` names a git implementation detail, not the user's checkout:
6110
+ * a stopped rebase still knows which branch it parked, and that name is what
6111
+ * every panel should show. Only a genuinely nameless HEAD falls back. */
6112
+ function branchLabel(repository, t) {
6113
+ if (repository.branch !== void 0) return repository.branch;
6114
+ return repository.detached === true ? t("repositoryDetached") : t("repositoryUnknownBranch");
6115
+ }
6116
+ //#endregion
6057
6117
  //#region src/client/session-preset.ts
6058
6118
  /**
6059
6119
  * Resolve one row's preset id, newest seat first.
@@ -6191,7 +6251,7 @@ window.__ModuleLoader__.load({
6191
6251
  }) : rows.map((row) => {
6192
6252
  const repository = row.cwd === void 0 ? void 0 : statuses[row.cwd];
6193
6253
  const pullRequest = repository?.pullRequest;
6194
- const branch = repository?.status === "ready" ? repository.detached === true ? t("repositoryDetached") : repository.branch ?? t("repositoryUnknownBranch") : repository === void 0 ? t("overviewLoading") : t("repositoryUnavailable");
6254
+ const branch = repository?.status === "ready" ? branchLabel(repository, t) : repository === void 0 ? t("overviewLoading") : t("repositoryUnavailable");
6195
6255
  return /* @__PURE__ */ jsxs("button", {
6196
6256
  type: "button",
6197
6257
  style: overviewRow,
@@ -8592,6 +8652,169 @@ window.__ModuleLoader__.load({
8592
8652
  }) : null]
8593
8653
  });
8594
8654
  }
8655
+ /** The way out of a stopped merge or rebase. A rebase detaches HEAD, which
8656
+ * hides every other control on this bar, and the update-branch dialog that
8657
+ * started it takes its conflict list along when it closes -- so this one is
8658
+ * mounted from repository state instead, and survives being dismissed. */
8659
+ function ConflictControl({ sessionId, repository, t, report, submitPrompt }) {
8660
+ const [dialog, setDialog] = useState();
8661
+ const operation = repository.operation;
8662
+ if (operation === void 0) return null;
8663
+ const conflicts = repository.conflicts ?? [];
8664
+ const operationName = t(`conflictOperation_${operation}`);
8665
+ const closeDialog = () => {
8666
+ if (dialog?.submitting !== true) setDialog(void 0);
8667
+ };
8668
+ const run = (action) => {
8669
+ if (dialog === void 0 || dialog.submitting) return;
8670
+ const { error: _error, ...pending } = dialog;
8671
+ setDialog({
8672
+ ...pending,
8673
+ submitting: true
8674
+ });
8675
+ executeRepositoryAction(sessionId, {
8676
+ action,
8677
+ fingerprint: "",
8678
+ message: "",
8679
+ includeUnstaged: false,
8680
+ push: dialog.push
8681
+ }).then((result) => {
8682
+ if (result.conflicts !== void 0 && result.conflicts.length > 0) {
8683
+ setDialog({
8684
+ ...pending,
8685
+ submitting: false,
8686
+ confirmAbort: false
8687
+ });
8688
+ return;
8689
+ }
8690
+ report(action === "resolve-abort" ? t("conflictAborted", { operation: operationName }) : t(result.pushed ? "conflictPushed" : "conflictContinued", { operation: operationName }));
8691
+ setDialog(void 0);
8692
+ }, (reason) => {
8693
+ setDialog({
8694
+ ...pending,
8695
+ submitting: false,
8696
+ error: reason instanceof Error ? reason.message : t("diffActionFailed")
8697
+ });
8698
+ });
8699
+ };
8700
+ const openDialog = () => {
8701
+ setDialog({
8702
+ submitting: false,
8703
+ confirmAbort: false,
8704
+ push: repository.remote !== void 0 && repository.pullRequest?.state === "open"
8705
+ });
8706
+ };
8707
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
8708
+ /* @__PURE__ */ jsx("button", {
8709
+ type: "button",
8710
+ style: repositoryConflictTrigger,
8711
+ title: t("conflictDescription", { operation: operationName }),
8712
+ onClick: openDialog,
8713
+ children: conflicts.length > 0 ? t("conflictBadge", {
8714
+ operation: operationName,
8715
+ count: conflicts.length
8716
+ }) : t("conflictBadgeReady", { operation: operationName })
8717
+ }),
8718
+ dialog === void 0 ? null : /* @__PURE__ */ jsx("style", {
8719
+ "data-dsh-claude-repository-modal-styles": true,
8720
+ children: diffModalCss
8721
+ }),
8722
+ /* @__PURE__ */ jsx(Modal, {
8723
+ className: "dshClaudeRepositoryActionModal",
8724
+ contentClassName: "dshClaudeRepositoryActionModalContent",
8725
+ open: dialog !== void 0,
8726
+ onClose: closeDialog,
8727
+ title: t("conflictTitle"),
8728
+ closeLabel: t("diffCancel"),
8729
+ description: t("conflictDescription", { operation: operationName }),
8730
+ footer: /* @__PURE__ */ jsxs("div", {
8731
+ style: diffModalFooter,
8732
+ children: [/* @__PURE__ */ jsx("button", {
8733
+ type: "button",
8734
+ style: {
8735
+ ...button,
8736
+ ...diffModalButton
8737
+ },
8738
+ disabled: dialog?.submitting === true,
8739
+ onClick: () => {
8740
+ if (dialog?.confirmAbort === true) run("resolve-abort");
8741
+ else setDialog((current) => current === void 0 ? current : {
8742
+ ...current,
8743
+ confirmAbort: true
8744
+ });
8745
+ },
8746
+ children: t("conflictAbort", { operation: operationName })
8747
+ }), /* @__PURE__ */ jsx("button", {
8748
+ type: "button",
8749
+ style: {
8750
+ ...primaryButton,
8751
+ ...diffModalButton
8752
+ },
8753
+ disabled: dialog?.submitting === true || conflicts.length > 0,
8754
+ onClick: () => run("resolve-continue"),
8755
+ children: dialog?.submitting === true ? t("diffSubmitting") : t("conflictContinue", { operation: operationName })
8756
+ })]
8757
+ }),
8758
+ children: dialog === void 0 ? null : /* @__PURE__ */ jsxs("div", {
8759
+ style: diffModalBody,
8760
+ children: [
8761
+ /* @__PURE__ */ jsxs("div", {
8762
+ style: diffModalMeta,
8763
+ children: [/* @__PURE__ */ jsxs("strong", {
8764
+ style: diffModalMetaText,
8765
+ children: [
8766
+ operationName,
8767
+ " · ",
8768
+ branchLabel(repository, t)
8769
+ ]
8770
+ }), /* @__PURE__ */ jsx("span", {
8771
+ style: diffModalFileState,
8772
+ children: conflicts.length > 0 ? t("conflictFiles") : t("conflictReady")
8773
+ })]
8774
+ }),
8775
+ conflicts.length === 0 ? null : /* @__PURE__ */ jsx("ul", {
8776
+ style: diffModalConflicts,
8777
+ children: conflicts.map((file) => /* @__PURE__ */ jsx("li", { children: file }, file))
8778
+ }),
8779
+ conflicts.length === 0 || submitPrompt === void 0 ? null : /* @__PURE__ */ jsx("button", {
8780
+ type: "button",
8781
+ style: diffModalConflictResolve,
8782
+ onClick: () => {
8783
+ submitPrompt(composeConflictsPrompt(conflicts, operation, repository.pullRequest?.baseBranch));
8784
+ closeDialog();
8785
+ },
8786
+ children: t("conflictResolve")
8787
+ }),
8788
+ repository.remote === void 0 ? null : /* @__PURE__ */ jsxs("label", {
8789
+ style: diffModalCheckbox,
8790
+ children: [/* @__PURE__ */ jsx("input", {
8791
+ type: "checkbox",
8792
+ checked: dialog.push,
8793
+ disabled: dialog.submitting,
8794
+ onChange: (event) => {
8795
+ const { checked } = event.currentTarget;
8796
+ setDialog((current) => current === void 0 ? current : {
8797
+ ...current,
8798
+ push: checked
8799
+ });
8800
+ }
8801
+ }), t("conflictPush")]
8802
+ }),
8803
+ !dialog.confirmAbort ? null : /* @__PURE__ */ jsx("p", {
8804
+ role: "alert",
8805
+ style: diffModalStatus,
8806
+ children: t("conflictAbortConfirm", { operation: operationName })
8807
+ }),
8808
+ dialog.error === void 0 ? null : /* @__PURE__ */ jsx("p", {
8809
+ role: "alert",
8810
+ style: diffModalError,
8811
+ children: dialog.error
8812
+ })
8813
+ ]
8814
+ })
8815
+ })
8816
+ ] });
8817
+ }
8595
8818
  /** The trigger only shows on a clean branch that is behind its base -- but a
8596
8819
  * conflicted rebase leaves the tree dirty on a detached HEAD, so an open
8597
8820
  * dialog (and its resolve button) has to outlive that. */
@@ -8763,7 +8986,7 @@ window.__ModuleLoader__.load({
8763
8986
  type: "button",
8764
8987
  style: diffModalConflictResolve,
8765
8988
  onClick: () => {
8766
- submitPrompt(composeConflictsPrompt(base, dialog.conflicts ?? [], method));
8989
+ submitPrompt(composeConflictsPrompt(dialog.conflicts ?? [], method, base));
8767
8990
  closeDialog();
8768
8991
  },
8769
8992
  children: t("diffUpdateBranchResolve")
@@ -9034,7 +9257,7 @@ window.__ModuleLoader__.load({
9034
9257
  const repository = projection.repository;
9035
9258
  const { toast, report } = useActionToast();
9036
9259
  if (blank || !projection.owned || repository === void 0) return null;
9037
- const branch = repository.detached === true ? t("repositoryDetached") : repository.branch ?? t("repositoryUnknownBranch");
9260
+ const branch = branchLabel(repository, t);
9038
9261
  const pullRequest = repository.pullRequest;
9039
9262
  const merged = pullRequest?.state === "merged";
9040
9263
  const mergedAge = merged ? relativeAge(pullRequest.mergedAt) : void 0;
@@ -9106,79 +9329,89 @@ window.__ModuleLoader__.load({
9106
9329
  }) : null,
9107
9330
  /* @__PURE__ */ jsxs("span", {
9108
9331
  style: repositoryStatusItems,
9109
- children: [hasDiff || pushable ? /* @__PURE__ */ jsxs("button", {
9110
- type: "button",
9111
- style: {
9112
- ...diffTrigger,
9113
- ...merged ? diffTriggerMuted : {}
9114
- },
9115
- onClick: openDiff,
9116
- "aria-label": t("diffOpen"),
9117
- children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9118
- style: merged ? diffAddMuted : diffAdd,
9119
- children: ["+", repository.diff.additions]
9120
- }), /* @__PURE__ */ jsxs("span", {
9121
- style: merged ? diffDeleteMuted : diffDelete,
9122
- children: ["−", repository.diff.deletions]
9123
- })] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
9124
- style: merged ? diffAheadMuted : diffAhead,
9125
- children: ["↑", aheadCount > 0 ? aheadCount : ""]
9126
- }) : null]
9127
- }) : null, pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9128
- style: repositoryMergedStatus,
9129
- children: [
9130
- /* @__PURE__ */ jsx("span", {
9131
- style: repositoryMergedDot,
9132
- "aria-hidden": "true"
9133
- }),
9134
- t("repositoryState_merged"),
9135
- mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
9136
- style: repositoryMergedAge,
9137
- children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
9138
- })
9139
- ]
9140
- }), /* @__PURE__ */ jsx(CleanupControl, {
9141
- repository,
9142
- t,
9143
- report,
9144
- ...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
9145
- })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
9146
- pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
9147
- sessionId,
9148
- pullNumber: pullRequest.number,
9149
- t,
9150
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9151
- }) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9152
- label: t(`repositoryChecks_${pullRequest.checks}`),
9153
- tone: pullRequest.checks === "passing" ? "success" : "warning",
9154
- children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9155
- }),
9156
- pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9157
- label: t(`repositoryReview_${pullRequest.review}`),
9158
- tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9159
- children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9160
- }),
9161
- /* @__PURE__ */ jsx(AutoFixControl, {
9162
- sessionId,
9163
- repository,
9164
- running,
9165
- t,
9166
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9167
- }),
9168
- /* @__PURE__ */ jsx(UpdateBranchControl, {
9332
+ children: [
9333
+ /* @__PURE__ */ jsx(ConflictControl, {
9169
9334
  sessionId,
9170
9335
  repository,
9171
9336
  t,
9172
9337
  report,
9173
9338
  ...submitPrompt === void 0 ? {} : { submitPrompt }
9174
9339
  }),
9175
- /* @__PURE__ */ jsx(MergePullRequestControl, {
9176
- sessionId,
9340
+ hasDiff || pushable ? /* @__PURE__ */ jsxs("button", {
9341
+ type: "button",
9342
+ style: {
9343
+ ...diffTrigger,
9344
+ ...merged ? diffTriggerMuted : {}
9345
+ },
9346
+ onClick: openDiff,
9347
+ "aria-label": t("diffOpen"),
9348
+ children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9349
+ style: merged ? diffAddMuted : diffAdd,
9350
+ children: ["+", repository.diff.additions]
9351
+ }), /* @__PURE__ */ jsxs("span", {
9352
+ style: merged ? diffDeleteMuted : diffDelete,
9353
+ children: ["−", repository.diff.deletions]
9354
+ })] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
9355
+ style: merged ? diffAheadMuted : diffAhead,
9356
+ children: ["↑", aheadCount > 0 ? aheadCount : ""]
9357
+ }) : null]
9358
+ }) : null,
9359
+ pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9360
+ style: repositoryMergedStatus,
9361
+ children: [
9362
+ /* @__PURE__ */ jsx("span", {
9363
+ style: repositoryMergedDot,
9364
+ "aria-hidden": "true"
9365
+ }),
9366
+ t("repositoryState_merged"),
9367
+ mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
9368
+ style: repositoryMergedAge,
9369
+ children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
9370
+ })
9371
+ ]
9372
+ }), /* @__PURE__ */ jsx(CleanupControl, {
9177
9373
  repository,
9178
9374
  t,
9179
- report
9180
- })
9181
- ] })]
9375
+ report,
9376
+ ...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
9377
+ })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
9378
+ pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
9379
+ sessionId,
9380
+ pullNumber: pullRequest.number,
9381
+ t,
9382
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9383
+ }) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9384
+ label: t(`repositoryChecks_${pullRequest.checks}`),
9385
+ tone: pullRequest.checks === "passing" ? "success" : "warning",
9386
+ children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9387
+ }),
9388
+ pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9389
+ label: t(`repositoryReview_${pullRequest.review}`),
9390
+ tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9391
+ children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9392
+ }),
9393
+ /* @__PURE__ */ jsx(AutoFixControl, {
9394
+ sessionId,
9395
+ repository,
9396
+ running,
9397
+ t,
9398
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9399
+ }),
9400
+ /* @__PURE__ */ jsx(UpdateBranchControl, {
9401
+ sessionId,
9402
+ repository,
9403
+ t,
9404
+ report,
9405
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9406
+ }),
9407
+ /* @__PURE__ */ jsx(MergePullRequestControl, {
9408
+ sessionId,
9409
+ repository,
9410
+ t,
9411
+ report
9412
+ })
9413
+ ] })
9414
+ ]
9182
9415
  })
9183
9416
  ]
9184
9417
  })]
@@ -14387,7 +14620,7 @@ window.__ModuleLoader__.load({
14387
14620
  return (action === "commit" ? t("diffCommit") : action === "commit-push" ? t("diffCommitPush") : action === "push" ? t("diffPush") : action === "merge-pr" ? t("diffMergePr") : action === "update-branch" ? t("diffUpdateBranch") : t("diffCreatePr")).replace(/[….]+$/u, "");
14388
14621
  }
14389
14622
  function repositoryActionAvailability(repository) {
14390
- const ready = repository?.status === "ready" && repository.detached !== true;
14623
+ const ready = repository?.status === "ready" && repository.detached !== true && (repository.conflicts ?? []).length === 0;
14391
14624
  const committable = ready && repository.dirty === true;
14392
14625
  const hasRemote = repository?.remote !== void 0;
14393
14626
  const hasOpenPullRequest = repository?.pullRequest?.state === "open";
@@ -14707,7 +14940,7 @@ window.__ModuleLoader__.load({
14707
14940
  repository?.status
14708
14941
  ]);
14709
14942
  if (!projection.owned || repository?.status !== "ready" || diff === void 0) return null;
14710
- const branch = repository.detached === true ? t("repositoryDetached") : repository.branch ?? t("repositoryUnknownBranch");
14943
+ const branch = branchLabel(repository, t);
14711
14944
  const availability = repositoryActionAvailability(repository);
14712
14945
  const anyActionAvailable = availability["commit"] || availability["commit-push"] || availability["push"] || availability["create-pr"];
14713
14946
  const menuItems = [
@@ -18767,6 +19000,24 @@ window.__ModuleLoader__.load({
18767
19000
  diffUpdateBranchCompleted: "已更新并推送 commit {commit}",
18768
19001
  diffUpdateBranchConflicts: "更新产生冲突,以下文件需要解决:",
18769
19002
  diffUpdateBranchResolve: "让 Claude 解决冲突",
19003
+ conflictTitle: "解决冲突",
19004
+ conflictOperation_rebase: "Rebase",
19005
+ conflictOperation_merge: "合并",
19006
+ "conflictOperation_cherry-pick": "Cherry-pick",
19007
+ conflictOperation_revert: "Revert",
19008
+ conflictBadge: "{operation} 冲突 {count}",
19009
+ conflictBadgeReady: "继续 {operation}",
19010
+ conflictDescription: "{operation} 尚未完成。解决冲突后继续,或中止回到操作前的状态。",
19011
+ conflictFiles: "以下文件存在冲突,需要解决并 git add:",
19012
+ conflictReady: "冲突已全部解决,可以继续。",
19013
+ conflictResolve: "让 Claude 解决冲突",
19014
+ conflictContinue: "继续 {operation}",
19015
+ conflictAbort: "中止 {operation}",
19016
+ conflictAbortConfirm: "再次点击「中止 {operation}」确认:{operation} 期间的改动会被丢弃。",
19017
+ conflictPush: "完成后推送到远端",
19018
+ conflictContinued: "{operation} 已完成",
19019
+ conflictPushed: "{operation} 已完成并推送",
19020
+ conflictAborted: "已中止 {operation}",
18770
19021
  repositoryChecksOpen: "查看失败的检查",
18771
19022
  checksCardTitle: "失败的检查",
18772
19023
  checksCardLoading: "正在读取检查详情…",
@@ -19191,6 +19442,24 @@ window.__ModuleLoader__.load({
19191
19442
  diffUpdateBranchCompleted: "Updated and pushed commit {commit}",
19192
19443
  diffUpdateBranchConflicts: "The update left conflicts in these files:",
19193
19444
  diffUpdateBranchResolve: "Have Claude resolve the conflicts",
19445
+ conflictTitle: "Resolve conflicts",
19446
+ conflictOperation_rebase: "Rebase",
19447
+ conflictOperation_merge: "Merge",
19448
+ "conflictOperation_cherry-pick": "Cherry-pick",
19449
+ conflictOperation_revert: "Revert",
19450
+ conflictBadge: "{operation}: {count} conflict(s)",
19451
+ conflictBadgeReady: "Continue {operation}",
19452
+ conflictDescription: "The {operation} is unfinished. Resolve the conflicts and continue, or abort to return to the state before it.",
19453
+ conflictFiles: "These files conflict and must be resolved and staged:",
19454
+ conflictReady: "Every conflict is resolved. The operation can continue.",
19455
+ conflictResolve: "Have Claude resolve the conflicts",
19456
+ conflictContinue: "Continue {operation}",
19457
+ conflictAbort: "Abort {operation}",
19458
+ conflictAbortConfirm: "Press \"Abort {operation}\" again to confirm: work done during the {operation} is discarded.",
19459
+ conflictPush: "Push once it finishes",
19460
+ conflictContinued: "{operation} finished",
19461
+ conflictPushed: "{operation} finished and pushed",
19462
+ conflictAborted: "Aborted the {operation}",
19194
19463
  repositoryChecksOpen: "Show failing checks",
19195
19464
  checksCardTitle: "Failing checks",
19196
19465
  checksCardLoading: "Loading check details…",