@norman-else/dsh-claude 0.1.46 → 0.1.48

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.js CHANGED
@@ -4,7 +4,7 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  module.exports;
6
6
  var { Fragment, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } = require("react");
7
- var { Button, DiffBlock, DisclosureRow, HoverCard, IconAgentPresetOutline16, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconCheckOutline16, IconChevronDownOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconCloseOutline16, IconEditOutline16, IconEllipsisOutline16, IconFullscreenOutline16, IconListPenOutline16, IconLoadingOutline16, IconQueueOutline14, IconRefreshOutline14, IconRefreshOutline16, IconRightUpOutline14, IconSearchOutline16, IconSendOutline14, IconSparkle16, IconThinkOutline14, IconTrashOutline16, MarkdownText, Menu, Modal, StateDot, Toast, Tooltip, useDismissOnOutsidePointer } = require("@deepseek-ai/dsh-client-ui-primitives");
7
+ var { Button, DiffBlock, DisclosureRow, HoverCard, IconAgentPresetOutline16, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconCheckOutline16, IconChevronDownOutline14, IconChevronRightOutline14, IconChevronUpOutline14, IconCloseOutline16, IconEditOutline16, IconListPenOutline16, IconLoadingOutline16, IconQueueOutline14, IconRefreshOutline14, IconRefreshOutline16, IconRightUpOutline14, IconSearchOutline16, IconSendOutline14, IconSparkle16, IconThinkOutline14, IconTrashOutline16, MarkdownText, Menu, Modal, StateDot, Toast, Tooltip, useDismissOnOutsidePointer } = require("@deepseek-ai/dsh-client-ui-primitives");
8
8
  var { Fragment: Fragment$1, jsx, jsxs } = require("react/jsx-runtime");
9
9
  var { createPortal } = require("react-dom");
10
10
  /** Claude's subagent dispatch tools; rendered as plugin-owned group cards
@@ -25,7 +25,6 @@ window.__ModuleLoader__.load({
25
25
  const CLAUDE_REPOSITORY_FILE_PATH = "/plugins/dsh-claude/repository/file";
26
26
  const CLAUDE_JIRA_PATH = "/plugins/dsh-claude/jira";
27
27
  const CLAUDE_ASK_PATH = "/plugins/dsh-claude/ask";
28
- const CLAUDE_EDITOR_OPEN_PATH = "/plugins/dsh-claude/editor/open";
29
28
  const CLAUDE_REWIND_PATH = "/plugins/dsh-claude/rewind";
30
29
  const CLAUDE_PLAN_FEEDBACK_PATH = "/plugins/dsh-claude/plan/feedback";
31
30
  const CLAUDE_PROMPTS_PATH = "/plugins/dsh-claude/prompts";
@@ -2212,28 +2211,38 @@ window.__ModuleLoader__.load({
2212
2211
  background: "transparent",
2213
2212
  color: "var(--dsw-alias-label-tertiary)"
2214
2213
  };
2215
- const repositoryAutoFix = {
2216
- width: 26,
2217
- minHeight: 26,
2218
- flex: "none",
2219
- display: "inline-flex",
2220
- alignItems: "center",
2221
- justifyContent: "center",
2222
- padding: 0,
2223
- border: "1px solid var(--dsw-alias-border-l2, color-mix(in srgb, currentColor 16%, transparent))",
2224
- borderRadius: 7,
2225
- outline: "none",
2226
- background: "var(--dsw-alias-bg-layer-2)",
2227
- color: "var(--dsw-alias-label-tertiary)",
2228
- font: "inherit",
2229
- lineHeight: "18px",
2230
- cursor: "pointer"
2231
- };
2232
- const repositoryAutoFixActive = {
2233
- borderColor: "color-mix(in srgb, var(--dsw-static-blue-450) 45%, transparent)",
2234
- background: "color-mix(in srgb, var(--dsw-static-blue-450) 12%, transparent)",
2235
- color: "var(--dsw-static-blue-450)"
2236
- };
2214
+ const repositoryAutoFixClass = "dshClaudeAutoFix";
2215
+ /** The switch is styled entirely from a stylesheet: its base look, its on
2216
+ * state off `aria-checked`, its ring off `:focus-visible`. Nothing is set
2217
+ * inline, because an inline border, background or colour would outrank the
2218
+ * on-state rule and the switch would toggle without ever looking switched. */
2219
+ const repositoryAutoFixCss = `
2220
+ .${repositoryAutoFixClass} {
2221
+ width: 26px;
2222
+ min-height: 26px;
2223
+ flex: none;
2224
+ display: inline-flex;
2225
+ align-items: center;
2226
+ justify-content: center;
2227
+ padding: 0;
2228
+ border: 1px solid var(--dsw-alias-border-l2, color-mix(in srgb, currentColor 16%, transparent));
2229
+ border-radius: 7px;
2230
+ outline: none;
2231
+ background: var(--dsw-alias-bg-layer-2);
2232
+ color: var(--dsw-alias-label-tertiary);
2233
+ font: inherit;
2234
+ line-height: 18px;
2235
+ cursor: pointer;
2236
+ }
2237
+ .${repositoryAutoFixClass}:focus-visible {
2238
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-static-blue-450) 28%, transparent);
2239
+ }
2240
+ .${repositoryAutoFixClass}[aria-checked="true"] {
2241
+ border-color: var(--dsw-static-blue-450);
2242
+ background: var(--dsw-static-blue-450);
2243
+ color: var(--dsw-alias-label-on-accent, #fff);
2244
+ }
2245
+ `;
2237
2246
  const repositoryUpdateTrigger = {
2238
2247
  flex: "none",
2239
2248
  display: "inline-flex",
@@ -3235,11 +3244,6 @@ window.__ModuleLoader__.load({
3235
3244
  background: "var(--dsw-alias-bg-layer-1)",
3236
3245
  boxShadow: "0 4px 16px color-mix(in srgb, #000 12%, transparent)"
3237
3246
  };
3238
- const diffPanelMaximized = {
3239
- width: "100%",
3240
- height: "100%",
3241
- margin: 0
3242
- };
3243
3247
  const diffHeader = {
3244
3248
  boxSizing: "border-box",
3245
3249
  height: 49,
@@ -4454,10 +4458,10 @@ window.__ModuleLoader__.load({
4454
4458
  "merge",
4455
4459
  "cherry-pick",
4456
4460
  "revert"
4457
- ].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;
4461
+ ].includes(String(repository.operation)) || repository.pullRequestOnly !== void 0 && typeof repository.pullRequestOnly !== "boolean" || 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;
4458
4462
  if (repository.diff !== void 0) {
4459
4463
  const diff = record$7(repository.diff);
4460
- 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;
4464
+ if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.elided !== void 0 && !(Array.isArray(diff.elided) && diff.elided.length <= 1e3 && diff.elided.every((item) => typeof item === "string")) || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
4461
4465
  }
4462
4466
  if (repository.pullRequest === void 0) return true;
4463
4467
  const pullRequest = record$7(repository.pullRequest);
@@ -5060,10 +5064,10 @@ window.__ModuleLoader__.load({
5060
5064
  ".dsh-claude-act-running{animation:dsh-claude-act-pulse 1.2s ease-in-out infinite}",
5061
5065
  "@keyframes dsh-claude-act-pulse{0%,100%{opacity:1}50%{opacity:.3}}"
5062
5066
  ].join("");
5063
- let cssInjected$4 = false;
5064
- function ensureCss$4() {
5065
- if (cssInjected$4 || typeof document === "undefined") return;
5066
- cssInjected$4 = true;
5067
+ let cssInjected$3 = false;
5068
+ function ensureCss$3() {
5069
+ if (cssInjected$3 || typeof document === "undefined") return;
5070
+ cssInjected$3 = true;
5067
5071
  const element = document.createElement("style");
5068
5072
  element.dataset.dshClaudeActivity = "";
5069
5073
  element.textContent = ACTIVITY_CSS;
@@ -5474,7 +5478,7 @@ window.__ModuleLoader__.load({
5474
5478
  * the steps the Host never had a message for. Mounted at the turn's foot, so
5475
5479
  * it closes the turn under the task badge rather than above it. */
5476
5480
  function ClaudeTurnUsage({ usage, t }) {
5477
- ensureCss$4();
5481
+ ensureCss$3();
5478
5482
  const parts = turnUsageParts(usage, t);
5479
5483
  if (parts.length === 0) return null;
5480
5484
  return /* @__PURE__ */ jsxs("div", {
@@ -5493,7 +5497,7 @@ window.__ModuleLoader__.load({
5493
5497
  });
5494
5498
  }
5495
5499
  function ClaudeActivityNode({ node, useClaudeProjection, t }) {
5496
- ensureCss$4();
5500
+ ensureCss$3();
5497
5501
  const marker = node.data;
5498
5502
  const activities = useClaudeProjection((value) => selectStepActivities(value, marker.turn, marker.step));
5499
5503
  const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS$1);
@@ -6073,8 +6077,8 @@ window.__ModuleLoader__.load({
6073
6077
  }
6074
6078
  return users;
6075
6079
  }
6076
- async function loadFailingChecks(sessionId, pullNumber, signal) {
6077
- const body = await loadJson("/checks", sessionId, pullNumber, signal);
6080
+ async function loadFailingChecks(sessionId, pullNumber, signal, root) {
6081
+ const body = await loadJson("/checks", sessionId, pullNumber, signal, root);
6078
6082
  if (!Array.isArray(body.checks)) throw new Error("Invalid pull request checks response.");
6079
6083
  const checks = [];
6080
6084
  for (const item of body.checks) {
@@ -7603,18 +7607,6 @@ window.__ModuleLoader__.load({
7603
7607
  toolUseId: key.slice(cut + 1)
7604
7608
  };
7605
7609
  }
7606
- /** Restore-from-maximized: four corners pulling inward. Mirrors the diff
7607
- * panel's own, which the primitives set has no counterpart for. */
7608
- function RestorePanelIcon$1() {
7609
- return /* @__PURE__ */ jsx("svg", {
7610
- width: "16",
7611
- height: "16",
7612
- viewBox: "0 0 16 16",
7613
- fill: "currentColor",
7614
- "aria-hidden": "true",
7615
- children: /* @__PURE__ */ jsx("path", { d: "M1.5 5h3V2h1.4v4.4H1.5V5Zm9.9-3h1.4v3h3v1.4h-4.4V2ZM1.5 9.6h4.4V14H4.5v-3h-3V9.6Zm9.9 0h4.4V11h-3v3h-1.4V9.6Z" })
7616
- });
7617
- }
7618
7610
  /** Card and rows reproduce the primitives' menu surface, like the session
7619
7611
  * menu next door: r7, inverted hairline, shadow-lv3, 2px inset. */
7620
7612
  const PICKER_CSS = [
@@ -7663,7 +7655,7 @@ window.__ModuleLoader__.load({
7663
7655
  /** The plan behind an `ExitPlanMode` approval, as the document it was written
7664
7656
  * as. The decision itself stays with the Host's approval dialog; this panel
7665
7657
  * is where the plan is actually read, under the reader's own prose palette. */
7666
- function ClaudePlanPanel({ useClaudeProjection, t, sessionId, closeDetails, maximized, toggleMaximized, submitChanges = sendPlanForChanges }) {
7658
+ function ClaudePlanPanel({ useClaudeProjection, t, sessionId, closeDetails, submitChanges = sendPlanForChanges }) {
7667
7659
  const markdownLabels = useClaudeMarkdownLabels(t);
7668
7660
  const owned = useClaudeProjection((projection) => projection.owned);
7669
7661
  const reviews = planReviews(useClaudeProjection((projection) => projection.activities));
@@ -7755,10 +7747,7 @@ window.__ModuleLoader__.load({
7755
7747
  });
7756
7748
  return /* @__PURE__ */ jsxs("div", {
7757
7749
  className: detailsCardClass,
7758
- style: {
7759
- ...tasksPanel,
7760
- ...maximized ? diffPanelMaximized : {}
7761
- },
7750
+ style: tasksPanel,
7762
7751
  children: [
7763
7752
  /* @__PURE__ */ jsxs("style", {
7764
7753
  "data-dsh-claude-panel-icon-styles": true,
@@ -7834,21 +7823,15 @@ window.__ModuleLoader__.load({
7834
7823
  style: badge(review.state),
7835
7824
  children: t(STATE_LABEL[review.state])
7836
7825
  })]
7837
- }), /* @__PURE__ */ jsxs("div", {
7826
+ }), /* @__PURE__ */ jsx("div", {
7838
7827
  style: planHeaderEnd,
7839
- children: [/* @__PURE__ */ jsx("button", {
7840
- type: "button",
7841
- className: panelIconButtonClass,
7842
- "aria-label": maximized ? t("planRestore") : t("planMaximize"),
7843
- onClick: toggleMaximized,
7844
- children: maximized ? /* @__PURE__ */ jsx(RestorePanelIcon$1, {}) : /* @__PURE__ */ jsx(IconFullscreenOutline16, {})
7845
- }), /* @__PURE__ */ jsx("button", {
7828
+ children: /* @__PURE__ */ jsx("button", {
7846
7829
  type: "button",
7847
7830
  className: panelIconButtonClass,
7848
7831
  "aria-label": t("planClose"),
7849
7832
  onClick: closeDetails,
7850
7833
  children: /* @__PURE__ */ jsx(IconCloseOutline16, {})
7851
- })]
7834
+ })
7852
7835
  })]
7853
7836
  }),
7854
7837
  /* @__PURE__ */ jsx("div", {
@@ -7970,10 +7953,10 @@ window.__ModuleLoader__.load({
7970
7953
  "width:6px;height:6px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#e2a03f);",
7971
7954
  "box-shadow:0 0 0 2px var(--dsw-alias-bg-base)}"
7972
7955
  ].join("");
7973
- let cssInjected$3 = false;
7974
- function ensureCss$3() {
7975
- if (cssInjected$3 || typeof document === "undefined") return;
7976
- cssInjected$3 = true;
7956
+ let cssInjected$2 = false;
7957
+ function ensureCss$2() {
7958
+ if (cssInjected$2 || typeof document === "undefined") return;
7959
+ cssInjected$2 = true;
7977
7960
  const element = document.createElement("style");
7978
7961
  element.dataset.dshClaudeHeaderPlan = "";
7979
7962
  element.textContent = ACTION_CSS$1;
@@ -8009,7 +7992,7 @@ window.__ModuleLoader__.load({
8009
7992
  if (!open) togglePlan();
8010
7993
  });
8011
7994
  if (!owned || review === void 0) return null;
8012
- ensureCss$3();
7995
+ ensureCss$2();
8013
7996
  const label = t(open ? "planClose" : "planOpen");
8014
7997
  return /* @__PURE__ */ jsx(Tooltip, {
8015
7998
  label,
@@ -8229,10 +8212,16 @@ window.__ModuleLoader__.load({
8229
8212
  sessionId
8230
8213
  } });
8231
8214
  }
8232
- async function cleanupMergedRepository(path, baseBranch) {
8215
+ /** `branch` names the merged branch when the checkout is no longer on it (a
8216
+ * linked pull request whose clone is back on base). Without a pull request
8217
+ * there is no base, and `requirePushed` has the Host refuse a branch whose
8218
+ * commits are on no remote. */
8219
+ async function cleanupMergedRepository(path, baseBranch, branch, requirePushed) {
8233
8220
  const body = await pluginWrite(`${CLAUDE_REPOSITORY_SETUP_PATH}/cleanup`, "remote", void 0, { json: {
8234
8221
  path,
8235
- baseBranch
8222
+ ...baseBranch === void 0 ? {} : { baseBranch },
8223
+ ...branch === void 0 ? {} : { branch },
8224
+ ...requirePushed === true ? { requirePushed: true } : {}
8236
8225
  } });
8237
8226
  if (body.mode !== "worktree" && body.mode !== "checkout" || typeof body.root !== "string" || typeof body.branch !== "string") throw new Error("Invalid repository cleanup response.");
8238
8227
  return body;
@@ -8321,7 +8310,13 @@ window.__ModuleLoader__.load({
8321
8310
  ],
8322
8311
  uiConversation: ["binding"],
8323
8312
  uiSession: ["provide"],
8324
- inputTriggers: ["registerSource"]
8313
+ inputTriggers: ["registerSource"],
8314
+ sidebarRight: [
8315
+ "openTabIn",
8316
+ "closeIn",
8317
+ "toggleExpanded"
8318
+ ],
8319
+ sidebarRightTabs: ["register"]
8325
8320
  };
8326
8321
  /** One line per service the Host no longer provides, or provides without a
8327
8322
  * method this plugin calls on it; empty when everything resolves. */
@@ -8594,24 +8589,32 @@ window.__ModuleLoader__.load({
8594
8589
  }
8595
8590
  /** Watches an open pull request and hands new review comments and failing
8596
8591
  * CI runs to Claude automatically until the user switches it off. */
8597
- function AutoFixControl({ sessionId, repository, running, t, submitPrompt }) {
8592
+ /** A prompt about a linked checkout says which one, so Claude does not
8593
+ * apply it to the session's own. */
8594
+ function linkedPreamble(repository, root, t) {
8595
+ return root === void 0 ? "" : `${t("linkedRepositoryPrompt", {
8596
+ root,
8597
+ branch: repository.branch ?? ""
8598
+ })}\n\n`;
8599
+ }
8600
+ function AutoFixControl({ sessionId, repository, root, running, t, submitPrompt }) {
8598
8601
  const pullRequest = repository.pullRequest;
8599
8602
  const open = pullRequest?.state === "open";
8600
8603
  const number = pullRequest?.number;
8601
8604
  const checks = pullRequest?.checks;
8602
- const [enabled, setEnabled] = useState(() => autoFixEnabled(sessionId));
8603
- const [focused, setFocused] = useState(false);
8605
+ const scope = root === void 0 ? sessionId : `${sessionId}#${root}`;
8606
+ const [enabled, setEnabled] = useState(() => autoFixEnabled(scope));
8604
8607
  useEffect(() => {
8605
- setEnabled(autoFixEnabled(sessionId));
8606
- }, [sessionId]);
8608
+ setEnabled(autoFixEnabled(scope));
8609
+ }, [scope]);
8607
8610
  useEffect(() => {
8608
8611
  if (!enabled || !open || running || number === void 0 || submitPrompt === void 0) return;
8609
8612
  let cancelled = false;
8610
8613
  const tick = async () => {
8611
- const [comments, failing] = await Promise.all([loadPullRequestThreads(sessionId, number).catch(() => []), checks === "failing" ? loadFailingChecks(sessionId, number).catch(() => []) : Promise.resolve([])]);
8614
+ const [comments, failing] = await Promise.all([loadPullRequestThreads(sessionId, number, void 0, root).catch(() => []), checks === "failing" ? loadFailingChecks(sessionId, number, void 0, root).catch(() => []) : Promise.resolve([])]);
8612
8615
  if (cancelled) return;
8613
- const plan = planAutoFix(autoFixMemory(sessionId), comments, failing);
8614
- if (plan.prompt !== void 0 && submitPrompt(plan.prompt, "idle")) rememberAutoFix(sessionId, plan.memory);
8616
+ const plan = planAutoFix(autoFixMemory(scope), comments, failing);
8617
+ if (plan.prompt !== void 0 && submitPrompt(`${linkedPreamble(repository, root, t)}${plan.prompt}`, "idle")) rememberAutoFix(scope, plan.memory);
8615
8618
  };
8616
8619
  tick();
8617
8620
  const timer = setInterval(() => {
@@ -8626,15 +8629,19 @@ window.__ModuleLoader__.load({
8626
8629
  enabled,
8627
8630
  number,
8628
8631
  open,
8632
+ repository,
8633
+ root,
8629
8634
  running,
8635
+ scope,
8630
8636
  sessionId,
8631
- submitPrompt
8637
+ submitPrompt,
8638
+ t
8632
8639
  ]);
8633
8640
  if (!open || submitPrompt === void 0) return null;
8634
8641
  const toggle = () => {
8635
8642
  const next = !enabled;
8636
8643
  setEnabled(next);
8637
- setAutoFixEnabled(sessionId, next);
8644
+ setAutoFixEnabled(scope, next);
8638
8645
  };
8639
8646
  return /* @__PURE__ */ jsx(Tooltip, {
8640
8647
  label: `${t("autoFixLabel")} · ${t("autoFixTitle")}`,
@@ -8644,19 +8651,9 @@ window.__ModuleLoader__.load({
8644
8651
  children: /* @__PURE__ */ jsx("button", {
8645
8652
  type: "button",
8646
8653
  role: "switch",
8654
+ className: repositoryAutoFixClass,
8647
8655
  "aria-checked": enabled,
8648
8656
  "aria-label": t("autoFixLabel"),
8649
- style: {
8650
- ...repositoryAutoFix,
8651
- ...enabled ? repositoryAutoFixActive : {},
8652
- ...focused ? heroWorktreeToggleFocused : {}
8653
- },
8654
- onFocus: () => {
8655
- setFocused(true);
8656
- },
8657
- onBlur: () => {
8658
- setFocused(false);
8659
- },
8660
8657
  onClick: (event) => {
8661
8658
  toggle();
8662
8659
  event.currentTarget.blur();
@@ -8676,7 +8673,8 @@ window.__ModuleLoader__.load({
8676
8673
  })
8677
8674
  });
8678
8675
  }
8679
- function FailingChecksControl({ sessionId, pullNumber, t, submitPrompt }) {
8676
+ function FailingChecksControl({ sessionId, repository, root, t, submitPrompt }) {
8677
+ const pullNumber = repository.pullRequest.number;
8680
8678
  const [open, setOpen] = useState(false);
8681
8679
  const [loading, setLoading] = useState(false);
8682
8680
  const [checks, setChecks] = useState([]);
@@ -8703,7 +8701,7 @@ window.__ModuleLoader__.load({
8703
8701
  controller.current = aborter;
8704
8702
  setLoading(true);
8705
8703
  setError(void 0);
8706
- loadFailingChecks(sessionId, pullNumber, aborter.signal).then((value) => {
8704
+ loadFailingChecks(sessionId, pullNumber, aborter.signal, root).then((value) => {
8707
8705
  setChecks(value);
8708
8706
  setLoading(false);
8709
8707
  }, (reason) => {
@@ -8769,7 +8767,7 @@ window.__ModuleLoader__.load({
8769
8767
  type: "button",
8770
8768
  style: repositoryChecksFix,
8771
8769
  onClick: () => {
8772
- submitPrompt(composeChecksPrompt(checks));
8770
+ submitPrompt(`${linkedPreamble(repository, root, t)}${composeChecksPrompt(checks)}`);
8773
8771
  setOpen(false);
8774
8772
  },
8775
8773
  children: t("checksCardFix")
@@ -8782,7 +8780,7 @@ window.__ModuleLoader__.load({
8782
8780
  * hides every other control on this bar, and the update-branch dialog that
8783
8781
  * started it takes its conflict list along when it closes -- so this one is
8784
8782
  * mounted from repository state instead, and survives being dismissed. */
8785
- function ConflictControl({ sessionId, repository, t, report, submitPrompt }) {
8783
+ function ConflictControl({ sessionId, repository, root, t, report, submitPrompt }) {
8786
8784
  const [dialog, setDialog] = useState();
8787
8785
  const operation = repository.operation;
8788
8786
  if (operation === void 0) return null;
@@ -8804,7 +8802,7 @@ window.__ModuleLoader__.load({
8804
8802
  message: "",
8805
8803
  includeUnstaged: false,
8806
8804
  push: dialog.push
8807
- }).then((result) => {
8805
+ }, root).then((result) => {
8808
8806
  if (result.conflicts !== void 0 && result.conflicts.length > 0) {
8809
8807
  setDialog({
8810
8808
  ...pending,
@@ -8906,7 +8904,7 @@ window.__ModuleLoader__.load({
8906
8904
  type: "button",
8907
8905
  style: diffModalConflictResolve,
8908
8906
  onClick: () => {
8909
- submitPrompt(composeConflictsPrompt(conflicts, operation, repository.pullRequest?.baseBranch));
8907
+ submitPrompt(`${linkedPreamble(repository, root, t)}${composeConflictsPrompt(conflicts, operation, repository.pullRequest?.baseBranch)}`);
8910
8908
  closeDialog();
8911
8909
  },
8912
8910
  children: t("conflictResolve")
@@ -8949,7 +8947,7 @@ window.__ModuleLoader__.load({
8949
8947
  if (pullRequest?.baseBranch === void 0) return false;
8950
8948
  return dialogOpen || pullRequest.state === "open" && repository.detached !== true && repository.dirty !== true && (repository.baseBehind ?? 0) > 0;
8951
8949
  }
8952
- function UpdateBranchControl({ sessionId, repository, t, report, submitPrompt }) {
8950
+ function UpdateBranchControl({ sessionId, repository, root, t, report, submitPrompt }) {
8953
8951
  const [dialog, setDialog] = useState();
8954
8952
  const [method, setMethod] = useState("rebase");
8955
8953
  const controller = useRef();
@@ -8967,7 +8965,7 @@ window.__ModuleLoader__.load({
8967
8965
  });
8968
8966
  const aborter = new AbortController();
8969
8967
  controller.current = aborter;
8970
- loadRepositoryActionPreview(sessionId, aborter.signal).then((preview) => {
8968
+ loadRepositoryActionPreview(sessionId, aborter.signal, root).then((preview) => {
8971
8969
  setDialog({
8972
8970
  loading: false,
8973
8971
  submitting: false,
@@ -9001,7 +8999,7 @@ window.__ModuleLoader__.load({
9001
8999
  includeUnstaged: false,
9002
9000
  baseBranch: base,
9003
9001
  mergeMethod: method
9004
- }).then((result) => {
9002
+ }, root).then((result) => {
9005
9003
  if (result.conflicts !== void 0 && result.conflicts.length > 0) {
9006
9004
  setDialog({
9007
9005
  ...pending,
@@ -9112,7 +9110,7 @@ window.__ModuleLoader__.load({
9112
9110
  type: "button",
9113
9111
  style: diffModalConflictResolve,
9114
9112
  onClick: () => {
9115
- submitPrompt(composeConflictsPrompt(dialog.conflicts ?? [], method, base));
9113
+ submitPrompt(`${linkedPreamble(repository, root, t)}${composeConflictsPrompt(dialog.conflicts ?? [], method, base)}`);
9116
9114
  closeDialog();
9117
9115
  },
9118
9116
  children: t("diffUpdateBranchResolve")
@@ -9135,14 +9133,17 @@ window.__ModuleLoader__.load({
9135
9133
  const [dialog, setDialog] = useState();
9136
9134
  const pullRequest = repository.pullRequest;
9137
9135
  const base = pullRequest?.baseBranch;
9138
- if (pullRequest?.state !== "merged" || base === void 0 || repository.root === void 0 || repository.detached === true) return null;
9136
+ const merged = pullRequest?.state === "merged" && base !== void 0;
9137
+ const unmerged = pullRequest === void 0 && repository.worktree === true && repository.dirty !== true;
9138
+ if (!merged && !unmerged || repository.root === void 0 || repository.detached === true) return null;
9139
9139
  const root = repository.root;
9140
9140
  const closeDialog = () => {
9141
9141
  if (dialog?.submitting !== true) setDialog(void 0);
9142
9142
  };
9143
9143
  const confirm = () => {
9144
9144
  setDialog({ submitting: true });
9145
- cleanupMergedRepository(root, base).then(async (result) => {
9145
+ const named = repository.pullRequestOnly === true || unmerged ? repository.branch : void 0;
9146
+ cleanupMergedRepository(root, base, named, unmerged).then(async (result) => {
9146
9147
  report(t("cleanupCompleted", { branch: result.branch }));
9147
9148
  setDialog(void 0);
9148
9149
  if (result.mode === "worktree" && deleteWorkspace !== void 0) await deleteWorkspace();
@@ -9174,7 +9175,7 @@ window.__ModuleLoader__.load({
9174
9175
  onClose: closeDialog,
9175
9176
  title: t("cleanupTitle"),
9176
9177
  closeLabel: t("diffCancel"),
9177
- description: t("cleanupDescription"),
9178
+ description: t(unmerged ? "cleanupDescriptionUnmerged" : "cleanupDescription"),
9178
9179
  footer: /* @__PURE__ */ jsxs("div", {
9179
9180
  style: diffModalFooter,
9180
9181
  children: [/* @__PURE__ */ jsx("button", {
@@ -9203,11 +9204,7 @@ window.__ModuleLoader__.load({
9203
9204
  style: diffModalMeta,
9204
9205
  children: [/* @__PURE__ */ jsxs("strong", {
9205
9206
  style: diffModalMetaText,
9206
- children: [
9207
- repository.branch ?? t("repositoryUnknownBranch"),
9208
- " → ",
9209
- base
9210
- ]
9207
+ children: [repository.branch ?? t("repositoryUnknownBranch"), base === void 0 ? "" : ` → ${base}`]
9211
9208
  }), /* @__PURE__ */ jsx("span", {
9212
9209
  style: diffModalFileState,
9213
9210
  children: repository.worktree === true ? t("repositoryWorktree") : t("repositoryLocal")
@@ -9226,7 +9223,7 @@ window.__ModuleLoader__.load({
9226
9223
  "squash",
9227
9224
  "rebase"
9228
9225
  ];
9229
- function MergePullRequestControl({ sessionId, repository, t, report }) {
9226
+ function MergePullRequestControl({ sessionId, repository, root, t, report }) {
9230
9227
  const [menuOpen, setMenuOpen] = useState(false);
9231
9228
  const [dialog, setDialog] = useState();
9232
9229
  const controller = useRef();
@@ -9244,7 +9241,7 @@ window.__ModuleLoader__.load({
9244
9241
  });
9245
9242
  const aborter = new AbortController();
9246
9243
  controller.current = aborter;
9247
- loadRepositoryActionPreview(sessionId, aborter.signal).then((preview) => {
9244
+ loadRepositoryActionPreview(sessionId, aborter.signal, root).then((preview) => {
9248
9245
  setDialog((current) => ({
9249
9246
  method,
9250
9247
  admin: current?.admin ?? false,
@@ -9281,8 +9278,9 @@ window.__ModuleLoader__.load({
9281
9278
  message: "",
9282
9279
  includeUnstaged: false,
9283
9280
  mergeMethod: dialog.method,
9284
- ...dialog.admin ? { admin: true } : {}
9285
- }).then(() => {
9281
+ ...dialog.admin ? { admin: true } : {},
9282
+ ...root === void 0 ? {} : { pullNumber: pullRequest.number }
9283
+ }, root).then(() => {
9286
9284
  report(t("diffMergeCompleted", { number: pullRequest.number }));
9287
9285
  setDialog(void 0);
9288
9286
  }, (error) => {
@@ -9410,12 +9408,121 @@ window.__ModuleLoader__.load({
9410
9408
  children: [/* @__PURE__ */ jsx("path", { d: "M6.8 9.2a3 3 0 0 0 4.2 0l2-2a3 3 0 0 0-4.2-4.2l-1 1" }), /* @__PURE__ */ jsx("path", { d: "M9.2 6.8a3 3 0 0 0-4.2 0l-2 2a3 3 0 0 0 4.2 4.2l1-1" })]
9411
9409
  });
9412
9410
  }
9411
+ /** Everything to the right of the branch: the diff counts and the pull
9412
+ * request's controls. One row for the session checkout and each linked one;
9413
+ * `root` scopes the requests to a linked checkout. */
9414
+ function RepositoryControls({ sessionId, repository, root, running, t, report, openDiff, submitPrompt, deleteWorkspace }) {
9415
+ const pullRequest = repository.pullRequest;
9416
+ const merged = pullRequest?.state === "merged";
9417
+ const mergedAge = merged ? relativeAge(pullRequest.mergedAt) : void 0;
9418
+ const aheadCount = repository.ahead ?? 0;
9419
+ const pushable = repository.remote !== void 0 && repository.detached !== true && (aheadCount > 0 || repository.upstream === false);
9420
+ const hasDiff = repository.diff !== void 0 && (repository.diff.additions > 0 || repository.diff.deletions > 0);
9421
+ const actionable = repository.pullRequestOnly !== true || root !== void 0;
9422
+ const prompt = submitPrompt === void 0 ? {} : { submitPrompt };
9423
+ return /* @__PURE__ */ jsxs("span", {
9424
+ style: repositoryStatusItems,
9425
+ children: [
9426
+ /* @__PURE__ */ jsx(ConflictControl, {
9427
+ sessionId,
9428
+ repository,
9429
+ root,
9430
+ t,
9431
+ report,
9432
+ ...prompt
9433
+ }),
9434
+ hasDiff || pushable ? /* @__PURE__ */ jsxs("button", {
9435
+ type: "button",
9436
+ style: {
9437
+ ...diffTrigger,
9438
+ ...merged ? diffTriggerMuted : {}
9439
+ },
9440
+ onClick: openDiff,
9441
+ "aria-label": t("diffOpen"),
9442
+ children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9443
+ style: merged ? diffAddMuted : diffAdd,
9444
+ children: ["+", repository.diff.additions]
9445
+ }), /* @__PURE__ */ jsxs("span", {
9446
+ style: merged ? diffDeleteMuted : diffDelete,
9447
+ children: ["−", repository.diff.deletions]
9448
+ })] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
9449
+ style: merged ? diffAheadMuted : diffAhead,
9450
+ children: ["↑", aheadCount > 0 ? aheadCount : ""]
9451
+ }) : null]
9452
+ }) : null,
9453
+ pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9454
+ style: repositoryMergedStatus,
9455
+ children: [
9456
+ /* @__PURE__ */ jsx("span", {
9457
+ style: repositoryMergedDot,
9458
+ "aria-hidden": "true"
9459
+ }),
9460
+ t("repositoryState_merged"),
9461
+ mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
9462
+ style: repositoryMergedAge,
9463
+ children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
9464
+ })
9465
+ ]
9466
+ }), /* @__PURE__ */ jsx(CleanupControl, {
9467
+ repository,
9468
+ t,
9469
+ report,
9470
+ ...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
9471
+ })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
9472
+ pullRequest.checks === "failing" && actionable ? /* @__PURE__ */ jsx(FailingChecksControl, {
9473
+ sessionId,
9474
+ repository: {
9475
+ ...repository,
9476
+ pullRequest
9477
+ },
9478
+ root,
9479
+ t,
9480
+ ...prompt
9481
+ }) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9482
+ label: t(`repositoryChecks_${pullRequest.checks}`),
9483
+ tone: pullRequest.checks === "passing" ? "success" : pullRequest.checks === "failing" ? "error" : "warning",
9484
+ children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9485
+ }),
9486
+ pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9487
+ label: t(`repositoryReview_${pullRequest.review}`),
9488
+ tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9489
+ children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9490
+ }),
9491
+ actionable ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
9492
+ /* @__PURE__ */ jsx(AutoFixControl, {
9493
+ sessionId,
9494
+ repository,
9495
+ root,
9496
+ running,
9497
+ t,
9498
+ ...prompt
9499
+ }),
9500
+ /* @__PURE__ */ jsx(UpdateBranchControl, {
9501
+ sessionId,
9502
+ repository,
9503
+ root,
9504
+ t,
9505
+ report,
9506
+ ...prompt
9507
+ }),
9508
+ /* @__PURE__ */ jsx(MergePullRequestControl, {
9509
+ sessionId,
9510
+ repository,
9511
+ root,
9512
+ t,
9513
+ report
9514
+ })
9515
+ ] }) : null
9516
+ ] })
9517
+ ]
9518
+ });
9519
+ }
9413
9520
  /** A checkout the session wrote into besides its own, stacked above the
9414
9521
  * session bar so that one stays put next to the composer. The same readout
9415
- * as the session bar, minus the controls that act on the session (auto-fix, update,
9416
- * merge, cleanup), which live with the checkout the session was opened on. */
9417
- function LinkedRepositoryBar({ repository, t, openDiff, report }) {
9418
- const key = repository.root ?? repository.cwd;
9522
+ * and controls as the session bar, scoped to that checkout; only the
9523
+ * workspace deletion after a clean-up stays with the session's own. */
9524
+ function LinkedRepositoryBar({ sessionId, repository, running, t, openDiff, report, submitPrompt }) {
9525
+ const key = repository.root ?? `${repository.remote ?? repository.cwd}#${repository.pullRequest?.number ?? ""}`;
9419
9526
  if (repository.status !== "ready") return /* @__PURE__ */ jsxs("div", {
9420
9527
  style: {
9421
9528
  ...repositoryBar,
@@ -9436,9 +9543,7 @@ window.__ModuleLoader__.load({
9436
9543
  })]
9437
9544
  });
9438
9545
  const branch = branchLabel(repository, t);
9439
- const pullRequest = repository.pullRequest;
9440
- const merged = pullRequest?.state === "merged";
9441
- const hasDiff = repository.diff !== void 0 && (repository.diff.additions > 0 || repository.diff.deletions > 0);
9546
+ const merged = repository.pullRequest?.state === "merged";
9442
9547
  return /* @__PURE__ */ jsxs("div", {
9443
9548
  style: {
9444
9549
  ...repositoryBar,
@@ -9474,42 +9579,15 @@ window.__ModuleLoader__.load({
9474
9579
  style: repositoryWorktree,
9475
9580
  children: t("repositoryWorktree")
9476
9581
  }) : null,
9477
- /* @__PURE__ */ jsxs("span", {
9478
- style: repositoryStatusItems,
9479
- children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs("button", {
9480
- type: "button",
9481
- style: {
9482
- ...diffTrigger,
9483
- ...merged ? diffTriggerMuted : {}
9484
- },
9485
- onClick: () => openDiff(repository.root),
9486
- "aria-label": t("diffOpen"),
9487
- children: [/* @__PURE__ */ jsxs("span", {
9488
- style: merged ? diffAddMuted : diffAdd,
9489
- children: ["+", repository.diff.additions]
9490
- }), /* @__PURE__ */ jsxs("span", {
9491
- style: merged ? diffDeleteMuted : diffDelete,
9492
- children: ["−", repository.diff.deletions]
9493
- })]
9494
- }) : null, pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9495
- style: repositoryMergedStatus,
9496
- children: [/* @__PURE__ */ jsx("span", {
9497
- style: repositoryMergedDot,
9498
- "aria-hidden": "true"
9499
- }), t("repositoryState_merged")]
9500
- }), /* @__PURE__ */ jsx(CleanupControl, {
9501
- repository,
9502
- t,
9503
- report
9504
- })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9505
- label: t(`repositoryChecks_${pullRequest.checks}`),
9506
- tone: pullRequest.checks === "passing" ? "success" : pullRequest.checks === "failing" ? "error" : "warning",
9507
- children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9508
- }), pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9509
- label: t(`repositoryReview_${pullRequest.review}`),
9510
- tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9511
- children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9512
- })] })]
9582
+ /* @__PURE__ */ jsx(RepositoryControls, {
9583
+ sessionId,
9584
+ repository,
9585
+ root: repository.root,
9586
+ running,
9587
+ t,
9588
+ report,
9589
+ openDiff: () => openDiff(repository.root),
9590
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9513
9591
  })
9514
9592
  ]
9515
9593
  });
@@ -9522,18 +9600,16 @@ window.__ModuleLoader__.load({
9522
9600
  const { toast, report } = useActionToast();
9523
9601
  if (blank || !projection.owned || repository === void 0) return null;
9524
9602
  const branch = branchLabel(repository, t);
9525
- const pullRequest = repository.pullRequest;
9526
- const merged = pullRequest?.state === "merged";
9527
- const mergedAge = merged ? relativeAge(pullRequest.mergedAt) : void 0;
9528
- const aheadCount = repository.ahead ?? 0;
9529
- const pushable = repository.remote !== void 0 && repository.detached !== true && (aheadCount > 0 || repository.upstream === false);
9530
- const hasDiff = repository.diff !== void 0 && (repository.diff.additions > 0 || repository.diff.deletions > 0);
9603
+ const merged = repository.pullRequest?.state === "merged";
9531
9604
  const linked = (projection.repositories ?? []).map((item) => /* @__PURE__ */ jsx(LinkedRepositoryBar, {
9605
+ sessionId,
9532
9606
  repository: item,
9607
+ running,
9533
9608
  t,
9534
9609
  openDiff,
9535
- report
9536
- }, item.root ?? item.cwd));
9610
+ report,
9611
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9612
+ }, item.root ?? `${item.remote ?? item.cwd}#${item.pullRequest?.number ?? ""}`));
9537
9613
  if (repository.status !== "ready") return /* @__PURE__ */ jsxs("div", {
9538
9614
  style: repositoryBarFrame,
9539
9615
  [CLAUDE_COMPOSER_BAR_ATTRIBUTE]: "",
@@ -9552,6 +9628,10 @@ window.__ModuleLoader__.load({
9552
9628
  style: repositoryBarFrame,
9553
9629
  [CLAUDE_COMPOSER_BAR_ATTRIBUTE]: "",
9554
9630
  children: [
9631
+ /* @__PURE__ */ jsx("style", {
9632
+ "data-dsh-claude-repository-bar-styles": true,
9633
+ children: repositoryAutoFixCss
9634
+ }),
9555
9635
  toast,
9556
9636
  linked,
9557
9637
  /* @__PURE__ */ jsxs("div", {
@@ -9600,91 +9680,15 @@ window.__ModuleLoader__.load({
9600
9680
  style: repositoryWorktree,
9601
9681
  children: t("repositoryWorktree")
9602
9682
  }) : null,
9603
- /* @__PURE__ */ jsxs("span", {
9604
- style: repositoryStatusItems,
9605
- children: [
9606
- /* @__PURE__ */ jsx(ConflictControl, {
9607
- sessionId,
9608
- repository,
9609
- t,
9610
- report,
9611
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9612
- }),
9613
- hasDiff || pushable ? /* @__PURE__ */ jsxs("button", {
9614
- type: "button",
9615
- style: {
9616
- ...diffTrigger,
9617
- ...merged ? diffTriggerMuted : {}
9618
- },
9619
- onClick: () => openDiff(),
9620
- "aria-label": t("diffOpen"),
9621
- children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9622
- style: merged ? diffAddMuted : diffAdd,
9623
- children: ["+", repository.diff.additions]
9624
- }), /* @__PURE__ */ jsxs("span", {
9625
- style: merged ? diffDeleteMuted : diffDelete,
9626
- children: ["−", repository.diff.deletions]
9627
- })] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
9628
- style: merged ? diffAheadMuted : diffAhead,
9629
- children: ["↑", aheadCount > 0 ? aheadCount : ""]
9630
- }) : null]
9631
- }) : null,
9632
- pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9633
- style: repositoryMergedStatus,
9634
- children: [
9635
- /* @__PURE__ */ jsx("span", {
9636
- style: repositoryMergedDot,
9637
- "aria-hidden": "true"
9638
- }),
9639
- t("repositoryState_merged"),
9640
- mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
9641
- style: repositoryMergedAge,
9642
- children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
9643
- })
9644
- ]
9645
- }), /* @__PURE__ */ jsx(CleanupControl, {
9646
- repository,
9647
- t,
9648
- report,
9649
- ...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
9650
- })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
9651
- pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
9652
- sessionId,
9653
- pullNumber: pullRequest.number,
9654
- t,
9655
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9656
- }) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9657
- label: t(`repositoryChecks_${pullRequest.checks}`),
9658
- tone: pullRequest.checks === "passing" ? "success" : "warning",
9659
- children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9660
- }),
9661
- pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9662
- label: t(`repositoryReview_${pullRequest.review}`),
9663
- tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9664
- children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9665
- }),
9666
- /* @__PURE__ */ jsx(AutoFixControl, {
9667
- sessionId,
9668
- repository,
9669
- running,
9670
- t,
9671
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9672
- }),
9673
- /* @__PURE__ */ jsx(UpdateBranchControl, {
9674
- sessionId,
9675
- repository,
9676
- t,
9677
- report,
9678
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9679
- }),
9680
- /* @__PURE__ */ jsx(MergePullRequestControl, {
9681
- sessionId,
9682
- repository,
9683
- t,
9684
- report
9685
- })
9686
- ] })
9687
- ]
9683
+ /* @__PURE__ */ jsx(RepositoryControls, {
9684
+ sessionId,
9685
+ repository,
9686
+ running,
9687
+ t,
9688
+ report,
9689
+ openDiff: () => openDiff(),
9690
+ ...submitPrompt === void 0 ? {} : { submitPrompt },
9691
+ ...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
9688
9692
  })
9689
9693
  ]
9690
9694
  })
@@ -9867,27 +9871,186 @@ window.__ModuleLoader__.load({
9867
9871
  });
9868
9872
  }
9869
9873
  //#endregion
9870
- //#region node_modules/.pnpm/dompurify@3.4.14/node_modules/dompurify/dist/purify.es.mjs
9871
- /*! @license DOMPurify 3.4.14 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.14/LICENSE */
9872
- function _arrayLikeToArray(r, a) {
9873
- (null == a || a > r.length) && (a = r.length);
9874
- for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
9875
- return n;
9876
- }
9877
- function _arrayWithHoles(r) {
9878
- if (Array.isArray(r)) return r;
9879
- }
9880
- function _iterableToArrayLimit(r, l) {
9881
- var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
9882
- if (null != t) {
9883
- var e, n, i, u, a = [], f = true, o = false;
9874
+ //#region src/client/ClaudeQueueDock.tsx
9875
+ /** Replaces the Host queue strip (same slot cell, lower priority) with one
9876
+ * styled like the repository status bar. Behaviour mirrors the Host strip:
9877
+ * one row inline, several rows behind a collapsible count, edit/remove/steer. */
9878
+ function ClaudeQueueDock({ useSession, t, updateQueue, notify }) {
9879
+ const inbox = useSession((session) => session.queue);
9880
+ const queue = useMemo(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
9881
+ const running = useSession((session) => session.running);
9882
+ const mutable = useSession((session) => session.subagent === null);
9883
+ const [editing, setEditing] = useState();
9884
+ const [busy, setBusy] = useState();
9885
+ const [collapsed, setCollapsed] = useState(true);
9886
+ const listId = useId();
9887
+ useEffect(() => {
9888
+ if (queue.length === 0 && !collapsed) setCollapsed(true);
9889
+ if (editing !== void 0 && (!mutable || !queue.some((row) => row.id === editing.id))) setEditing(void 0);
9890
+ }, [
9891
+ collapsed,
9892
+ editing,
9893
+ mutable,
9894
+ queue
9895
+ ]);
9896
+ if (queue.length === 0) return null;
9897
+ const interacting = mutable && (editing !== void 0 || busy !== void 0);
9898
+ const expanded = !collapsed || interacting;
9899
+ const listVisible = queue.length === 1 || expanded;
9900
+ const apply = async (id, action, failure) => {
9901
+ setBusy(id);
9884
9902
  try {
9885
- if (i = (t = t.call(r)).next, 0 === l);
9886
- else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
9887
- } catch (r) {
9888
- o = true, n = r;
9903
+ await updateQueue(id, action);
9904
+ return true;
9905
+ } catch {
9906
+ notify("error", failure);
9907
+ return false;
9889
9908
  } finally {
9890
- try {
9909
+ setBusy((current) => current === id ? void 0 : current);
9910
+ }
9911
+ };
9912
+ const saveEdit = async () => {
9913
+ if (editing === void 0 || editing.text.trim() === "") return;
9914
+ if (await apply(editing.id, {
9915
+ kind: "edit",
9916
+ content: [{
9917
+ type: "text",
9918
+ text: editing.text
9919
+ }]
9920
+ }, t("queueEditFailed"))) setEditing(void 0);
9921
+ };
9922
+ const action = (label, icon, onClick, disabled, hint) => /* @__PURE__ */ jsx(Tooltip, {
9923
+ label: hint ?? label,
9924
+ side: "bottom",
9925
+ delayMs: 400,
9926
+ children: /* @__PURE__ */ jsx("button", {
9927
+ type: "button",
9928
+ className: panelIconButtonClass,
9929
+ "aria-label": label,
9930
+ disabled,
9931
+ onClick,
9932
+ children: icon
9933
+ })
9934
+ });
9935
+ return /* @__PURE__ */ jsxs("div", {
9936
+ style: repositoryBarFrame,
9937
+ "data-claude-queue-dock": "",
9938
+ children: [/* @__PURE__ */ jsx("style", {
9939
+ "data-dsh-claude-queue-styles": true,
9940
+ children: panelIconButtonCss
9941
+ }), /* @__PURE__ */ jsxs("div", {
9942
+ style: queueBar,
9943
+ children: [queue.length > 1 ? /* @__PURE__ */ jsxs("button", {
9944
+ type: "button",
9945
+ style: queueHeader,
9946
+ "aria-controls": listId,
9947
+ "aria-expanded": expanded,
9948
+ disabled: interacting,
9949
+ onClick: () => {
9950
+ setCollapsed((value) => !value);
9951
+ },
9952
+ children: [
9953
+ /* @__PURE__ */ jsx("span", {
9954
+ style: queueLead,
9955
+ "aria-hidden": "true",
9956
+ children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
9957
+ }),
9958
+ /* @__PURE__ */ jsx("span", {
9959
+ style: queueCount,
9960
+ children: t("queueCount", { n: queue.length })
9961
+ }),
9962
+ /* @__PURE__ */ jsx("span", {
9963
+ style: queueLead,
9964
+ "aria-hidden": "true",
9965
+ children: expanded ? /* @__PURE__ */ jsx(IconChevronDownOutline14, {}) : /* @__PURE__ */ jsx(IconChevronUpOutline14, {})
9966
+ })
9967
+ ]
9968
+ }) : null, /* @__PURE__ */ jsx("ul", {
9969
+ id: listId,
9970
+ style: queueList,
9971
+ hidden: !listVisible,
9972
+ children: listVisible ? queue.map((row, index) => /* @__PURE__ */ jsxs("li", {
9973
+ style: {
9974
+ ...queueRow,
9975
+ ...index > 0 ? queueRowDivider : {}
9976
+ },
9977
+ children: [
9978
+ queue.length === 1 ? /* @__PURE__ */ jsx("span", {
9979
+ style: queueLead,
9980
+ "aria-hidden": "true",
9981
+ children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
9982
+ }) : null,
9983
+ editing?.id === row.id ? /* @__PURE__ */ jsx("input", {
9984
+ autoFocus: true,
9985
+ style: queueEditor,
9986
+ "aria-label": t("queueEdit"),
9987
+ value: editing.text,
9988
+ onChange: (event) => {
9989
+ setEditing({
9990
+ id: row.id,
9991
+ text: event.currentTarget.value
9992
+ });
9993
+ },
9994
+ onKeyDown: (event) => {
9995
+ if (event.key === "Escape") setEditing(void 0);
9996
+ else if (event.key === "Enter" && !event.nativeEvent.isComposing) {
9997
+ event.preventDefault();
9998
+ saveEdit();
9999
+ }
10000
+ }
10001
+ }) : /* @__PURE__ */ jsx("span", {
10002
+ style: queuePreview,
10003
+ children: row.preview
10004
+ }),
10005
+ mutable ? /* @__PURE__ */ jsx("span", {
10006
+ style: queueActions,
10007
+ children: editing?.id === row.id ? /* @__PURE__ */ jsxs(Fragment$1, { children: [action(t("queueSave"), /* @__PURE__ */ jsx(IconCheckOutline16, { size: 14 }), () => {
10008
+ saveEdit();
10009
+ }, busy !== void 0 || editing.text.trim() === ""), action(t("queueCancelEdit"), /* @__PURE__ */ jsx(IconCloseOutline16, { size: 14 }), () => {
10010
+ setEditing(void 0);
10011
+ }, busy !== void 0)] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
10012
+ action(t("queueEdit"), /* @__PURE__ */ jsx(IconEditOutline16, { size: 14 }), () => {
10013
+ if (row.text !== null) setEditing({
10014
+ id: row.id,
10015
+ text: row.text
10016
+ });
10017
+ }, busy !== void 0 || row.text === null, row.text === null ? t("queueEditUnsupported") : void 0),
10018
+ action(t("queueRemove"), /* @__PURE__ */ jsx(IconTrashOutline16, { size: 14 }), () => {
10019
+ apply(row.id, { kind: "remove" }, t("queueRemoveFailed"));
10020
+ }, busy !== void 0),
10021
+ action(t("queueSteer"), /* @__PURE__ */ jsx(IconSendOutline14, {}), () => {
10022
+ apply(row.id, { kind: "steer" }, t("queueSteerFailed"));
10023
+ }, busy !== void 0 || !running, running ? void 0 : t("queueSteerUnavailable"))
10024
+ ] })
10025
+ }) : null
10026
+ ]
10027
+ }, row.id)) : null
10028
+ })]
10029
+ })]
10030
+ });
10031
+ }
10032
+ //#endregion
10033
+ //#region node_modules/.pnpm/dompurify@3.4.14/node_modules/dompurify/dist/purify.es.mjs
10034
+ /*! @license DOMPurify 3.4.14 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.14/LICENSE */
10035
+ function _arrayLikeToArray(r, a) {
10036
+ (null == a || a > r.length) && (a = r.length);
10037
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10038
+ return n;
10039
+ }
10040
+ function _arrayWithHoles(r) {
10041
+ if (Array.isArray(r)) return r;
10042
+ }
10043
+ function _iterableToArrayLimit(r, l) {
10044
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
10045
+ if (null != t) {
10046
+ var e, n, i, u, a = [], f = true, o = false;
10047
+ try {
10048
+ if (i = (t = t.call(r)).next, 0 === l);
10049
+ else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
10050
+ } catch (r) {
10051
+ o = true, n = r;
10052
+ } finally {
10053
+ try {
9891
10054
  if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
9892
10055
  } finally {
9893
10056
  if (o) throw n;
@@ -14908,24 +15071,15 @@ window.__ModuleLoader__.load({
14908
15071
  "update-branch": ready && hasOpenPullRequest && repository?.dirty !== true && (repository?.baseBehind ?? 0) > 0
14909
15072
  };
14910
15073
  }
14911
- function RestorePanelIcon() {
14912
- return /* @__PURE__ */ jsx("svg", {
14913
- width: "16",
14914
- height: "16",
14915
- viewBox: "0 0 16 16",
14916
- fill: "currentColor",
14917
- "aria-hidden": "true",
14918
- children: /* @__PURE__ */ jsx("path", { d: "M1.5 5h3V2h1.4v4.4H1.5V5Zm9.9-3h1.4v3h3v1.4h-4.4V2ZM1.5 9.6h4.4V14H4.5v-3h-3V9.6Zm9.9 0h4.4V11h-3v3h-1.4V9.6Z" })
14919
- });
14920
- }
14921
15074
  function hasChanges(repository) {
14922
15075
  return (repository?.diff?.additions ?? 0) > 0 || (repository?.diff?.deletions ?? 0) > 0;
14923
15076
  }
14924
- /** The checkouts a session can show: its own first, then the ones it wrote into. */
15077
+ /** The checkouts a session can show: its own first, then the ones it wrote
15078
+ * into. A linked pull request with no local checkout has no diff to show. */
14925
15079
  function panelRepositories(projection) {
14926
- return [projection.repository, ...projection.repositories ?? []].filter((item) => item !== void 0);
15080
+ return [projection.repository, ...(projection.repositories ?? []).filter((item) => item.diff !== void 0)].filter((item) => item !== void 0);
14927
15081
  }
14928
- function ClaudeDiffPanel({ useClaudeProjection, t, sessionId, maximized, closeDetails, toggleMaximized, submitPrompt, initialRoot }) {
15082
+ function ClaudeDiffPanel({ useClaudeProjection, t, sessionId, closeDetails, submitPrompt, initialRoot }) {
14929
15083
  const projection = useClaudeProjection((value) => value);
14930
15084
  const repositories = useMemo(() => panelRepositories(projection), [projection]);
14931
15085
  const [selectedRoot, setSelectedRoot] = useState(() => initialRoot ?? (hasChanges(projection.repository) ? void 0 : repositories.find(hasChanges)?.root));
@@ -15354,10 +15508,7 @@ window.__ModuleLoader__.load({
15354
15508
  }),
15355
15509
  /* @__PURE__ */ jsxs("div", {
15356
15510
  className: detailsCardClass,
15357
- style: {
15358
- ...diffPanel,
15359
- ...maximized ? diffPanelMaximized : {}
15360
- },
15511
+ style: diffPanel,
15361
15512
  children: [
15362
15513
  /* @__PURE__ */ jsxs("header", {
15363
15514
  style: diffHeader,
@@ -15481,13 +15632,6 @@ window.__ModuleLoader__.load({
15481
15632
  children: /* @__PURE__ */ jsx("path", { d: "M2.75 2.75h10.5a1 1 0 0 1 1 1v6.5a1 1 0 0 1-1 1H8.2l-3.2 2.9v-2.9H2.75a1 1 0 0 1-1-1v-6.5a1 1 0 0 1 1-1Z" })
15482
15633
  }), openThreadCount]
15483
15634
  }) : null,
15484
- /* @__PURE__ */ jsx("button", {
15485
- type: "button",
15486
- className: panelIconButtonClass,
15487
- "aria-label": maximized ? t("diffRestore") : t("diffMaximize"),
15488
- onClick: toggleMaximized,
15489
- children: maximized ? /* @__PURE__ */ jsx(RestorePanelIcon, {}) : /* @__PURE__ */ jsx(IconFullscreenOutline16, {})
15490
- }),
15491
15635
  /* @__PURE__ */ jsx("button", {
15492
15636
  type: "button",
15493
15637
  className: panelIconButtonClass,
@@ -15530,35 +15674,45 @@ window.__ModuleLoader__.load({
15530
15674
  /* @__PURE__ */ jsxs("div", {
15531
15675
  ref: diffBodyRef,
15532
15676
  style: diffBody,
15533
- children: [diff.truncated ? /* @__PURE__ */ jsx("p", {
15534
- style: diffNotice,
15535
- children: t("diffTruncated")
15536
- }) : null, files.length === 0 ? /* @__PURE__ */ jsx("p", {
15537
- style: diffEmpty,
15538
- children: t("diffEmpty")
15539
- }) : files.map((file, index) => /* @__PURE__ */ jsx(DiffFileSection, {
15540
- file,
15541
- root: repository.root,
15542
- open: fileOpen(file.path, index),
15543
- onOpenChange: (open) => {
15544
- setFileOpen(file.path, open);
15545
- },
15546
- t,
15547
- comments: reviewComments.filter((comment) => comment.path === file.path),
15548
- ghThreads: ghThreads.filter((thread) => thread.path === file.path),
15549
- suggestMention,
15550
- now: threadsLoadedAt,
15551
- activeTargetKey,
15552
- onReplyToThread: replyToThread,
15553
- onThreadResolvedChange: changeThreadResolved,
15554
- onSendThreadToAi: submitPrompt === void 0 ? void 0 : (thread) => {
15555
- submitPrompt(composeCommentsPrompt([thread]));
15556
- },
15557
- editorAnchor: commentEditor !== void 0 && commentEditor.path === file.path ? commentEditor : void 0,
15558
- editorNode: commentEditorNode,
15559
- onOpenEditor: (anchor) => openCommentEditor(file.path, anchor),
15560
- onRemoveComment: removeComment
15561
- }, file.path))]
15677
+ children: [
15678
+ diff.truncated ? /* @__PURE__ */ jsx("p", {
15679
+ style: diffNotice,
15680
+ children: t("diffTruncated")
15681
+ }) : null,
15682
+ diff.elided !== void 0 && diff.elided.length > 0 ? /* @__PURE__ */ jsx("p", {
15683
+ style: diffNotice,
15684
+ children: t("diffElided", {
15685
+ count: diff.elided.length,
15686
+ files: diff.elided.join(", ")
15687
+ })
15688
+ }) : null,
15689
+ files.length === 0 ? /* @__PURE__ */ jsx("p", {
15690
+ style: diffEmpty,
15691
+ children: t("diffEmpty")
15692
+ }) : files.map((file, index) => /* @__PURE__ */ jsx(DiffFileSection, {
15693
+ file,
15694
+ root: repository.pullRequestOnly === true ? void 0 : repository.root,
15695
+ open: fileOpen(file.path, index),
15696
+ onOpenChange: (open) => {
15697
+ setFileOpen(file.path, open);
15698
+ },
15699
+ t,
15700
+ comments: reviewComments.filter((comment) => comment.path === file.path),
15701
+ ghThreads: ghThreads.filter((thread) => thread.path === file.path),
15702
+ suggestMention,
15703
+ now: threadsLoadedAt,
15704
+ activeTargetKey,
15705
+ onReplyToThread: replyToThread,
15706
+ onThreadResolvedChange: changeThreadResolved,
15707
+ onSendThreadToAi: submitPrompt === void 0 ? void 0 : (thread) => {
15708
+ submitPrompt(composeCommentsPrompt([thread]));
15709
+ },
15710
+ editorAnchor: commentEditor !== void 0 && commentEditor.path === file.path ? commentEditor : void 0,
15711
+ editorNode: commentEditorNode,
15712
+ onOpenEditor: (anchor) => openCommentEditor(file.path, anchor),
15713
+ onRemoveComment: removeComment
15714
+ }, file.path))
15715
+ ]
15562
15716
  })
15563
15717
  ]
15564
15718
  }),
@@ -15714,247 +15868,191 @@ window.__ModuleLoader__.load({
15714
15868
  ] });
15715
15869
  }
15716
15870
  //#endregion
15717
- //#region src/client/ClaudePanelOverlay.tsx
15718
- const useClientLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
15719
- function shouldRestoreFromEscape(event, root = document) {
15720
- return event.key === "Escape" && root.querySelector("[role=\"dialog\"][aria-modal=\"true\"]") === null;
15721
- }
15722
- function workspaceBounds(overlay) {
15723
- const frame = overlay.parentElement;
15724
- if (frame === null) return void 0;
15725
- const overlayRect = overlay.getBoundingClientRect();
15726
- const candidates = Array.from(frame.children).filter((element) => element !== overlay).map((element) => element.getBoundingClientRect()).filter((rect) => rect.width > 0 && rect.height > 0);
15727
- const insetCandidates = candidates.filter((rect) => rect.left > overlayRect.left);
15728
- const workspace = (insetCandidates.length > 0 ? insetCandidates : candidates).reduce((largest, rect) => {
15729
- if (largest === void 0 || rect.width * rect.height > largest.width * largest.height) return rect;
15730
- return largest;
15731
- }, void 0);
15732
- if (workspace === void 0) return void 0;
15733
- const left = Math.max(0, workspace.left - overlayRect.left);
15734
- const top = Math.max(0, workspace.top - overlayRect.top);
15871
+ //#region src/client/sidebar-tabs.tsx
15872
+ /**
15873
+ * The plugin's panels as tabs of the Host's right sidebar.
15874
+ *
15875
+ * Host 0.1.5 removed the `details` column this package used to register a
15876
+ * single panel into, and replaced it with a tabbed, splittable, floatable right
15877
+ * sidebar (`@deepseek-ai/dsh-client-ui-sidebar-right`). A panel there is a
15878
+ * tab *type*: declared once in `ctx.sidebarRightTabs`, with its body registered
15879
+ * into `sidebar.right.pane.tab` under the type's id, and opened per session
15880
+ * through `ctx.sidebarRight.openTabIn`. Reopening a type that is already open
15881
+ * focuses that tab and re-delivers the navigation params, so a second "show
15882
+ * tasks" press for another turn lands in the same tab.
15883
+ *
15884
+ * The Host owns fullscreen and closing now: the sidebar chrome carries its
15885
+ * own fullscreen toggle, so the panels draw none, and a panel's close button
15886
+ * closes its tab.
15887
+ */
15888
+ const CLAUDE_TAB_KINDS = {
15889
+ diff: "claude-diff",
15890
+ plan: "claude-plan",
15891
+ tasks: "claude-tasks",
15892
+ overview: "claude-overview"
15893
+ };
15894
+ /** Which tab (if any) each kind currently occupies, per session. */
15895
+ var TabOccupancy = class TabOccupancy {
15896
+ #byKey = /* @__PURE__ */ new Map();
15897
+ #listeners = /* @__PURE__ */ new Map();
15898
+ static key(kind, sessionId) {
15899
+ return `${kind}\0${sessionId}`;
15900
+ }
15901
+ tabId(kind, sessionId) {
15902
+ return this.#byKey.get(TabOccupancy.key(kind, sessionId));
15903
+ }
15904
+ note(kind, sessionId, tabId) {
15905
+ const key = TabOccupancy.key(kind, sessionId);
15906
+ const previous = this.#byKey.get(key);
15907
+ if (tabId === void 0) this.#byKey.delete(key);
15908
+ else this.#byKey.set(key, tabId);
15909
+ if (previous === tabId) return;
15910
+ for (const listener of this.#listeners.get(key) ?? []) listener();
15911
+ }
15912
+ source(kind, sessionId) {
15913
+ const key = TabOccupancy.key(kind, sessionId);
15914
+ return {
15915
+ subscribe: (listener) => {
15916
+ let set = this.#listeners.get(key);
15917
+ if (set === void 0) {
15918
+ set = /* @__PURE__ */ new Set();
15919
+ this.#listeners.set(key, set);
15920
+ }
15921
+ set.add(listener);
15922
+ return () => {
15923
+ set.delete(listener);
15924
+ };
15925
+ },
15926
+ getSnapshot: () => this.#byKey.has(key)
15927
+ };
15928
+ }
15929
+ };
15930
+ /** Bind a panel body to the tab it lives in: report occupancy, and map the
15931
+ * tab surface onto the panel's close / maximize props. */
15932
+ function useClaudeTab(useTabInfo, noteTab) {
15933
+ const info = useTabInfo();
15934
+ const tabId = info.tab.id;
15935
+ useEffect(() => {
15936
+ noteTab(tabId);
15937
+ return () => {
15938
+ noteTab(void 0);
15939
+ };
15940
+ }, [noteTab, tabId]);
15941
+ const actions = info.tab.actions;
15735
15942
  return {
15736
- left,
15737
- top,
15738
- width: Math.max(0, Math.min(workspace.width, overlayRect.width - left)),
15739
- height: Math.max(0, Math.min(workspace.height, overlayRect.height - top))
15943
+ closeDetails: useCallback(() => {
15944
+ actions.close();
15945
+ }, [actions]),
15946
+ params: info.tab.navigation.params ?? {}
15740
15947
  };
15741
15948
  }
15742
- function observeWorkspaceBounds(overlay, listener) {
15743
- const frame = overlay.parentElement;
15744
- if (frame === null) return () => {};
15745
- const update = () => {
15746
- const next = workspaceBounds(overlay);
15747
- if (next !== void 0) listener(next);
15748
- };
15749
- update();
15750
- const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(update);
15751
- observer?.observe(frame);
15752
- for (const element of Array.from(frame.children)) observer?.observe(element);
15753
- const mutation = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(update);
15754
- mutation?.observe(frame, {
15755
- attributes: true,
15756
- childList: true
15949
+ function ClaudeDiffTab({ useTabInfo, noteTab, ...panel }) {
15950
+ const { closeDetails, params } = useClaudeTab(useTabInfo, noteTab);
15951
+ return /* @__PURE__ */ jsx(ClaudeDiffPanel, {
15952
+ ...panel,
15953
+ closeDetails,
15954
+ ...params.initialRoot === void 0 ? {} : { initialRoot: params.initialRoot }
15757
15955
  });
15758
- window.addEventListener("resize", update);
15759
- return () => {
15760
- observer?.disconnect();
15761
- mutation?.disconnect();
15762
- window.removeEventListener("resize", update);
15763
- };
15764
15956
  }
15765
- function ClaudePanelOverlay({ children, onRestore }) {
15766
- const ref = useRef(null);
15767
- const [bounds, setBounds] = useState();
15768
- useClientLayoutEffect(() => {
15769
- const overlay = ref.current?.closest("[data-shell-overlay]");
15770
- if (overlay === void 0 || overlay === null) return;
15771
- return observeWorkspaceBounds(overlay, (next) => {
15772
- setBounds((current) => current?.left === next.left && current.top === next.top && current.width === next.width && current.height === next.height ? current : next);
15773
- });
15774
- }, []);
15775
- useEffect(() => {
15776
- const escape = (event) => {
15777
- if (shouldRestoreFromEscape(event)) onRestore();
15778
- };
15779
- window.addEventListener("keydown", escape);
15780
- return () => window.removeEventListener("keydown", escape);
15781
- }, [onRestore]);
15782
- return /* @__PURE__ */ jsx("div", {
15783
- ref,
15784
- "data-dsh-claude-panel-overlay": true,
15785
- style: {
15786
- position: "absolute",
15787
- left: bounds?.left ?? 0,
15788
- top: bounds?.top ?? 0,
15789
- width: bounds?.width ?? 0,
15790
- height: bounds?.height ?? 0,
15791
- visibility: bounds === void 0 ? "hidden" : "visible",
15792
- padding: 8,
15793
- boxSizing: "border-box",
15794
- background: "var(--dsw-alias-bg-base)",
15795
- pointerEvents: bounds === void 0 ? "none" : "auto"
15796
- },
15797
- children
15957
+ function ClaudePlanTab({ useTabInfo, noteTab, ...panel }) {
15958
+ const { closeDetails } = useClaudeTab(useTabInfo, noteTab);
15959
+ return /* @__PURE__ */ jsx(ClaudePlanPanel, {
15960
+ ...panel,
15961
+ closeDetails
15798
15962
  });
15799
15963
  }
15800
- //#endregion
15801
- //#region src/client/ClaudeQueueDock.tsx
15802
- /** Replaces the Host queue strip (same slot cell, lower priority) with one
15803
- * styled like the repository status bar. Behaviour mirrors the Host strip:
15804
- * one row inline, several rows behind a collapsible count, edit/remove/steer. */
15805
- function ClaudeQueueDock({ useSession, t, updateQueue, notify }) {
15806
- const inbox = useSession((session) => session.queue);
15807
- const queue = useMemo(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
15808
- const running = useSession((session) => session.running);
15809
- const mutable = useSession((session) => session.subagent === null);
15810
- const [editing, setEditing] = useState();
15811
- const [busy, setBusy] = useState();
15812
- const [collapsed, setCollapsed] = useState(true);
15813
- const listId = useId();
15814
- useEffect(() => {
15815
- if (queue.length === 0 && !collapsed) setCollapsed(true);
15816
- if (editing !== void 0 && (!mutable || !queue.some((row) => row.id === editing.id))) setEditing(void 0);
15817
- }, [
15818
- collapsed,
15819
- editing,
15820
- mutable,
15821
- queue
15822
- ]);
15823
- if (queue.length === 0) return null;
15824
- const interacting = mutable && (editing !== void 0 || busy !== void 0);
15825
- const expanded = !collapsed || interacting;
15826
- const listVisible = queue.length === 1 || expanded;
15827
- const apply = async (id, action, failure) => {
15828
- setBusy(id);
15829
- try {
15830
- await updateQueue(id, action);
15831
- return true;
15832
- } catch {
15833
- notify("error", failure);
15834
- return false;
15835
- } finally {
15836
- setBusy((current) => current === id ? void 0 : current);
15837
- }
15838
- };
15839
- const saveEdit = async () => {
15840
- if (editing === void 0 || editing.text.trim() === "") return;
15841
- if (await apply(editing.id, {
15842
- kind: "edit",
15843
- content: [{
15844
- type: "text",
15845
- text: editing.text
15846
- }]
15847
- }, t("queueEditFailed"))) setEditing(void 0);
15964
+ function ClaudeTasksTab({ useTabInfo, noteTab, ...panel }) {
15965
+ const { closeDetails, params } = useClaudeTab(useTabInfo, noteTab);
15966
+ return /* @__PURE__ */ jsx(ClaudeTasksPanel, {
15967
+ ...panel,
15968
+ closeDetails,
15969
+ turn: params.turn ?? 0
15970
+ });
15971
+ }
15972
+ function ClaudeOverviewTab({ useTabInfo, noteTab, face }) {
15973
+ const { closeDetails } = useClaudeTab(useTabInfo, noteTab);
15974
+ if (face === void 0) return null;
15975
+ return /* @__PURE__ */ jsx(ClaudePullRequestsPanel, {
15976
+ ...face,
15977
+ closeDetails
15978
+ });
15979
+ }
15980
+ /** Declare the four tab types and their bodies; returns the per-session
15981
+ * open / close / toggle face the rest of the client drives them through. */
15982
+ function registerClaudeSidebarTabs(ctx, options) {
15983
+ const { t, namespace } = options;
15984
+ const occupancy = new TabOccupancy();
15985
+ const faceFor = (kind, sessionId) => ({ noteTab: (tabId) => {
15986
+ occupancy.note(kind, sessionId, tabId);
15987
+ } });
15988
+ const titles = {
15989
+ [CLAUDE_TAB_KINDS.diff]: () => t("diffTabTitle"),
15990
+ [CLAUDE_TAB_KINDS.plan]: () => t("planPanelTitle"),
15991
+ [CLAUDE_TAB_KINDS.tasks]: () => t("tasksPanel"),
15992
+ [CLAUDE_TAB_KINDS.overview]: () => t("overviewTitle")
15848
15993
  };
15849
- const action = (label, icon, onClick, disabled, hint) => /* @__PURE__ */ jsx(Tooltip, {
15850
- label: hint ?? label,
15851
- side: "bottom",
15852
- delayMs: 400,
15853
- children: /* @__PURE__ */ jsx("button", {
15854
- type: "button",
15855
- className: panelIconButtonClass,
15856
- "aria-label": label,
15857
- disabled,
15858
- onClick,
15859
- children: icon
15994
+ for (const kind of Object.values(CLAUDE_TAB_KINDS)) ctx.effect(() => ctx.sidebarRightTabs.register({
15995
+ id: kind,
15996
+ kind,
15997
+ priority: "extension",
15998
+ title: titles[kind]
15999
+ }), `dsh-claude: ${kind} tab type`);
16000
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16001
+ name: "sidebar.right.pane.tab",
16002
+ key: CLAUDE_TAB_KINDS.diff,
16003
+ locale: namespace,
16004
+ inject: (sessionId) => ({
16005
+ t,
16006
+ sessionId,
16007
+ ...faceFor(CLAUDE_TAB_KINDS.diff, sessionId),
16008
+ ...options.diffFace(sessionId)
15860
16009
  })
15861
- });
15862
- return /* @__PURE__ */ jsxs("div", {
15863
- style: repositoryBarFrame,
15864
- "data-claude-queue-dock": "",
15865
- children: [/* @__PURE__ */ jsx("style", {
15866
- "data-dsh-claude-queue-styles": true,
15867
- children: panelIconButtonCss
15868
- }), /* @__PURE__ */ jsxs("div", {
15869
- style: queueBar,
15870
- children: [queue.length > 1 ? /* @__PURE__ */ jsxs("button", {
15871
- type: "button",
15872
- style: queueHeader,
15873
- "aria-controls": listId,
15874
- "aria-expanded": expanded,
15875
- disabled: interacting,
15876
- onClick: () => {
15877
- setCollapsed((value) => !value);
15878
- },
15879
- children: [
15880
- /* @__PURE__ */ jsx("span", {
15881
- style: queueLead,
15882
- "aria-hidden": "true",
15883
- children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
15884
- }),
15885
- /* @__PURE__ */ jsx("span", {
15886
- style: queueCount,
15887
- children: t("queueCount", { n: queue.length })
15888
- }),
15889
- /* @__PURE__ */ jsx("span", {
15890
- style: queueLead,
15891
- "aria-hidden": "true",
15892
- children: expanded ? /* @__PURE__ */ jsx(IconChevronDownOutline14, {}) : /* @__PURE__ */ jsx(IconChevronUpOutline14, {})
15893
- })
15894
- ]
15895
- }) : null, /* @__PURE__ */ jsx("ul", {
15896
- id: listId,
15897
- style: queueList,
15898
- hidden: !listVisible,
15899
- children: listVisible ? queue.map((row, index) => /* @__PURE__ */ jsxs("li", {
15900
- style: {
15901
- ...queueRow,
15902
- ...index > 0 ? queueRowDivider : {}
15903
- },
15904
- children: [
15905
- queue.length === 1 ? /* @__PURE__ */ jsx("span", {
15906
- style: queueLead,
15907
- "aria-hidden": "true",
15908
- children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
15909
- }) : null,
15910
- editing?.id === row.id ? /* @__PURE__ */ jsx("input", {
15911
- autoFocus: true,
15912
- style: queueEditor,
15913
- "aria-label": t("queueEdit"),
15914
- value: editing.text,
15915
- onChange: (event) => {
15916
- setEditing({
15917
- id: row.id,
15918
- text: event.currentTarget.value
15919
- });
15920
- },
15921
- onKeyDown: (event) => {
15922
- if (event.key === "Escape") setEditing(void 0);
15923
- else if (event.key === "Enter" && !event.nativeEvent.isComposing) {
15924
- event.preventDefault();
15925
- saveEdit();
15926
- }
15927
- }
15928
- }) : /* @__PURE__ */ jsx("span", {
15929
- style: queuePreview,
15930
- children: row.preview
15931
- }),
15932
- mutable ? /* @__PURE__ */ jsx("span", {
15933
- style: queueActions,
15934
- children: editing?.id === row.id ? /* @__PURE__ */ jsxs(Fragment$1, { children: [action(t("queueSave"), /* @__PURE__ */ jsx(IconCheckOutline16, { size: 14 }), () => {
15935
- saveEdit();
15936
- }, busy !== void 0 || editing.text.trim() === ""), action(t("queueCancelEdit"), /* @__PURE__ */ jsx(IconCloseOutline16, { size: 14 }), () => {
15937
- setEditing(void 0);
15938
- }, busy !== void 0)] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
15939
- action(t("queueEdit"), /* @__PURE__ */ jsx(IconEditOutline16, { size: 14 }), () => {
15940
- if (row.text !== null) setEditing({
15941
- id: row.id,
15942
- text: row.text
15943
- });
15944
- }, busy !== void 0 || row.text === null, row.text === null ? t("queueEditUnsupported") : void 0),
15945
- action(t("queueRemove"), /* @__PURE__ */ jsx(IconTrashOutline16, { size: 14 }), () => {
15946
- apply(row.id, { kind: "remove" }, t("queueRemoveFailed"));
15947
- }, busy !== void 0),
15948
- action(t("queueSteer"), /* @__PURE__ */ jsx(IconSendOutline14, {}), () => {
15949
- apply(row.id, { kind: "steer" }, t("queueSteerFailed"));
15950
- }, busy !== void 0 || !running, running ? void 0 : t("queueSteerUnavailable"))
15951
- ] })
15952
- }) : null
15953
- ]
15954
- }, row.id)) : null
15955
- })]
15956
- })]
15957
- });
16010
+ }, ClaudeDiffTab));
16011
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16012
+ name: "sidebar.right.pane.tab",
16013
+ key: CLAUDE_TAB_KINDS.plan,
16014
+ locale: namespace,
16015
+ inject: (sessionId) => ({
16016
+ t,
16017
+ sessionId,
16018
+ ...faceFor(CLAUDE_TAB_KINDS.plan, sessionId)
16019
+ })
16020
+ }, ClaudePlanTab));
16021
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16022
+ name: "sidebar.right.pane.tab",
16023
+ key: CLAUDE_TAB_KINDS.tasks,
16024
+ locale: namespace,
16025
+ inject: (sessionId) => ({
16026
+ t,
16027
+ ...faceFor(CLAUDE_TAB_KINDS.tasks, sessionId)
16028
+ })
16029
+ }, ClaudeTasksTab));
16030
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16031
+ name: "sidebar.right.pane.tab",
16032
+ key: CLAUDE_TAB_KINDS.overview,
16033
+ locale: namespace,
16034
+ inject: (sessionId) => ({
16035
+ ...faceFor(CLAUDE_TAB_KINDS.overview, sessionId),
16036
+ face: options.overviewFace(sessionId)
16037
+ })
16038
+ }, ClaudeOverviewTab));
16039
+ const open = (kind, sessionId, params) => {
16040
+ ctx.sidebarRight.openTabIn(sessionId, kind, { params });
16041
+ };
16042
+ const close = (kind, sessionId) => {
16043
+ const tabId = occupancy.tabId(kind, sessionId);
16044
+ if (tabId !== void 0) ctx.sidebarRight.closeIn(sessionId, tabId);
16045
+ };
16046
+ return {
16047
+ open,
16048
+ close,
16049
+ toggle: (kind, sessionId, params) => {
16050
+ if (occupancy.tabId(kind, sessionId) === void 0) open(kind, sessionId, params);
16051
+ else close(kind, sessionId);
16052
+ },
16053
+ isOpen: (kind, sessionId) => occupancy.tabId(kind, sessionId) !== void 0,
16054
+ sourceFor: (kind, sessionId) => occupancy.source(kind, sessionId)
16055
+ };
15958
16056
  }
15959
16057
  //#endregion
15960
16058
  //#region src/client/ask-api.ts
@@ -17905,10 +18003,10 @@ window.__ModuleLoader__.load({
17905
18003
  ".dsh-claude-header-diff-add{color:var(--dsw-alias-state-success-primary)}",
17906
18004
  ".dsh-claude-header-diff-del{color:var(--dsw-alias-state-error-primary)}"
17907
18005
  ].join("");
17908
- let cssInjected$2 = false;
17909
- function ensureCss$2() {
17910
- if (cssInjected$2 || typeof document === "undefined") return;
17911
- cssInjected$2 = true;
18006
+ let cssInjected$1 = false;
18007
+ function ensureCss$1() {
18008
+ if (cssInjected$1 || typeof document === "undefined") return;
18009
+ cssInjected$1 = true;
17912
18010
  const element = document.createElement("style");
17913
18011
  element.dataset.dshClaudeHeaderDiff = "";
17914
18012
  element.textContent = ACTION_CSS;
@@ -17946,7 +18044,7 @@ window.__ModuleLoader__.load({
17946
18044
  const deletions = useClaudeProjection((projection) => diffTotal(projection, "deletions"));
17947
18045
  const open = useSyncExternalStore(diffOpen.subscribe, diffOpen.getSnapshot, diffOpen.getSnapshot);
17948
18046
  if (!owned) return null;
17949
- ensureCss$2();
18047
+ ensureCss$1();
17950
18048
  const hasCounts = additions > 0 || deletions > 0;
17951
18049
  const label = t(open ? "diffClose" : "diffOpen");
17952
18050
  return /* @__PURE__ */ jsx(Tooltip, {
@@ -17972,168 +18070,6 @@ window.__ModuleLoader__.load({
17972
18070
  });
17973
18071
  }
17974
18072
  //#endregion
17975
- //#region src/client/editor-open-api.ts
17976
- /** Launch the session's project in a desktop editor on the host machine. */
17977
- async function openProjectInEditor(sessionId, editor) {
17978
- await pluginWrite(CLAUDE_EDITOR_OPEN_PATH, "fast", void 0, { query: {
17979
- sessionId,
17980
- editor
17981
- } });
17982
- }
17983
- //#endregion
17984
- //#region src/client/ClaudeSessionMenu.tsx
17985
- /** Trigger matches the neighbouring diff action; the cards reproduce the
17986
- * primitives' menu surface (r12, inverted hairline, shadow-lv3, 4px inset).
17987
- *
17988
- * Hand-rolled rather than the `Menu` primitive because this menu needs its
17989
- * submenu on the LEFT: the trigger is the header's rightmost control, and the
17990
- * primitive's nested card is pinned to `left: 100%`, which would open the
17991
- * editor list off the right edge of the window. */
17992
- const MENU_CSS = [
17993
- ".dsh-claude-header-menu-root{position:relative;display:inline-flex}",
17994
- ".dsh-claude-header-menu{flex:none;display:inline-flex;align-items:center;justify-content:center;",
17995
- "width:32px;height:32px;padding:0;border:0;border-radius:9px;background:transparent;",
17996
- "color:var(--dsw-alias-label-secondary);cursor:pointer;transition:background .12s ease,color .12s ease}",
17997
- ".dsh-claude-header-menu:hover,.dsh-claude-header-menu:focus-visible,.dsh-claude-header-menu[aria-expanded=\"true\"]{",
17998
- "background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
17999
- ".dsh-claude-header-menu:active{background:var(--dsw-alias-interactive-bg-active)}",
18000
- ".dsh-claude-header-menu:focus-visible{outline:none}",
18001
- ".dsh-claude-header-menu>svg{display:block;transform:rotate(90deg)}",
18002
- ".dsh-claude-header-menu-card,.dsh-claude-header-menu-sub{box-sizing:border-box;position:absolute;",
18003
- "display:flex;flex-direction:column;padding:2px;border:1px solid var(--dsw-alias-border-inverted);",
18004
- "border-radius:7px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3)}",
18005
- ".dsh-claude-header-menu-card{top:calc(100% + 4px);right:0;z-index:100;min-width:164px;max-width:360px}",
18006
- ".dsh-claude-header-menu-sub{top:-2px;right:calc(100% + 10px);z-index:101;min-width:164px}",
18007
- ".dsh-claude-header-menu-sub::after{content:\"\";position:absolute;top:0;bottom:0;right:-10px;width:10px}",
18008
- ".dsh-claude-header-menu-row{position:relative}",
18009
- ".dsh-claude-header-menu-item{display:flex;align-items:center;gap:6px;width:100%;min-height:26px;",
18010
- "padding:3px 7px;border:none;border-radius:5px;background:transparent;cursor:pointer;text-align:left;",
18011
- "font-family:var(--dsw-font-family);font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary)}",
18012
- ".dsh-claude-header-menu-item:hover,.dsh-claude-header-menu-item:focus-visible,",
18013
- ".dsh-claude-header-menu-item[aria-expanded=\"true\"]{background:var(--dsw-alias-interactive-bg-hover)}",
18014
- ".dsh-claude-header-menu-item:focus-visible{outline:none}",
18015
- ".dsh-claude-header-menu-item>span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
18016
- ".dsh-claude-header-menu-item>svg{flex:none;color:var(--dsw-alias-label-tertiary)}",
18017
- ".dsh-claude-header-menu-heading{padding:4px 7px;font-family:var(--dsw-font-family);font-size:11px;",
18018
- "line-height:16px;color:var(--dsw-alias-label-tertiary)}"
18019
- ].join("");
18020
- let cssInjected$1 = false;
18021
- function ensureCss$1() {
18022
- if (cssInjected$1 || typeof document === "undefined") return;
18023
- cssInjected$1 = true;
18024
- for (const stale of document.querySelectorAll("style[data-dsh-claude-header-menu]")) stale.remove();
18025
- const element = document.createElement("style");
18026
- element.dataset.dshClaudeHeaderMenu = "";
18027
- element.textContent = MENU_CSS;
18028
- document.head.appendChild(element);
18029
- }
18030
- const EDITORS = [{
18031
- id: "cursor",
18032
- label: "Cursor"
18033
- }, {
18034
- id: "idea",
18035
- label: "IntelliJ IDEA"
18036
- }];
18037
- /** The open menu, split out so the SSR tests can render it without a DOM to
18038
- * drive the hover that opens the submenu. */
18039
- function ClaudeSessionMenuCard({ openIn, submenuOpen, failure, onSubmenuOpen, onSelect }) {
18040
- return /* @__PURE__ */ jsxs("div", {
18041
- className: "dsh-claude-header-menu-card",
18042
- role: "menu",
18043
- children: [/* @__PURE__ */ jsxs("div", {
18044
- className: "dsh-claude-header-menu-row",
18045
- onMouseEnter: () => onSubmenuOpen(true),
18046
- onMouseLeave: () => onSubmenuOpen(false),
18047
- children: [/* @__PURE__ */ jsxs("button", {
18048
- type: "button",
18049
- role: "menuitem",
18050
- className: "dsh-claude-header-menu-item",
18051
- "aria-haspopup": "menu",
18052
- "aria-expanded": submenuOpen,
18053
- onFocus: () => onSubmenuOpen(true),
18054
- onClick: () => onSubmenuOpen(true),
18055
- children: [/* @__PURE__ */ jsx("span", { children: openIn }), /* @__PURE__ */ jsx(IconChevronRightOutline14, {})]
18056
- }), submenuOpen ? /* @__PURE__ */ jsxs("div", {
18057
- className: "dsh-claude-header-menu-sub",
18058
- role: "menu",
18059
- children: [/* @__PURE__ */ jsx("div", {
18060
- className: "dsh-claude-header-menu-heading",
18061
- children: openIn
18062
- }), EDITORS.map((editor) => /* @__PURE__ */ jsx("button", {
18063
- type: "button",
18064
- role: "menuitem",
18065
- className: "dsh-claude-header-menu-item",
18066
- onClick: () => onSelect(editor.id),
18067
- children: /* @__PURE__ */ jsx("span", { children: editor.label })
18068
- }, editor.id))]
18069
- }) : null]
18070
- }), failure === void 0 ? null : /* @__PURE__ */ jsx("div", {
18071
- className: "dsh-claude-header-menu-heading",
18072
- children: failure
18073
- })]
18074
- });
18075
- }
18076
- function ClaudeSessionMenu({ t, sessionId, useClaudeProjection, openInEditor = openProjectInEditor }) {
18077
- const owned = useClaudeProjection((projection) => projection.owned);
18078
- const root = useRef(null);
18079
- const [open, setOpen] = useState(false);
18080
- const [submenuOpen, setSubmenuOpen] = useState(false);
18081
- const [failure, setFailure] = useState(void 0);
18082
- const close = () => {
18083
- setOpen(false);
18084
- setSubmenuOpen(false);
18085
- setFailure(void 0);
18086
- };
18087
- useDismissOnOutsidePointer(root, open, (next) => {
18088
- if (!next) close();
18089
- });
18090
- useEffect(() => {
18091
- if (!open) return void 0;
18092
- const onKeyDown = (event) => {
18093
- if (event.key === "Escape") close();
18094
- };
18095
- document.addEventListener("keydown", onKeyDown);
18096
- return () => document.removeEventListener("keydown", onKeyDown);
18097
- }, [open]);
18098
- if (!owned) return null;
18099
- ensureCss$1();
18100
- const label = t("sessionMenu");
18101
- const openIn = t("sessionMenuOpenIn");
18102
- const select = (editor) => {
18103
- setFailure(void 0);
18104
- openInEditor(sessionId, editor).then(() => close(), (error) => setFailure(t("sessionMenuOpenFailed", { message: error instanceof Error ? error.message : String(error) })));
18105
- };
18106
- return /* @__PURE__ */ jsxs("span", {
18107
- className: "dsh-claude-header-menu-root",
18108
- ref: root,
18109
- children: [/* @__PURE__ */ jsx(Tooltip, {
18110
- label,
18111
- side: "bottom",
18112
- delayMs: 250,
18113
- disabled: open,
18114
- children: /* @__PURE__ */ jsx("button", {
18115
- type: "button",
18116
- className: "dsh-claude-header-menu",
18117
- "aria-label": label,
18118
- "aria-haspopup": "menu",
18119
- "aria-expanded": open,
18120
- "data-session": sessionId,
18121
- onClick: () => {
18122
- if (open) close();
18123
- else setOpen(true);
18124
- },
18125
- children: /* @__PURE__ */ jsx(IconEllipsisOutline16, {})
18126
- })
18127
- }), open ? /* @__PURE__ */ jsx(ClaudeSessionMenuCard, {
18128
- openIn,
18129
- submenuOpen,
18130
- failure,
18131
- onSubmenuOpen: setSubmenuOpen,
18132
- onSelect: select
18133
- }) : null]
18134
- });
18135
- }
18136
- //#endregion
18137
18073
  //#region src/client/claude-mark.ts
18138
18074
  /** The Claude wordmark glyph, lifted verbatim from the brand SVG.
18139
18075
  *
@@ -18280,41 +18216,6 @@ window.__ModuleLoader__.load({
18280
18216
  });
18281
18217
  }
18282
18218
  //#endregion
18283
- //#region src/client/panel-open-store.ts
18284
- var PanelOpenStore = class {
18285
- #sessionId;
18286
- #listeners = /* @__PURE__ */ new Set();
18287
- /** Mark this panel open for one session, replacing any previous holder. */
18288
- open(sessionId) {
18289
- this.#set(sessionId);
18290
- }
18291
- /** Mark this panel closed. */
18292
- close() {
18293
- this.#set(void 0);
18294
- }
18295
- /** Whether this panel is currently open for this session. */
18296
- isOpen(sessionId) {
18297
- return this.#sessionId === sessionId;
18298
- }
18299
- /** A per-session snapshot source for `useSyncExternalStore`. */
18300
- sourceFor(sessionId) {
18301
- return {
18302
- subscribe: (listener) => {
18303
- this.#listeners.add(listener);
18304
- return () => {
18305
- this.#listeners.delete(listener);
18306
- };
18307
- },
18308
- getSnapshot: () => this.isOpen(sessionId)
18309
- };
18310
- }
18311
- #set(sessionId) {
18312
- if (this.#sessionId === sessionId) return;
18313
- this.#sessionId = sessionId;
18314
- for (const listener of this.#listeners) listener();
18315
- }
18316
- };
18317
- //#endregion
18318
18219
  //#region src/client/claude-command-source.ts
18319
18220
  const SOURCE_NAME = "Claude Code";
18320
18221
  function commandsFor(store, session) {
@@ -18874,7 +18775,7 @@ window.__ModuleLoader__.load({
18874
18775
  for (const seat of document.querySelectorAll(`[${CLAUDE_SEAT_ATTRIBUTE}]`)) seat.removeAttribute(CLAUDE_SEAT_ATTRIBUTE);
18875
18776
  };
18876
18777
  }
18877
- const HOST_CHROME_CSS = `button[class*="sessionLogButton"]{display:none}${["header:has(.dsh-claude-header-diff)>[role=\"tablist\"]{display:none}", "header:has(.dsh-claude-header-diff){padding-bottom:10px}"].join("")}${[
18778
+ const HOST_CHROME_CSS = `button[class*="sessionLogButton"],button[class*="moreButton"][aria-haspopup="menu"]{display:none}${["header:has(.dsh-claude-header-diff)>[role=\"tablist\"]{display:none}", "header:has(.dsh-claude-header-diff){padding-bottom:10px}"].join("")}${[
18878
18779
  `button[${CLAUDE_SEAT_ATTRIBUTE}]>[class*="seatIcon"]{display:none}`,
18879
18780
  `button[${CLAUDE_SEAT_ATTRIBUTE}]::before{content:"";flex:none;width:16px;height:16px;`,
18880
18781
  `background:${claudeMarkUrl()} center/contain no-repeat}`
@@ -18894,170 +18795,6 @@ window.__ModuleLoader__.load({
18894
18795
  };
18895
18796
  }
18896
18797
  //#endregion
18897
- //#region src/client/details-resize.ts
18898
- const DETAILS_MIN_WIDTH = 300;
18899
- const DETAILS_DEFAULT_WIDTH = 720;
18900
- const DETAILS_MAX_RATIO = .5;
18901
- const DRAG_THRESHOLD = 4;
18902
- function clampDetailsWidth(width, frameWidth) {
18903
- const maximum = Math.max(DETAILS_MIN_WIDTH, Math.floor(frameWidth * DETAILS_MAX_RATIO));
18904
- return Math.min(maximum, Math.max(DETAILS_MIN_WIDTH, Math.round(width)));
18905
- }
18906
- function defaultDetailsWidth(frameWidth) {
18907
- return clampDetailsWidth(DETAILS_DEFAULT_WIDTH, frameWidth);
18908
- }
18909
- function pixelValue(value) {
18910
- const parsed = Number.parseFloat(value);
18911
- return Number.isFinite(parsed) ? parsed : void 0;
18912
- }
18913
- /**
18914
- * Extends the native Details drag handle for the plugin Diff panel without
18915
- * depending on DSH's private layout store. The returned cleanup restores every
18916
- * inline value owned by the native frame.
18917
- */
18918
- function enableExpandedDetailsResize() {
18919
- if (typeof document === "undefined" || typeof window === "undefined") return () => void 0;
18920
- let frame;
18921
- let handle;
18922
- let width;
18923
- let dragStartX = 0;
18924
- let dragStartWidth = 0;
18925
- let pointerId;
18926
- let dragged = false;
18927
- let originalGrid = "";
18928
- let originalHandleLeft = "";
18929
- let frameWasDragging = false;
18930
- let handleWasDragging = false;
18931
- let resizeObserver;
18932
- let mutationObserver;
18933
- let animationFrame;
18934
- const applyWidth = () => {
18935
- if (frame === void 0 || handle === void 0 || width === void 0) return;
18936
- const frameWidth = frame.getBoundingClientRect().width;
18937
- if (frameWidth <= 0) return;
18938
- width = clampDetailsWidth(width, frameWidth);
18939
- const grid = `${pixelValue(getComputedStyle(frame).gridTemplateColumns.split(/\s+/u)[0] ?? "") ?? 0}px minmax(0, 1fr) ${width}px`;
18940
- const left = `${frameWidth - width}px`;
18941
- if (frame.style.gridTemplateColumns !== grid) frame.style.gridTemplateColumns = grid;
18942
- if (handle.style.left !== left) handle.style.left = left;
18943
- };
18944
- const scheduleApply = () => {
18945
- if (animationFrame !== void 0) return;
18946
- animationFrame = window.requestAnimationFrame(() => {
18947
- animationFrame = void 0;
18948
- applyWidth();
18949
- });
18950
- };
18951
- const finishDrag = () => {
18952
- if (pointerId === void 0) return;
18953
- pointerId = void 0;
18954
- if (frame !== void 0 && !frameWasDragging) frame.removeAttribute("data-dragging");
18955
- if (handle !== void 0 && !handleWasDragging) handle.removeAttribute("data-dragging");
18956
- };
18957
- const onPointerMove = (event) => {
18958
- if (event.pointerId !== pointerId || frame === void 0) return;
18959
- const delta = event.clientX - dragStartX;
18960
- if (!dragged && Math.abs(delta) < DRAG_THRESHOLD) return;
18961
- dragged = true;
18962
- width = clampDetailsWidth(dragStartWidth - delta, frame.getBoundingClientRect().width);
18963
- applyWidth();
18964
- event.preventDefault();
18965
- };
18966
- const onPointerEnd = (event) => {
18967
- if (event.pointerId !== pointerId) return;
18968
- finishDrag();
18969
- };
18970
- const onPointerDown = (event) => {
18971
- const target = event.target;
18972
- if (!(target instanceof Element)) return;
18973
- const candidate = target.closest("[data-side=\"details\"]");
18974
- const candidateFrame = candidate?.parentElement;
18975
- if (candidate === null || candidate === void 0 || candidateFrame === null || candidateFrame === void 0) return;
18976
- const currentFrame = candidateFrame;
18977
- const currentHandle = candidate;
18978
- const detailsColumn = currentFrame.children.item(2);
18979
- const measuredWidth = detailsColumn instanceof HTMLElement ? detailsColumn.getBoundingClientRect().width : 0;
18980
- const frameWidth = currentFrame.getBoundingClientRect().width;
18981
- if (measuredWidth <= 0 || frameWidth <= 0) return;
18982
- const firstDrag = frame === void 0;
18983
- frame = currentFrame;
18984
- handle = currentHandle;
18985
- width = clampDetailsWidth(width ?? measuredWidth, frameWidth);
18986
- dragStartWidth = width;
18987
- dragStartX = event.clientX;
18988
- pointerId = event.pointerId;
18989
- dragged = false;
18990
- if (firstDrag) {
18991
- originalGrid = currentFrame.style.gridTemplateColumns;
18992
- originalHandleLeft = currentHandle.style.left;
18993
- frameWasDragging = currentFrame.hasAttribute("data-dragging");
18994
- handleWasDragging = currentHandle.hasAttribute("data-dragging");
18995
- }
18996
- currentFrame.setAttribute("data-dragging", "");
18997
- currentHandle.setAttribute("data-dragging", "true");
18998
- if (firstDrag) {
18999
- resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleApply);
19000
- resizeObserver?.observe(currentFrame);
19001
- mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(scheduleApply);
19002
- mutationObserver?.observe(currentFrame, {
19003
- attributes: true,
19004
- attributeFilter: ["style"]
19005
- });
19006
- mutationObserver?.observe(currentHandle, {
19007
- attributes: true,
19008
- attributeFilter: ["style"]
19009
- });
19010
- }
19011
- event.preventDefault();
19012
- event.stopPropagation();
19013
- event.stopImmediatePropagation();
19014
- };
19015
- const applyDefaultWidth = () => {
19016
- const initialHandle = document.querySelector("[data-side=\"details\"]");
19017
- const initialFrame = initialHandle?.parentElement;
19018
- if (initialHandle === null || initialHandle === void 0 || initialFrame === null || initialFrame === void 0) return;
19019
- frame = initialFrame;
19020
- handle = initialHandle;
19021
- originalGrid = frame.style.gridTemplateColumns;
19022
- originalHandleLeft = handle.style.left;
19023
- frameWasDragging = frame.hasAttribute("data-dragging");
19024
- handleWasDragging = handle.hasAttribute("data-dragging");
19025
- width = defaultDetailsWidth(frame.getBoundingClientRect().width);
19026
- resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleApply);
19027
- resizeObserver?.observe(frame);
19028
- mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(scheduleApply);
19029
- mutationObserver?.observe(frame, {
19030
- attributes: true,
19031
- attributeFilter: ["style"]
19032
- });
19033
- mutationObserver?.observe(handle, {
19034
- attributes: true,
19035
- attributeFilter: ["style"]
19036
- });
19037
- applyWidth();
19038
- };
19039
- animationFrame = window.requestAnimationFrame(() => {
19040
- animationFrame = void 0;
19041
- applyDefaultWidth();
19042
- });
19043
- document.addEventListener("pointerdown", onPointerDown, true);
19044
- window.addEventListener("pointermove", onPointerMove, true);
19045
- window.addEventListener("pointerup", onPointerEnd, true);
19046
- window.addEventListener("pointercancel", onPointerEnd, true);
19047
- return () => {
19048
- document.removeEventListener("pointerdown", onPointerDown, true);
19049
- window.removeEventListener("pointermove", onPointerMove, true);
19050
- window.removeEventListener("pointerup", onPointerEnd, true);
19051
- window.removeEventListener("pointercancel", onPointerEnd, true);
19052
- resizeObserver?.disconnect();
19053
- mutationObserver?.disconnect();
19054
- if (animationFrame !== void 0) window.cancelAnimationFrame(animationFrame);
19055
- finishDrag();
19056
- if (frame !== void 0) frame.style.gridTemplateColumns = originalGrid;
19057
- if (handle !== void 0) handle.style.left = originalHandleLeft;
19058
- };
19059
- }
19060
- //#endregion
19061
18798
  //#region src/client/locales.ts
19062
18799
  const zh = {
19063
18800
  nav: "Claude Code",
@@ -19250,11 +18987,10 @@ window.__ModuleLoader__.load({
19250
18987
  yes: "是",
19251
18988
  no: "否",
19252
18989
  diffOpen: "查看分支改动",
18990
+ diffTabTitle: "分支改动",
19253
18991
  planPanelTitle: "Plan",
19254
18992
  planClose: "关闭方案面板",
19255
18993
  planOpen: "查看方案",
19256
- planMaximize: "最大化方案面板",
19257
- planRestore: "还原方案面板",
19258
18994
  planNth: "{index} / {total}",
19259
18995
  planHistory: "本会话的方案",
19260
18996
  planNotePlaceholder: "写下要改的地方,Enter 记一条,⌘/Ctrl+Enter 发送",
@@ -19272,9 +19008,6 @@ window.__ModuleLoader__.load({
19272
19008
  planEmpty: "本会话还没有待查看的方案。",
19273
19009
  planPendingHint: "在 DSH 的审批弹框里批准或拒绝这个方案。",
19274
19010
  diffClose: "关闭 Diff 面板",
19275
- sessionMenu: "会话菜单",
19276
- sessionMenuOpenIn: "打开方式",
19277
- sessionMenuOpenFailed: "无法打开:{message}",
19278
19011
  promptSource: "常用提示词",
19279
19012
  promptSave: "存为常用提示词",
19280
19013
  promptSaveName: "名字",
@@ -19292,11 +19025,12 @@ window.__ModuleLoader__.load({
19292
19025
  presetHeaderHint: "当前会话运行的 Agent 预设",
19293
19026
  diffRepository: "仓库",
19294
19027
  repositoryLinked: "关联仓库:本会话改动过",
19028
+ linkedRepositoryPrompt: "以下内容针对关联仓库 {root}(分支 {branch}),不是本会话自己的仓库。",
19295
19029
  diffFiles: "{count} 个已修改文件",
19296
19030
  diffFilesShort: "{count} 个文件",
19297
19031
  diffTruncated: "Diff 超出安全显示上限。请在终端中查看完整内容。",
19298
19032
  diffEmpty: "没有可显示的 tracked 文件改动",
19299
- diffMaximize: "最大化 Diff 面板",
19033
+ diffElided: "{count} 个文件过大未展示:{files}",
19300
19034
  diffCommentPrevious: "上一条评论",
19301
19035
  diffCommentNext: "下一条评论",
19302
19036
  diffCommentPosition: "第 {index} 条,共 {total} 条",
@@ -19306,7 +19040,6 @@ window.__ModuleLoader__.load({
19306
19040
  diffCardFiles: "{count} 个文件",
19307
19041
  diffExpandAll: "展开全部文件",
19308
19042
  diffCollapseAll: "收起全部文件",
19309
- diffRestore: "还原 Diff 面板",
19310
19043
  diffCommit: "Commit",
19311
19044
  diffCommitMenu: "打开提交操作菜单",
19312
19045
  diffCommitPush: "Commit & Push…",
@@ -19419,6 +19152,7 @@ window.__ModuleLoader__.load({
19419
19152
  cleanupTitle: "清理已合并分支",
19420
19153
  cleanupDescription: "删除 worktree 与本地分支,并归档对应 workspace 的会话后移除 workspace;普通 checkout 则切回 base 分支并删除已合并分支。",
19421
19154
  cleanupCompleted: "已清理 {branch}",
19155
+ cleanupDescriptionUnmerged: "这个 worktree 没有对应的 PR。删除 worktree 与本地分支,并归档对应 workspace 的会话后移除 workspace;分支上有任何未推送到远端的提交时会拒绝。",
19422
19156
  overviewOpen: "打开 Claude 会话看板",
19423
19157
  overviewTitle: "会话看板",
19424
19158
  overviewBody: "所有 Claude 会话的运行状态、分支与 PR",
@@ -19694,11 +19428,10 @@ window.__ModuleLoader__.load({
19694
19428
  yes: "Yes",
19695
19429
  no: "No",
19696
19430
  diffOpen: "View branch changes",
19431
+ diffTabTitle: "Branch changes",
19697
19432
  planPanelTitle: "Plan",
19698
19433
  planClose: "Close plan panel",
19699
19434
  planOpen: "View plan",
19700
- planMaximize: "Maximize plan panel",
19701
- planRestore: "Restore plan panel",
19702
19435
  planNth: "{index} / {total}",
19703
19436
  planHistory: "Plans in this session",
19704
19437
  planNotePlaceholder: "What should change? Enter files a note, ⌘/Ctrl+Enter sends",
@@ -19716,9 +19449,6 @@ window.__ModuleLoader__.load({
19716
19449
  planEmpty: "This session has no plan to read yet.",
19717
19450
  planPendingHint: "Approve or reject this plan in the DSH approval dialog.",
19718
19451
  diffClose: "Close diff panel",
19719
- sessionMenu: "Session menu",
19720
- sessionMenuOpenIn: "Open in",
19721
- sessionMenuOpenFailed: "Could not open: {message}",
19722
19452
  promptSource: "Prompts",
19723
19453
  promptSave: "Save as a prompt",
19724
19454
  promptSaveName: "Name",
@@ -19736,11 +19466,12 @@ window.__ModuleLoader__.load({
19736
19466
  presetHeaderHint: "The agent preset this session runs",
19737
19467
  diffRepository: "Repository",
19738
19468
  repositoryLinked: "Linked repository: changed by this session",
19469
+ linkedRepositoryPrompt: "The following concerns the linked repository at {root} (branch {branch}), not this session's own checkout.",
19739
19470
  diffFiles: "{count} modified file(s)",
19740
19471
  diffFilesShort: "{count} files",
19741
19472
  diffTruncated: "The diff exceeds the safe display limit. Use the terminal to inspect the complete content.",
19742
19473
  diffEmpty: "No tracked file changes to display",
19743
- diffMaximize: "Maximize diff panel",
19474
+ diffElided: "Skipped {count} oversized file(s): {files}",
19744
19475
  diffCommentPrevious: "Previous comment",
19745
19476
  diffCommentNext: "Next comment",
19746
19477
  diffCommentPosition: "Comment {index} of {total}",
@@ -19750,7 +19481,6 @@ window.__ModuleLoader__.load({
19750
19481
  diffCardFiles: "{count} files",
19751
19482
  diffExpandAll: "Expand all files",
19752
19483
  diffCollapseAll: "Collapse all files",
19753
- diffRestore: "Restore diff panel",
19754
19484
  diffCommit: "Commit",
19755
19485
  diffCommitMenu: "Open commit actions",
19756
19486
  diffCommitPush: "Commit & Push…",
@@ -19863,6 +19593,7 @@ window.__ModuleLoader__.load({
19863
19593
  cleanupTitle: "Clean up merged branch",
19864
19594
  cleanupDescription: "Remove the worktree and local branch, then archive the workspace sessions and delete the workspace; a plain checkout switches back to the base branch and deletes the merged branch.",
19865
19595
  cleanupCompleted: "Cleaned up {branch}",
19596
+ cleanupDescriptionUnmerged: "This worktree has no pull request. Remove the worktree and local branch, then archive the workspace sessions and delete the workspace; refused while the branch holds commits that are on no remote.",
19866
19597
  overviewOpen: "Open the Claude session board",
19867
19598
  overviewTitle: "Session board",
19868
19599
  overviewBody: "Run state, branch, and pull request across Claude sessions",
@@ -19949,39 +19680,7 @@ window.__ModuleLoader__.load({
19949
19680
  };
19950
19681
  //#endregion
19951
19682
  //#region src/client/index.tsx
19952
- function MaximizedDiff({ source, t, sessionId, closeDetails, restore, submitPrompt, initialRoot }) {
19953
- const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
19954
- const useClaudeProjection = (selector) => selector(snapshot);
19955
- return /* @__PURE__ */ jsx(ClaudePanelOverlay, {
19956
- onRestore: restore,
19957
- children: /* @__PURE__ */ jsx(ClaudeDiffPanel, {
19958
- useClaudeProjection,
19959
- t,
19960
- sessionId,
19961
- maximized: true,
19962
- closeDetails,
19963
- toggleMaximized: restore,
19964
- ...submitPrompt === void 0 ? {} : { submitPrompt },
19965
- ...initialRoot === void 0 ? {} : { initialRoot }
19966
- })
19967
- });
19968
- }
19969
19683
  const name = "dsh-claude-client";
19970
- function MaximizedPlan({ source, t, sessionId, closeDetails, restore }) {
19971
- const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
19972
- const useClaudeProjection = (selector) => selector(snapshot);
19973
- return /* @__PURE__ */ jsx(ClaudePanelOverlay, {
19974
- onRestore: restore,
19975
- children: /* @__PURE__ */ jsx(ClaudePlanPanel, {
19976
- useClaudeProjection,
19977
- t,
19978
- sessionId,
19979
- maximized: true,
19980
- closeDetails,
19981
- toggleMaximized: restore
19982
- })
19983
- });
19984
- }
19985
19684
  const inject = [
19986
19685
  "slots",
19987
19686
  "locale",
@@ -19993,7 +19692,9 @@ window.__ModuleLoader__.load({
19993
19692
  "workspaces",
19994
19693
  "inputTriggers",
19995
19694
  "conversation",
19996
- "connection"
19695
+ "connection",
19696
+ "sidebarRight",
19697
+ "sidebarRightTabs"
19997
19698
  ];
19998
19699
  /** Resolve one session's composer facade.
19999
19700
  *
@@ -20093,224 +19794,39 @@ window.__ModuleLoader__.load({
20093
19794
  key: "claude-activity-step",
20094
19795
  locale: namespace
20095
19796
  }, ClaudeActivityNode));
20096
- const layout = ctx.get("layout");
20097
- let disposePluginDetails;
20098
- let disposeDiffOverlay;
20099
- let disposePlanOverlay;
20100
- let disposeExpandedDetailsResize;
20101
- let detailsSessionId;
20102
- const diffOpen = new PanelOpenStore();
20103
- const planOpen = new PanelOpenStore();
20104
- const restoreDiff = () => {
20105
- if (disposeDiffOverlay === void 0) return;
20106
- disposeDiffOverlay();
20107
- disposeDiffOverlay = void 0;
20108
- layout?.openDetails();
20109
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19797
+ const sidebarTabs = registerClaudeSidebarTabs(ctx, {
19798
+ t,
19799
+ namespace,
19800
+ diffFace: (sessionId) => {
19801
+ const submitPrompt = submitPromptFor(sessionId);
19802
+ return submitPrompt === void 0 ? {} : { submitPrompt };
19803
+ },
19804
+ overviewFace: () => sessions === void 0 ? void 0 : {
19805
+ t,
19806
+ openSession: (id) => {
19807
+ sessions.open(id);
19808
+ },
19809
+ loadStatus: loadRepositoryStatusFor,
19810
+ sessions: sessions.list,
19811
+ ...workspaces === void 0 ? {} : { workspaces: workspaces.list },
19812
+ projectionFor: (id) => projections.source(id)
19813
+ }
19814
+ });
19815
+ const openTasksPanel = (sessionId, turn) => {
19816
+ sidebarTabs.open(CLAUDE_TAB_KINDS.tasks, sessionId, { turn });
20110
19817
  };
20111
- const restorePlan = () => {
20112
- if (disposePlanOverlay === void 0) return;
20113
- disposePlanOverlay();
20114
- disposePlanOverlay = void 0;
20115
- layout?.openDetails();
20116
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19818
+ const openOverviewPanel = (sessionId) => {
19819
+ sidebarTabs.open(CLAUDE_TAB_KINDS.overview, sessionId, {});
20117
19820
  };
20118
- const closePluginDetails = () => {
20119
- if (disposePluginDetails === void 0 && disposeDiffOverlay === void 0 && disposePlanOverlay === void 0 && disposeExpandedDetailsResize === void 0 && detailsSessionId === void 0) return;
20120
- disposeDiffOverlay?.();
20121
- disposeDiffOverlay = void 0;
20122
- disposePlanOverlay?.();
20123
- disposePlanOverlay = void 0;
20124
- disposeExpandedDetailsResize?.();
20125
- disposeExpandedDetailsResize = void 0;
20126
- disposePluginDetails?.();
20127
- disposePluginDetails = void 0;
20128
- detailsSessionId = void 0;
20129
- diffOpen.close();
20130
- planOpen.close();
20131
- layout?.closeDetails();
19821
+ const openDiffPanel = (sessionId, initialRoot) => {
19822
+ sidebarTabs.open(CLAUDE_TAB_KINDS.diff, sessionId, initialRoot === void 0 ? {} : { initialRoot });
20132
19823
  };
20133
19824
  ctx.effect(() => ctx.slots.onEntryError((key, entry, error) => {
20134
19825
  const id = entry.options.id === void 0 ? "" : ` id="${String(entry.options.id)}"`;
20135
19826
  const message = error instanceof Error ? `${error.message}
20136
19827
  ${error.stack ?? ""}` : String(error);
20137
19828
  diagnostics.report("slot-entry-crashed", `slot "${key}"${id}: ${message}`);
20138
- if (key === "shell.overlay" && entry.options.id === "claude-diff-overlay") restoreDiff();
20139
- if (key === "shell.overlay" && entry.options.id === "claude-plan-overlay") restorePlan();
20140
19829
  }), "dsh-claude: Slot entry failure reporting");
20141
- const openTasksPanel = (sessionId, turn) => {
20142
- closePluginDetails();
20143
- try {
20144
- disposePluginDetails = ctx.slots.register({
20145
- name: "details",
20146
- priority: -10,
20147
- locale: namespace,
20148
- inject: () => ({
20149
- t,
20150
- turn,
20151
- closeDetails: closePluginDetails
20152
- })
20153
- }, ClaudeTasksPanel);
20154
- } catch {
20155
- return;
20156
- }
20157
- detailsSessionId = sessionId;
20158
- layout?.openDetails();
20159
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20160
- };
20161
- const openPlanPanel = (sessionId) => {
20162
- closePluginDetails();
20163
- const maximizePlan = () => {
20164
- if (disposePlanOverlay !== void 0) {
20165
- restorePlan();
20166
- return;
20167
- }
20168
- disposeExpandedDetailsResize?.();
20169
- disposeExpandedDetailsResize = void 0;
20170
- layout?.closeDetails();
20171
- try {
20172
- disposePlanOverlay = ctx.slots.register({
20173
- name: "shell.overlay",
20174
- id: "claude-plan-overlay",
20175
- locale: namespace
20176
- }, () => /* @__PURE__ */ jsx(MaximizedPlan, {
20177
- source: projections.source(sessionId),
20178
- t,
20179
- sessionId,
20180
- closeDetails: closePluginDetails,
20181
- restore: restorePlan
20182
- }));
20183
- } catch {
20184
- disposePlanOverlay = void 0;
20185
- layout?.openDetails();
20186
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20187
- }
20188
- };
20189
- try {
20190
- disposePluginDetails = ctx.slots.register({
20191
- name: "details",
20192
- priority: -10,
20193
- locale: namespace,
20194
- inject: () => ({
20195
- t,
20196
- sessionId,
20197
- closeDetails: closePluginDetails,
20198
- maximized: false,
20199
- toggleMaximized: maximizePlan
20200
- })
20201
- }, ClaudePlanPanel);
20202
- } catch {
20203
- return;
20204
- }
20205
- detailsSessionId = sessionId;
20206
- planOpen.open(sessionId);
20207
- layout?.openDetails();
20208
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20209
- };
20210
- const openOverviewPanel = (sessionId) => {
20211
- if (sessions === void 0) return;
20212
- closePluginDetails();
20213
- try {
20214
- disposePluginDetails = ctx.slots.register({
20215
- name: "details",
20216
- priority: -10,
20217
- locale: namespace,
20218
- inject: () => ({
20219
- t,
20220
- closeDetails: closePluginDetails,
20221
- openSession: (id) => {
20222
- sessions.open(id);
20223
- },
20224
- loadStatus: loadRepositoryStatusFor,
20225
- sessions: sessions.list,
20226
- ...workspaces === void 0 ? {} : { workspaces: workspaces.list },
20227
- projectionFor: (id) => projections.source(id)
20228
- })
20229
- }, ClaudePullRequestsPanel);
20230
- } catch {
20231
- return;
20232
- }
20233
- detailsSessionId = sessionId;
20234
- layout?.openDetails();
20235
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20236
- };
20237
- const openDiffPanel = (sessionId, initialRoot) => {
20238
- closePluginDetails();
20239
- detailsSessionId = sessionId;
20240
- const submitPrompt = submitPromptFor(sessionId);
20241
- const registerDetails = () => {
20242
- try {
20243
- disposePluginDetails = ctx.slots.register({
20244
- name: "details",
20245
- priority: -10,
20246
- locale: namespace,
20247
- inject: () => ({
20248
- t,
20249
- sessionId,
20250
- maximized: false,
20251
- closeDetails: closePluginDetails,
20252
- toggleMaximized: maximizeDiff,
20253
- ...submitPrompt === void 0 ? {} : { submitPrompt },
20254
- ...initialRoot === void 0 ? {} : { initialRoot }
20255
- })
20256
- }, ClaudeDiffPanel);
20257
- } catch {
20258
- disposePluginDetails = void 0;
20259
- return false;
20260
- }
20261
- layout?.openDetails();
20262
- return true;
20263
- };
20264
- const maximizeDiff = () => {
20265
- if (disposeDiffOverlay !== void 0) {
20266
- restoreDiff();
20267
- return;
20268
- }
20269
- disposeExpandedDetailsResize?.();
20270
- disposeExpandedDetailsResize = void 0;
20271
- layout?.closeDetails();
20272
- try {
20273
- disposeDiffOverlay = ctx.slots.register({
20274
- name: "shell.overlay",
20275
- id: "claude-diff-overlay",
20276
- locale: namespace
20277
- }, () => /* @__PURE__ */ jsx(MaximizedDiff, {
20278
- source: projections.source(sessionId),
20279
- t,
20280
- sessionId,
20281
- closeDetails: closePluginDetails,
20282
- restore: restoreDiff,
20283
- ...submitPrompt === void 0 ? {} : { submitPrompt },
20284
- ...initialRoot === void 0 ? {} : { initialRoot }
20285
- }));
20286
- } catch {
20287
- disposeDiffOverlay = void 0;
20288
- layout?.openDetails();
20289
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20290
- }
20291
- };
20292
- if (!registerDetails()) {
20293
- detailsSessionId = void 0;
20294
- return;
20295
- }
20296
- diffOpen.open(sessionId);
20297
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20298
- };
20299
- ctx.effect(() => {
20300
- if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => closePluginDetails();
20301
- const observer = new MutationObserver(() => {
20302
- if (detailsSessionId !== void 0 && disposeDiffOverlay === void 0 && disposePlanOverlay === void 0 && document.querySelector("[data-details-collapsed]") !== null) closePluginDetails();
20303
- });
20304
- observer.observe(document.body, {
20305
- attributes: true,
20306
- attributeFilter: ["data-details-collapsed"],
20307
- subtree: true
20308
- });
20309
- return () => {
20310
- observer.disconnect();
20311
- closePluginDetails();
20312
- };
20313
- }, "dsh-claude: details panel lifecycle");
20314
19830
  ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
20315
19831
  name: "conversation.chat.node",
20316
19832
  key: "claude-active-tasks",
@@ -20353,10 +19869,9 @@ window.__ModuleLoader__.load({
20353
19869
  inject: (sessionId) => ({
20354
19870
  t,
20355
19871
  togglePlan: () => {
20356
- if (planOpen.isOpen(sessionId)) closePluginDetails();
20357
- else openPlanPanel(sessionId);
19872
+ sidebarTabs.toggle(CLAUDE_TAB_KINDS.plan, sessionId, {});
20358
19873
  },
20359
- planOpen: planOpen.sourceFor(sessionId)
19874
+ planOpen: sidebarTabs.sourceFor(CLAUDE_TAB_KINDS.plan, sessionId)
20360
19875
  })
20361
19876
  }, ClaudePlanHeaderAction));
20362
19877
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
@@ -20367,19 +19882,11 @@ window.__ModuleLoader__.load({
20367
19882
  inject: (sessionId) => ({
20368
19883
  t,
20369
19884
  toggleDiff: () => {
20370
- if (diffOpen.isOpen(sessionId)) closePluginDetails();
20371
- else openDiffPanel(sessionId);
19885
+ sidebarTabs.toggle(CLAUDE_TAB_KINDS.diff, sessionId, {});
20372
19886
  },
20373
- diffOpen: diffOpen.sourceFor(sessionId)
19887
+ diffOpen: sidebarTabs.sourceFor(CLAUDE_TAB_KINDS.diff, sessionId)
20374
19888
  })
20375
19889
  }, ClaudeDiffHeaderAction));
20376
- ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
20377
- name: "conversation.session.header.utilities",
20378
- id: "claude-session-menu",
20379
- order: 31,
20380
- locale: namespace,
20381
- inject: () => ({ t })
20382
- }, ClaudeSessionMenu));
20383
19890
  ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
20384
19891
  name: "conversation.input.left",
20385
19892
  id: "claude-prompt-save",
@@ -20567,7 +20074,7 @@ window.__ModuleLoader__.load({
20567
20074
  const sourceInput = sessionInput(conversation, sourceScope);
20568
20075
  const rawDraft = sourceInput.state.getSnapshot().draft;
20569
20076
  const draft = ticket === void 0 ? rawDraft : rawDraft.trim() === "" ? ticketPrompt(ticket) : `${rawDraft.trimEnd()}\n\n${ticketContext(ticket)}`;
20570
- const imageIds = sourceInput.state.getSnapshot().imageIds;
20077
+ const imageIds = sourceInput.state.getSnapshot().attachmentIds;
20571
20078
  const prepared = await prepareRepository(cwd, branch, useWorktree, ticket?.key, onProgress, ticket === void 0 ? draft : void 0);
20572
20079
  if (ticket !== void 0) assignJiraTicket(ticket.key).catch((reason) => {
20573
20080
  console.warn(`dsh-claude: could not assign ${ticket.key}: ${reason instanceof Error ? reason.message : String(reason)}`);
@@ -20588,14 +20095,14 @@ window.__ModuleLoader__.load({
20588
20095
  sessions.noteAgentPreset?.(targetSessionId, presetResponse.value);
20589
20096
  const targetInput = sessionInput(conversation, targetScope);
20590
20097
  onProgress("transferring-draft");
20591
- if (imageIds.length > 0 && !targetInput.addImages(imageIds)) throw new Error(t("repositoryDraftTransferFailed"));
20098
+ if (imageIds.length > 0 && !targetInput.addAttachments(imageIds)) throw new Error(t("repositoryDraftTransferFailed"));
20592
20099
  if (draft !== "") targetInput.setDraft(draft);
20593
20100
  bindLease(prepared.leaseId, targetSessionId);
20594
20101
  sessions.open(targetSessionId);
20595
20102
  onProgress("submitting");
20596
20103
  targetInput.submit();
20597
20104
  sourceInput.setDraft("");
20598
- for (const imageId of imageIds) sourceInput.removeImage(imageId);
20105
+ for (const imageId of imageIds) sourceInput.removeAttachment(imageId);
20599
20106
  },
20600
20107
  prepareMany: async (cwd, branch, tickets, onProgress) => {
20601
20108
  const sourceScope = sessions.scope(sourceSessionId);
@@ -20643,9 +20150,6 @@ window.__ModuleLoader__.load({
20643
20150
  })
20644
20151
  }, ClaudeHeroRepositoryControls));
20645
20152
  }
20646
- if (sessions !== void 0) ctx.effect(() => sessions.list.subscribe(() => {
20647
- if (detailsSessionId !== void 0 && sessions.list.getSnapshot().current !== detailsSessionId) closePluginDetails();
20648
- }), "dsh-claude: details panel session tracking");
20649
20153
  ctx.slots.inject("settings.section", () => ctx.slots.register({
20650
20154
  name: "settings.section",
20651
20155
  id: "claude-code",