@norman-else/dsh-claude 0.1.45 → 0.1.47

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, 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");
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";
@@ -1958,6 +1957,17 @@ window.__ModuleLoader__.load({
1958
1957
  borderColor: "color-mix(in srgb, #a78bfa 35%, transparent)",
1959
1958
  background: "color-mix(in srgb, #a78bfa 5%, var(--dsw-alias-bg-layer-1))"
1960
1959
  };
1960
+ /** A checkout the session wrote into besides its own: lighter, dashed, and
1961
+ * stacked above the session bar so it reads as attached rather than primary. */
1962
+ const repositoryBarLinked = {
1963
+ marginBottom: 6,
1964
+ minHeight: 34,
1965
+ padding: "5px 11px",
1966
+ borderStyle: "dashed",
1967
+ background: "transparent",
1968
+ boxShadow: "none",
1969
+ color: "var(--dsw-alias-label-secondary)"
1970
+ };
1961
1971
  const repositoryPrIcon = {
1962
1972
  width: 20,
1963
1973
  height: 20,
@@ -3251,11 +3261,6 @@ window.__ModuleLoader__.load({
3251
3261
  fontWeight: 650,
3252
3262
  whiteSpace: "nowrap"
3253
3263
  };
3254
- const diffHeaderLabel = {
3255
- minWidth: 0,
3256
- overflow: "hidden",
3257
- textOverflow: "ellipsis"
3258
- };
3259
3264
  const diffHeaderActions = {
3260
3265
  flex: "none",
3261
3266
  display: "flex",
@@ -3296,11 +3301,68 @@ window.__ModuleLoader__.load({
3296
3301
  font: "inherit",
3297
3302
  cursor: "pointer"
3298
3303
  };
3299
- const diffHeaderBranch = {
3304
+ const diffRepositoryTriggerClass = "dshClaudeDiffRepositoryTrigger";
3305
+ /** The checkout switch: a quiet pill in the header's own type, lit on hover
3306
+ * and while its menu is open, with the chevron set off in a lighter tone. */
3307
+ const diffRepositoryCss = `
3308
+ .${diffRepositoryTriggerClass} {
3309
+ flex: none;
3310
+ display: inline-flex;
3311
+ align-items: center;
3312
+ gap: 4px;
3313
+ max-width: 220px;
3314
+ padding: 2px 5px 2px 8px;
3315
+ border: none;
3316
+ border-radius: 7px;
3317
+ background: var(--dsw-alias-interactive-bg-hover);
3318
+ color: inherit;
3319
+ font: inherit;
3320
+ cursor: pointer;
3321
+ transition: background .12s ease;
3322
+ }
3323
+ .${diffRepositoryTriggerClass}:hover,
3324
+ .${diffRepositoryTriggerClass}:focus-visible,
3325
+ .${diffRepositoryTriggerClass}[aria-expanded="true"] {
3326
+ background: var(--dsw-alias-interactive-bg-active);
3327
+ }
3328
+ .${diffRepositoryTriggerClass}:focus-visible { outline: none; }
3329
+ .${diffRepositoryTriggerClass} > span {
3330
+ min-width: 0;
3331
+ overflow: hidden;
3332
+ text-overflow: ellipsis;
3333
+ white-space: nowrap;
3334
+ }
3335
+ .${diffRepositoryTriggerClass} > svg {
3336
+ flex: none;
3337
+ color: var(--dsw-alias-label-tertiary);
3338
+ }
3339
+ `;
3340
+ /** A row of the checkout menu: name left, its counts right in tabular figures. */
3341
+ const diffRepositoryRow = {
3342
+ display: "flex",
3343
+ alignItems: "baseline",
3344
+ gap: 14,
3345
+ minWidth: 0
3346
+ };
3347
+ const diffRepositoryRowName = {
3348
+ minWidth: 0,
3349
+ flex: 1,
3300
3350
  overflow: "hidden",
3301
- color: "var(--dsw-alias-label-tertiary)",
3351
+ textOverflow: "ellipsis",
3352
+ whiteSpace: "nowrap"
3353
+ };
3354
+ const diffRepositoryRowCounts = {
3355
+ flex: "none",
3356
+ display: "inline-flex",
3357
+ gap: 6,
3302
3358
  fontSize: 12,
3303
- fontWeight: 500,
3359
+ fontVariantNumeric: "tabular-nums"
3360
+ };
3361
+ /** The branch is the panel's title: it inherits the header's primary weight
3362
+ * and colour, and only yields to an ellipsis once the actions need the room. */
3363
+ const diffHeaderBranch = {
3364
+ minWidth: 0,
3365
+ overflow: "hidden",
3304
3366
  textOverflow: "ellipsis",
3305
3367
  whiteSpace: "nowrap"
3306
3368
  };
@@ -4367,6 +4429,8 @@ window.__ModuleLoader__.load({
4367
4429
  const MAX_DIFF_CHARS = 262144;
4368
4430
  const MAX_CONFLICT_PATHS = 100;
4369
4431
  const MAX_REVIEW_COMMENTS = 50;
4432
+ /** Matches the server's cap on probed checkouts. */
4433
+ const MAX_REPOSITORIES = 8;
4370
4434
  const MAX_REVIEW_COMMENT_CHARS = 2e3;
4371
4435
  const MAX_TRANSCRIPT_CHARS = 64e3;
4372
4436
  function record$7(value) {
@@ -4392,7 +4456,7 @@ window.__ModuleLoader__.load({
4392
4456
  ].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;
4393
4457
  if (repository.diff !== void 0) {
4394
4458
  const diff = record$7(repository.diff);
4395
- if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
4459
+ 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;
4396
4460
  }
4397
4461
  if (repository.pullRequest === void 0) return true;
4398
4462
  const pullRequest = record$7(repository.pullRequest);
@@ -4434,6 +4498,7 @@ window.__ModuleLoader__.load({
4434
4498
  const tasks = input.tasks === void 0 ? void 0 : record$7(input.tasks);
4435
4499
  if (tasks !== void 0 && !Array.isArray(tasks.tasks)) throw new Error("invalid Claude tasks projection");
4436
4500
  if (input.repository !== void 0 && !validateRepository(input.repository)) throw new Error("invalid Claude repository projection");
4501
+ if (input.repositories !== void 0 && (!Array.isArray(input.repositories) || input.repositories.length > MAX_REPOSITORIES || !input.repositories.every(validateRepository))) throw new Error("invalid Claude repositories projection");
4437
4502
  if (input.reviewComments !== void 0) {
4438
4503
  if (!Array.isArray(input.reviewComments) || input.reviewComments.length > MAX_REVIEW_COMMENTS) throw new Error("invalid Claude review comment projection");
4439
4504
  for (const item of input.reviewComments) {
@@ -4500,6 +4565,7 @@ window.__ModuleLoader__.load({
4500
4565
  let contextUsage;
4501
4566
  let tasks;
4502
4567
  let repository;
4568
+ let repositories;
4503
4569
  let reviewComments;
4504
4570
  let rewind;
4505
4571
  const byStep = /* @__PURE__ */ new Map();
@@ -4580,6 +4646,7 @@ window.__ModuleLoader__.load({
4580
4646
  ...contextUsage === void 0 ? {} : { contextUsage },
4581
4647
  ...tasks === void 0 ? {} : { tasks },
4582
4648
  ...repository === void 0 ? {} : { repository },
4649
+ ...repositories === void 0 ? {} : { repositories },
4583
4650
  ...reviewComments === void 0 ? {} : { reviewComments },
4584
4651
  ...rewind === void 0 ? {} : { rewind },
4585
4652
  byStep: new Map(byStep)
@@ -4681,6 +4748,7 @@ window.__ModuleLoader__.load({
4681
4748
  contextUsage = next.contextUsage;
4682
4749
  tasks = next.tasks;
4683
4750
  repository = next.repository;
4751
+ repositories = next.repositories;
4684
4752
  reviewComments = next.reviewComments;
4685
4753
  rewind = next.rewind;
4686
4754
  reset(next.activities);
@@ -4713,11 +4781,13 @@ window.__ModuleLoader__.load({
4713
4781
  owned: event.owned,
4714
4782
  commands: event.commands,
4715
4783
  ...event.repository === void 0 ? {} : { repository: event.repository },
4784
+ ...event.repositories === void 0 ? {} : { repositories: event.repositories },
4716
4785
  ...event.reviewComments === void 0 ? {} : { reviewComments: event.reviewComments }
4717
4786
  });
4718
4787
  owned = event.owned;
4719
4788
  commands = event.commands;
4720
4789
  repository = event.repository;
4790
+ repositories = event.repositories;
4721
4791
  reviewComments = event.reviewComments;
4722
4792
  revision += 1;
4723
4793
  break;
@@ -4989,10 +5059,10 @@ window.__ModuleLoader__.load({
4989
5059
  ".dsh-claude-act-running{animation:dsh-claude-act-pulse 1.2s ease-in-out infinite}",
4990
5060
  "@keyframes dsh-claude-act-pulse{0%,100%{opacity:1}50%{opacity:.3}}"
4991
5061
  ].join("");
4992
- let cssInjected$4 = false;
4993
- function ensureCss$4() {
4994
- if (cssInjected$4 || typeof document === "undefined") return;
4995
- cssInjected$4 = true;
5062
+ let cssInjected$3 = false;
5063
+ function ensureCss$3() {
5064
+ if (cssInjected$3 || typeof document === "undefined") return;
5065
+ cssInjected$3 = true;
4996
5066
  const element = document.createElement("style");
4997
5067
  element.dataset.dshClaudeActivity = "";
4998
5068
  element.textContent = ACTIVITY_CSS;
@@ -5403,7 +5473,7 @@ window.__ModuleLoader__.load({
5403
5473
  * the steps the Host never had a message for. Mounted at the turn's foot, so
5404
5474
  * it closes the turn under the task badge rather than above it. */
5405
5475
  function ClaudeTurnUsage({ usage, t }) {
5406
- ensureCss$4();
5476
+ ensureCss$3();
5407
5477
  const parts = turnUsageParts(usage, t);
5408
5478
  if (parts.length === 0) return null;
5409
5479
  return /* @__PURE__ */ jsxs("div", {
@@ -5422,7 +5492,7 @@ window.__ModuleLoader__.load({
5422
5492
  });
5423
5493
  }
5424
5494
  function ClaudeActivityNode({ node, useClaudeProjection, t }) {
5425
- ensureCss$4();
5495
+ ensureCss$3();
5426
5496
  const marker = node.data;
5427
5497
  const activities = useClaudeProjection((value) => selectStepActivities(value, marker.turn, marker.step));
5428
5498
  const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS$1);
@@ -5916,10 +5986,13 @@ window.__ModuleLoader__.load({
5916
5986
  function record$5(value) {
5917
5987
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
5918
5988
  }
5919
- function feedbackQuery(sessionId, pullNumber, extra) {
5989
+ /** `root` names another checkout the session wrote into; absent, the
5990
+ * session's own. The Host only honours roots it has vouched for. */
5991
+ function feedbackQuery(sessionId, pullNumber, root, extra) {
5920
5992
  return {
5921
5993
  sessionId,
5922
5994
  number: String(pullNumber),
5995
+ ...root === void 0 ? {} : { root },
5923
5996
  ...extra
5924
5997
  };
5925
5998
  }
@@ -5930,12 +6003,12 @@ window.__ModuleLoader__.load({
5930
6003
  }
5931
6004
  /** Every arm of this route shells out to `gh`, so reads and writes alike take
5932
6005
  * the remote budget. */
5933
- async function loadJson(path, sessionId, pullNumber, signal, extra) {
5934
- return answer(await pluginRead(`${CLAUDE_REPOSITORY_FEEDBACK_PATH}${path}`, "remote", signal, { query: feedbackQuery(sessionId, pullNumber, extra) }));
6006
+ async function loadJson(path, sessionId, pullNumber, signal, root, extra) {
6007
+ return answer(await pluginRead(`${CLAUDE_REPOSITORY_FEEDBACK_PATH}${path}`, "remote", signal, { query: feedbackQuery(sessionId, pullNumber, root, extra) }));
5935
6008
  }
5936
- async function postJson(path, sessionId, pullNumber, input) {
6009
+ async function postJson(path, sessionId, pullNumber, input, root) {
5937
6010
  return answer(await pluginWrite(`${CLAUDE_REPOSITORY_FEEDBACK_PATH}${path}`, "remote", void 0, {
5938
- query: feedbackQuery(sessionId, pullNumber),
6011
+ query: feedbackQuery(sessionId, pullNumber, root),
5939
6012
  json: input
5940
6013
  }));
5941
6014
  }
@@ -5944,8 +6017,8 @@ window.__ModuleLoader__.load({
5944
6017
  if (input === void 0 || typeof input.id !== "number" || typeof input.path !== "string" || typeof input.author !== "string" || typeof input.body !== "string" || typeof input.url !== "string" || input.avatarUrl !== void 0 && githubAvatarUrl(input.avatarUrl) === void 0 || input.side !== "new" && input.side !== "old" || input.line !== void 0 && typeof input.line !== "number" || input.createdAt !== void 0 && typeof input.createdAt !== "string" || input.bot !== void 0 && typeof input.bot !== "boolean") return void 0;
5945
6018
  return input;
5946
6019
  }
5947
- async function loadPullRequestThreads(sessionId, pullNumber, signal) {
5948
- const body = await loadJson("/comments", sessionId, pullNumber, signal);
6020
+ async function loadPullRequestThreads(sessionId, pullNumber, signal, root) {
6021
+ const body = await loadJson("/comments", sessionId, pullNumber, signal, root);
5949
6022
  if (!Array.isArray(body.threads)) throw new Error("Invalid pull request comments response.");
5950
6023
  const threads = [];
5951
6024
  for (const item of body.threads) {
@@ -5966,26 +6039,26 @@ window.__ModuleLoader__.load({
5966
6039
  return threads;
5967
6040
  }
5968
6041
  /** Post one reply into the thread that `commentId` belongs to. */
5969
- async function replyToReviewThread(sessionId, pullNumber, commentId, body) {
6042
+ async function replyToReviewThread(sessionId, pullNumber, commentId, body, root) {
5970
6043
  const comment = reviewComment((await postJson("/reply", sessionId, pullNumber, {
5971
6044
  commentId,
5972
6045
  body
5973
- })).comment);
6046
+ }, root)).comment);
5974
6047
  if (comment === void 0) throw new Error("Invalid pull request reply response.");
5975
6048
  return comment;
5976
6049
  }
5977
6050
  /** Resolve or reopen a thread; returns the state GitHub reports afterwards. */
5978
- async function setReviewThreadResolved(sessionId, pullNumber, threadId, resolved) {
6051
+ async function setReviewThreadResolved(sessionId, pullNumber, threadId, resolved, root) {
5979
6052
  const answer = await postJson("/resolve", sessionId, pullNumber, {
5980
6053
  threadId,
5981
6054
  resolved
5982
- });
6055
+ }, root);
5983
6056
  if (typeof answer.resolved !== "boolean") throw new Error("Invalid pull request resolve response.");
5984
6057
  return answer.resolved;
5985
6058
  }
5986
6059
  /** Logins GitHub would notify, for the reply composer's `@` completion. */
5987
- async function loadMentionableUsers(sessionId, pullNumber, query, signal) {
5988
- const body = await loadJson("/mentionables", sessionId, pullNumber, signal, { q: query });
6060
+ async function loadMentionableUsers(sessionId, pullNumber, query, signal, root) {
6061
+ const body = await loadJson("/mentionables", sessionId, pullNumber, signal, root, { q: query });
5989
6062
  if (!Array.isArray(body.users)) return [];
5990
6063
  const users = [];
5991
6064
  for (const item of body.users) {
@@ -6113,6 +6186,11 @@ window.__ModuleLoader__.load({
6113
6186
  if (repository.branch !== void 0) return repository.branch;
6114
6187
  return repository.detached === true ? t("repositoryDetached") : t("repositoryUnknownBranch");
6115
6188
  }
6189
+ /** The short name a checkout goes by: its GitHub repository, else the
6190
+ * directory it lives in. */
6191
+ function repositoryLabel(repository) {
6192
+ return repository.remote?.split("/").at(-1) ?? repository.root?.split(/[\\/]/u).filter((part) => part.length > 0).at(-1) ?? repository.cwd;
6193
+ }
6116
6194
  //#endregion
6117
6195
  //#region src/client/session-preset.ts
6118
6196
  /**
@@ -6167,6 +6245,42 @@ window.__ModuleLoader__.load({
6167
6245
  function repositoryName$1(remote) {
6168
6246
  return remote?.split("/").at(-1);
6169
6247
  }
6248
+ function PullRequestBadges({ pullRequest, t }) {
6249
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [pullRequest?.state === "open" && pullRequest.checks !== "none" ? /* @__PURE__ */ jsx(Badge, {
6250
+ label: t(`repositoryChecks_${pullRequest.checks}`),
6251
+ tone: pullRequest.checks === "passing" ? "success" : pullRequest.checks === "failing" ? "error" : "warning"
6252
+ }) : null, pullRequest?.state === "open" && pullRequest.review !== "none" ? /* @__PURE__ */ jsx(Badge, {
6253
+ label: t(`repositoryReview_${pullRequest.review}`),
6254
+ tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral"
6255
+ }) : null] });
6256
+ }
6257
+ function PullRequestBadge({ pullRequest, t }) {
6258
+ return pullRequest === void 0 ? /* @__PURE__ */ jsx(Badge, { label: t("overviewNoPr") }) : /* @__PURE__ */ jsx(Badge, {
6259
+ label: `#${pullRequest.number} · ${t(`repositoryState_${pullRequest.state}`)}`,
6260
+ tone: pullRequest.state === "merged" ? "merged" : pullRequest.state === "open" ? "success" : "neutral"
6261
+ });
6262
+ }
6263
+ /** The other checkouts a session wrote into, one line each, under its row. */
6264
+ function OverviewOtherRepositories({ source, t }) {
6265
+ return /* @__PURE__ */ jsx(Fragment$1, { children: (useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot).repositories ?? []).map((repository) => /* @__PURE__ */ jsxs("span", {
6266
+ style: overviewMeta,
6267
+ children: [
6268
+ /* @__PURE__ */ jsx("span", { children: repositoryLabel(repository) }),
6269
+ /* @__PURE__ */ jsx("span", {
6270
+ style: overviewBranch,
6271
+ children: branchLabel(repository, t)
6272
+ }),
6273
+ /* @__PURE__ */ jsx(PullRequestBadge, {
6274
+ pullRequest: repository.pullRequest,
6275
+ t
6276
+ }),
6277
+ /* @__PURE__ */ jsx(PullRequestBadges, {
6278
+ pullRequest: repository.pullRequest,
6279
+ t
6280
+ })
6281
+ ]
6282
+ }, repository.root ?? repository.cwd)) });
6283
+ }
6170
6284
  function OverviewAttention({ source, running, t }) {
6171
6285
  const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
6172
6286
  const attention = running ? overviewAttention(snapshot.activities) : void 0;
@@ -6258,49 +6372,52 @@ window.__ModuleLoader__.load({
6258
6372
  onClick: () => {
6259
6373
  openSession(row.id);
6260
6374
  },
6261
- children: [/* @__PURE__ */ jsxs("span", {
6262
- style: overviewRowTop,
6263
- children: [
6264
- row.running === true ? /* @__PURE__ */ jsx("span", {
6265
- style: overviewRunningDot,
6266
- "aria-label": t("overviewRunning")
6267
- }) : null,
6268
- /* @__PURE__ */ jsx("span", {
6269
- style: overviewTitle,
6270
- children: row.displayTitle ?? row.id
6271
- }),
6272
- pullRequest === void 0 ? /* @__PURE__ */ jsx(Badge, { label: t("overviewNoPr") }) : /* @__PURE__ */ jsx(Badge, {
6273
- label: `#${pullRequest.number} · ${t(`repositoryState_${pullRequest.state}`)}`,
6274
- tone: pullRequest.state === "merged" ? "merged" : pullRequest.state === "open" ? "success" : "neutral"
6275
- })
6276
- ]
6277
- }), /* @__PURE__ */ jsxs("span", {
6278
- style: overviewMeta,
6279
- children: [
6280
- repositoryName$1(repository?.remote) === void 0 ? null : /* @__PURE__ */ jsx("span", { children: repositoryName$1(repository?.remote) }),
6281
- /* @__PURE__ */ jsx("span", {
6282
- style: overviewBranch,
6283
- children: branch
6284
- }),
6285
- pullRequest?.state === "open" && pullRequest.checks !== "none" ? /* @__PURE__ */ jsx(Badge, {
6286
- label: t(`repositoryChecks_${pullRequest.checks}`),
6287
- tone: pullRequest.checks === "passing" ? "success" : pullRequest.checks === "failing" ? "error" : "warning"
6288
- }) : null,
6289
- pullRequest?.state === "open" && pullRequest.review !== "none" ? /* @__PURE__ */ jsx(Badge, {
6290
- label: t(`repositoryReview_${pullRequest.review}`),
6291
- tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral"
6292
- }) : null,
6293
- autoFixEnabled(row.id) ? /* @__PURE__ */ jsx(Badge, {
6294
- label: t("overviewAutoFix"),
6295
- tone: "success"
6296
- }) : null,
6297
- projectionFor === void 0 ? null : /* @__PURE__ */ jsx(OverviewAttention, {
6298
- source: projectionFor(row.id),
6299
- running: row.running === true,
6300
- t
6301
- })
6302
- ]
6303
- })]
6375
+ children: [
6376
+ /* @__PURE__ */ jsxs("span", {
6377
+ style: overviewRowTop,
6378
+ children: [
6379
+ row.running === true ? /* @__PURE__ */ jsx("span", {
6380
+ style: overviewRunningDot,
6381
+ "aria-label": t("overviewRunning")
6382
+ }) : null,
6383
+ /* @__PURE__ */ jsx("span", {
6384
+ style: overviewTitle,
6385
+ children: row.displayTitle ?? row.id
6386
+ }),
6387
+ /* @__PURE__ */ jsx(PullRequestBadge, {
6388
+ pullRequest,
6389
+ t
6390
+ })
6391
+ ]
6392
+ }),
6393
+ /* @__PURE__ */ jsxs("span", {
6394
+ style: overviewMeta,
6395
+ children: [
6396
+ repositoryName$1(repository?.remote) === void 0 ? null : /* @__PURE__ */ jsx("span", { children: repositoryName$1(repository?.remote) }),
6397
+ /* @__PURE__ */ jsx("span", {
6398
+ style: overviewBranch,
6399
+ children: branch
6400
+ }),
6401
+ /* @__PURE__ */ jsx(PullRequestBadges, {
6402
+ pullRequest,
6403
+ t
6404
+ }),
6405
+ autoFixEnabled(row.id) ? /* @__PURE__ */ jsx(Badge, {
6406
+ label: t("overviewAutoFix"),
6407
+ tone: "success"
6408
+ }) : null,
6409
+ projectionFor === void 0 ? null : /* @__PURE__ */ jsx(OverviewAttention, {
6410
+ source: projectionFor(row.id),
6411
+ running: row.running === true,
6412
+ t
6413
+ })
6414
+ ]
6415
+ }),
6416
+ projectionFor === void 0 ? null : /* @__PURE__ */ jsx(OverviewOtherRepositories, {
6417
+ source: projectionFor(row.id),
6418
+ t
6419
+ })
6420
+ ]
6304
6421
  }, row.id);
6305
6422
  })
6306
6423
  })
@@ -7852,10 +7969,10 @@ window.__ModuleLoader__.load({
7852
7969
  "width:6px;height:6px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#e2a03f);",
7853
7970
  "box-shadow:0 0 0 2px var(--dsw-alias-bg-base)}"
7854
7971
  ].join("");
7855
- let cssInjected$3 = false;
7856
- function ensureCss$3() {
7857
- if (cssInjected$3 || typeof document === "undefined") return;
7858
- cssInjected$3 = true;
7972
+ let cssInjected$2 = false;
7973
+ function ensureCss$2() {
7974
+ if (cssInjected$2 || typeof document === "undefined") return;
7975
+ cssInjected$2 = true;
7859
7976
  const element = document.createElement("style");
7860
7977
  element.dataset.dshClaudeHeaderPlan = "";
7861
7978
  element.textContent = ACTION_CSS$1;
@@ -7891,7 +8008,7 @@ window.__ModuleLoader__.load({
7891
8008
  if (!open) togglePlan();
7892
8009
  });
7893
8010
  if (!owned || review === void 0) return null;
7894
- ensureCss$3();
8011
+ ensureCss$2();
7895
8012
  const label = t(open ? "planClose" : "planOpen");
7896
8013
  return /* @__PURE__ */ jsx(Tooltip, {
7897
8014
  label,
@@ -7951,18 +8068,26 @@ window.__ModuleLoader__.load({
7951
8068
  if (input === void 0 || typeof input.commit !== "string" || typeof input.pushed !== "boolean" || input.pullRequestUrl !== void 0 && typeof input.pullRequestUrl !== "string" || input.conflicts !== void 0 && (!Array.isArray(input.conflicts) || input.conflicts.some((item) => typeof item !== "string"))) throw new Error("Invalid repository action result.");
7952
8069
  return input;
7953
8070
  }
8071
+ /** `root` names another checkout the session wrote into; absent, the
8072
+ * session's own. The Host only honours roots it has vouched for. */
8073
+ function sessionQuery(sessionId, root) {
8074
+ return {
8075
+ sessionId,
8076
+ ...root === void 0 ? {} : { root }
8077
+ };
8078
+ }
7954
8079
  /** The preview only chains local Git; everything that writes may reach a remote. */
7955
- async function loadRepositoryActionPreview(sessionId, signal) {
8080
+ async function loadRepositoryActionPreview(sessionId, signal, root) {
7956
8081
  try {
7957
- return preview(await pluginRead(`${CLAUDE_REPOSITORY_ACTION_PATH}/preview`, "git", signal, { query: { sessionId } }));
8082
+ return preview(await pluginRead(`${CLAUDE_REPOSITORY_ACTION_PATH}/preview`, "git", signal, { query: sessionQuery(sessionId, root) }));
7958
8083
  } catch (error) {
7959
8084
  throw actionError(error);
7960
8085
  }
7961
8086
  }
7962
- async function generateCommitMessage(sessionId, fingerprint, signal) {
8087
+ async function generateCommitMessage(sessionId, fingerprint, signal, root) {
7963
8088
  try {
7964
8089
  const value = record$3(await pluginWrite(`${CLAUDE_REPOSITORY_ACTION_PATH}/message`, "remote", signal, {
7965
- query: { sessionId },
8090
+ query: sessionQuery(sessionId, root),
7966
8091
  json: { fingerprint }
7967
8092
  }));
7968
8093
  if (typeof value?.message !== "string") throw new Error("Invalid generated commit message.");
@@ -7971,10 +8096,10 @@ window.__ModuleLoader__.load({
7971
8096
  throw actionError(error);
7972
8097
  }
7973
8098
  }
7974
- async function executeRepositoryAction(sessionId, request) {
8099
+ async function executeRepositoryAction(sessionId, request, root) {
7975
8100
  try {
7976
8101
  return result(await pluginWrite(CLAUDE_REPOSITORY_ACTION_PATH, "remote", void 0, {
7977
- query: { sessionId },
8102
+ query: sessionQuery(sessionId, root),
7978
8103
  json: request
7979
8104
  }));
7980
8105
  } catch (error) {
@@ -8195,7 +8320,13 @@ window.__ModuleLoader__.load({
8195
8320
  ],
8196
8321
  uiConversation: ["binding"],
8197
8322
  uiSession: ["provide"],
8198
- inputTriggers: ["registerSource"]
8323
+ inputTriggers: ["registerSource"],
8324
+ sidebarRight: [
8325
+ "openTabIn",
8326
+ "closeIn",
8327
+ "toggleExpanded"
8328
+ ],
8329
+ sidebarRightTabs: ["register"]
8199
8330
  };
8200
8331
  /** One line per service the Host no longer provides, or provides without a
8201
8332
  * method this plugin calls on it; empty when everything resolves. */
@@ -9112,25 +9243,28 @@ window.__ModuleLoader__.load({
9112
9243
  setMenuOpen(false);
9113
9244
  setDialog({
9114
9245
  method,
9246
+ admin: false,
9115
9247
  loading: true,
9116
9248
  submitting: false
9117
9249
  });
9118
9250
  const aborter = new AbortController();
9119
9251
  controller.current = aborter;
9120
9252
  loadRepositoryActionPreview(sessionId, aborter.signal).then((preview) => {
9121
- setDialog({
9253
+ setDialog((current) => ({
9122
9254
  method,
9255
+ admin: current?.admin ?? false,
9123
9256
  loading: false,
9124
9257
  submitting: false,
9125
9258
  fingerprint: preview.fingerprint
9126
- });
9259
+ }));
9127
9260
  }, (error) => {
9128
- if (!aborter.signal.aborted) setDialog({
9261
+ if (!aborter.signal.aborted) setDialog((current) => ({
9129
9262
  method,
9263
+ admin: current?.admin ?? false,
9130
9264
  loading: false,
9131
9265
  submitting: false,
9132
9266
  error: error instanceof Error ? error.message : t("diffActionFailed")
9133
- });
9267
+ }));
9134
9268
  });
9135
9269
  };
9136
9270
  const closeDialog = () => {
@@ -9151,7 +9285,8 @@ window.__ModuleLoader__.load({
9151
9285
  fingerprint: dialog.fingerprint,
9152
9286
  message: "",
9153
9287
  includeUnstaged: false,
9154
- mergeMethod: dialog.method
9288
+ mergeMethod: dialog.method,
9289
+ ...dialog.admin ? { admin: true } : {}
9155
9290
  }).then(() => {
9156
9291
  report(t("diffMergeCompleted", { number: pullRequest.number }));
9157
9292
  setDialog(void 0);
@@ -9240,6 +9375,22 @@ window.__ModuleLoader__.load({
9240
9375
  style: diffModalStatus,
9241
9376
  children: pullRequest.title
9242
9377
  }),
9378
+ /* @__PURE__ */ jsxs("label", {
9379
+ style: diffModalCheckbox,
9380
+ children: [/* @__PURE__ */ jsx("input", {
9381
+ type: "checkbox",
9382
+ name: "dsh-claude-merge-admin",
9383
+ checked: dialog.admin,
9384
+ disabled: dialog.submitting,
9385
+ onChange: (event) => {
9386
+ const { checked } = event.currentTarget;
9387
+ setDialog((current) => current === void 0 ? current : {
9388
+ ...current,
9389
+ admin: checked
9390
+ });
9391
+ }
9392
+ }), t("diffMergeAdmin")]
9393
+ }),
9243
9394
  dialog.error === void 0 ? null : /* @__PURE__ */ jsx("p", {
9244
9395
  role: "alert",
9245
9396
  style: diffModalError,
@@ -9250,6 +9401,124 @@ window.__ModuleLoader__.load({
9250
9401
  })
9251
9402
  ] });
9252
9403
  }
9404
+ function LinkIcon() {
9405
+ return /* @__PURE__ */ jsxs("svg", {
9406
+ width: "16",
9407
+ height: "16",
9408
+ viewBox: "0 0 16 16",
9409
+ fill: "none",
9410
+ stroke: "currentColor",
9411
+ strokeWidth: "1.8",
9412
+ strokeLinecap: "round",
9413
+ strokeLinejoin: "round",
9414
+ "aria-hidden": "true",
9415
+ 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" })]
9416
+ });
9417
+ }
9418
+ /** A checkout the session wrote into besides its own, stacked above the
9419
+ * session bar so that one stays put next to the composer. The same readout
9420
+ * as the session bar, minus the controls that act on the session (auto-fix, update,
9421
+ * merge, cleanup), which live with the checkout the session was opened on. */
9422
+ function LinkedRepositoryBar({ repository, t, openDiff, report }) {
9423
+ const key = repository.root ?? repository.cwd;
9424
+ if (repository.status !== "ready") return /* @__PURE__ */ jsxs("div", {
9425
+ style: {
9426
+ ...repositoryBar,
9427
+ ...repositoryBarLinked
9428
+ },
9429
+ "data-dsh-claude-linked-repository": key,
9430
+ children: [/* @__PURE__ */ jsx(StatusGlyph, {
9431
+ label: t("repositoryLinked"),
9432
+ tone: "neutral",
9433
+ children: /* @__PURE__ */ jsx(LinkIcon, {})
9434
+ }), /* @__PURE__ */ jsxs("span", {
9435
+ style: repositoryPrimary,
9436
+ children: [
9437
+ repositoryLabel(repository),
9438
+ " · ",
9439
+ repository.status === "not-repository" ? t("repositoryNotGit") : t("repositoryUnavailable")
9440
+ ]
9441
+ })]
9442
+ });
9443
+ const branch = branchLabel(repository, t);
9444
+ const pullRequest = repository.pullRequest;
9445
+ const merged = pullRequest?.state === "merged";
9446
+ const hasDiff = repository.diff !== void 0 && (repository.diff.additions > 0 || repository.diff.deletions > 0);
9447
+ return /* @__PURE__ */ jsxs("div", {
9448
+ style: {
9449
+ ...repositoryBar,
9450
+ ...repositoryBarLinked,
9451
+ ...merged ? repositoryBarMerged : {}
9452
+ },
9453
+ "data-dsh-claude-linked-repository": key,
9454
+ children: [
9455
+ /* @__PURE__ */ jsx(StatusGlyph, {
9456
+ label: t("repositoryLinked"),
9457
+ tone: "neutral",
9458
+ children: /* @__PURE__ */ jsx(LinkIcon, {})
9459
+ }),
9460
+ /* @__PURE__ */ jsx(PullRequestLink, {
9461
+ repository,
9462
+ t
9463
+ }),
9464
+ /* @__PURE__ */ jsx("span", {
9465
+ style: repositoryRemote,
9466
+ children: repositoryLabel(repository)
9467
+ }),
9468
+ /* @__PURE__ */ jsx(Tooltip, {
9469
+ label: branch,
9470
+ side: "top",
9471
+ delayMs: 250,
9472
+ maxWidth: 420,
9473
+ children: /* @__PURE__ */ jsx("span", {
9474
+ style: repositoryBranch,
9475
+ children: branch
9476
+ })
9477
+ }),
9478
+ repository.worktree === true ? /* @__PURE__ */ jsx("span", {
9479
+ style: repositoryWorktree,
9480
+ children: t("repositoryWorktree")
9481
+ }) : null,
9482
+ /* @__PURE__ */ jsxs("span", {
9483
+ style: repositoryStatusItems,
9484
+ children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs("button", {
9485
+ type: "button",
9486
+ style: {
9487
+ ...diffTrigger,
9488
+ ...merged ? diffTriggerMuted : {}
9489
+ },
9490
+ onClick: () => openDiff(repository.root),
9491
+ "aria-label": t("diffOpen"),
9492
+ children: [/* @__PURE__ */ jsxs("span", {
9493
+ style: merged ? diffAddMuted : diffAdd,
9494
+ children: ["+", repository.diff.additions]
9495
+ }), /* @__PURE__ */ jsxs("span", {
9496
+ style: merged ? diffDeleteMuted : diffDelete,
9497
+ children: ["−", repository.diff.deletions]
9498
+ })]
9499
+ }) : null, pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9500
+ style: repositoryMergedStatus,
9501
+ children: [/* @__PURE__ */ jsx("span", {
9502
+ style: repositoryMergedDot,
9503
+ "aria-hidden": "true"
9504
+ }), t("repositoryState_merged")]
9505
+ }), /* @__PURE__ */ jsx(CleanupControl, {
9506
+ repository,
9507
+ t,
9508
+ report
9509
+ })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9510
+ label: t(`repositoryChecks_${pullRequest.checks}`),
9511
+ tone: pullRequest.checks === "passing" ? "success" : pullRequest.checks === "failing" ? "error" : "warning",
9512
+ children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9513
+ }), pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9514
+ label: t(`repositoryReview_${pullRequest.review}`),
9515
+ tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9516
+ children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9517
+ })] })]
9518
+ })
9519
+ ]
9520
+ });
9521
+ }
9253
9522
  function ClaudeRepositoryStatus({ sessionId, useSessions, useClaudeProjection, t, openDiff, submitPrompt, openOverview, deleteWorkspace }) {
9254
9523
  const blank = useSessions((value) => value.byId[sessionId]?.blank === true);
9255
9524
  const running = useSessions((value) => value.byId[sessionId]?.running === true);
@@ -9264,10 +9533,16 @@ window.__ModuleLoader__.load({
9264
9533
  const aheadCount = repository.ahead ?? 0;
9265
9534
  const pushable = repository.remote !== void 0 && repository.detached !== true && (aheadCount > 0 || repository.upstream === false);
9266
9535
  const hasDiff = repository.diff !== void 0 && (repository.diff.additions > 0 || repository.diff.deletions > 0);
9267
- if (repository.status !== "ready") return /* @__PURE__ */ jsx("div", {
9536
+ const linked = (projection.repositories ?? []).map((item) => /* @__PURE__ */ jsx(LinkedRepositoryBar, {
9537
+ repository: item,
9538
+ t,
9539
+ openDiff,
9540
+ report
9541
+ }, item.root ?? item.cwd));
9542
+ if (repository.status !== "ready") return /* @__PURE__ */ jsxs("div", {
9268
9543
  style: repositoryBarFrame,
9269
9544
  [CLAUDE_COMPOSER_BAR_ATTRIBUTE]: "",
9270
- children: /* @__PURE__ */ jsxs("div", {
9545
+ children: [linked, /* @__PURE__ */ jsxs("div", {
9271
9546
  style: repositoryBar,
9272
9547
  children: [/* @__PURE__ */ jsx("span", {
9273
9548
  style: repositoryPrIcon,
@@ -9276,145 +9551,149 @@ window.__ModuleLoader__.load({
9276
9551
  style: repositoryPrimary,
9277
9552
  children: repository.status === "not-repository" ? t("repositoryNotGit") : t("repositoryUnavailable")
9278
9553
  })]
9279
- })
9554
+ })]
9280
9555
  });
9281
9556
  return /* @__PURE__ */ jsxs("div", {
9282
9557
  style: repositoryBarFrame,
9283
9558
  [CLAUDE_COMPOSER_BAR_ATTRIBUTE]: "",
9284
- children: [toast, /* @__PURE__ */ jsxs("div", {
9285
- style: {
9286
- ...repositoryBar,
9287
- ...merged ? repositoryBarMerged : {}
9288
- },
9289
- children: [
9290
- openOverview === void 0 ? /* @__PURE__ */ jsx("span", {
9291
- style: {
9292
- ...repositoryPrIcon,
9293
- ...merged ? repositoryPrIconMerged : {}
9294
- },
9295
- children: /* @__PURE__ */ jsx(PullRequestIcon, { merged })
9296
- }) : /* @__PURE__ */ jsx("button", {
9297
- type: "button",
9298
- style: {
9299
- ...repositoryPrIcon,
9300
- ...merged ? repositoryPrIconMerged : {},
9301
- ...repositoryPrIconButton
9302
- },
9303
- "aria-label": t("overviewOpen"),
9304
- title: t("overviewOpen"),
9305
- onClick: openOverview,
9306
- children: /* @__PURE__ */ jsx(PullRequestIcon, { merged })
9307
- }),
9308
- /* @__PURE__ */ jsx(PullRequestLink, {
9309
- repository,
9310
- t
9311
- }),
9312
- repository.remote === void 0 ? null : /* @__PURE__ */ jsx("span", {
9313
- style: repositoryRemote,
9314
- children: repositoryName(repository.remote)
9315
- }),
9316
- /* @__PURE__ */ jsx(Tooltip, {
9317
- label: branch,
9318
- side: "top",
9319
- delayMs: 250,
9320
- maxWidth: 420,
9321
- children: /* @__PURE__ */ jsx("span", {
9322
- style: repositoryBranch,
9323
- children: branch
9324
- })
9325
- }),
9326
- repository.worktree === true ? /* @__PURE__ */ jsx("span", {
9327
- style: repositoryWorktree,
9328
- children: t("repositoryWorktree")
9329
- }) : null,
9330
- /* @__PURE__ */ jsxs("span", {
9331
- style: repositoryStatusItems,
9332
- children: [
9333
- /* @__PURE__ */ jsx(ConflictControl, {
9334
- sessionId,
9335
- repository,
9336
- t,
9337
- report,
9338
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9339
- }),
9340
- hasDiff || pushable ? /* @__PURE__ */ jsxs("button", {
9341
- type: "button",
9342
- style: {
9343
- ...diffTrigger,
9344
- ...merged ? diffTriggerMuted : {}
9345
- },
9346
- onClick: openDiff,
9347
- "aria-label": t("diffOpen"),
9348
- children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9349
- style: merged ? diffAddMuted : diffAdd,
9350
- children: ["+", repository.diff.additions]
9351
- }), /* @__PURE__ */ jsxs("span", {
9352
- style: merged ? diffDeleteMuted : diffDelete,
9353
- children: ["−", repository.diff.deletions]
9354
- })] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
9355
- style: merged ? diffAheadMuted : diffAhead,
9356
- children: ["↑", aheadCount > 0 ? aheadCount : ""]
9357
- }) : null]
9358
- }) : null,
9359
- pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9360
- style: repositoryMergedStatus,
9361
- children: [
9362
- /* @__PURE__ */ jsx("span", {
9363
- style: repositoryMergedDot,
9364
- "aria-hidden": "true"
9365
- }),
9366
- t("repositoryState_merged"),
9367
- mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
9368
- style: repositoryMergedAge,
9369
- children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
9370
- })
9371
- ]
9372
- }), /* @__PURE__ */ jsx(CleanupControl, {
9373
- repository,
9374
- t,
9375
- report,
9376
- ...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
9377
- })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
9378
- pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
9379
- sessionId,
9380
- pullNumber: pullRequest.number,
9381
- t,
9382
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9383
- }) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9384
- label: t(`repositoryChecks_${pullRequest.checks}`),
9385
- tone: pullRequest.checks === "passing" ? "success" : "warning",
9386
- children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9387
- }),
9388
- pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9389
- label: t(`repositoryReview_${pullRequest.review}`),
9390
- tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9391
- children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9392
- }),
9393
- /* @__PURE__ */ jsx(AutoFixControl, {
9394
- sessionId,
9395
- repository,
9396
- running,
9397
- t,
9398
- ...submitPrompt === void 0 ? {} : { submitPrompt }
9399
- }),
9400
- /* @__PURE__ */ jsx(UpdateBranchControl, {
9559
+ children: [
9560
+ toast,
9561
+ linked,
9562
+ /* @__PURE__ */ jsxs("div", {
9563
+ style: {
9564
+ ...repositoryBar,
9565
+ ...merged ? repositoryBarMerged : {}
9566
+ },
9567
+ children: [
9568
+ openOverview === void 0 ? /* @__PURE__ */ jsx("span", {
9569
+ style: {
9570
+ ...repositoryPrIcon,
9571
+ ...merged ? repositoryPrIconMerged : {}
9572
+ },
9573
+ children: /* @__PURE__ */ jsx(PullRequestIcon, { merged })
9574
+ }) : /* @__PURE__ */ jsx("button", {
9575
+ type: "button",
9576
+ style: {
9577
+ ...repositoryPrIcon,
9578
+ ...merged ? repositoryPrIconMerged : {},
9579
+ ...repositoryPrIconButton
9580
+ },
9581
+ "aria-label": t("overviewOpen"),
9582
+ title: t("overviewOpen"),
9583
+ onClick: openOverview,
9584
+ children: /* @__PURE__ */ jsx(PullRequestIcon, { merged })
9585
+ }),
9586
+ /* @__PURE__ */ jsx(PullRequestLink, {
9587
+ repository,
9588
+ t
9589
+ }),
9590
+ repository.remote === void 0 ? null : /* @__PURE__ */ jsx("span", {
9591
+ style: repositoryRemote,
9592
+ children: repositoryName(repository.remote)
9593
+ }),
9594
+ /* @__PURE__ */ jsx(Tooltip, {
9595
+ label: branch,
9596
+ side: "top",
9597
+ delayMs: 250,
9598
+ maxWidth: 420,
9599
+ children: /* @__PURE__ */ jsx("span", {
9600
+ style: repositoryBranch,
9601
+ children: branch
9602
+ })
9603
+ }),
9604
+ repository.worktree === true ? /* @__PURE__ */ jsx("span", {
9605
+ style: repositoryWorktree,
9606
+ children: t("repositoryWorktree")
9607
+ }) : null,
9608
+ /* @__PURE__ */ jsxs("span", {
9609
+ style: repositoryStatusItems,
9610
+ children: [
9611
+ /* @__PURE__ */ jsx(ConflictControl, {
9401
9612
  sessionId,
9402
9613
  repository,
9403
9614
  t,
9404
9615
  report,
9405
9616
  ...submitPrompt === void 0 ? {} : { submitPrompt }
9406
9617
  }),
9407
- /* @__PURE__ */ jsx(MergePullRequestControl, {
9408
- sessionId,
9409
- repository,
9410
- t,
9411
- report
9412
- })
9413
- ] })
9414
- ]
9415
- })
9416
- ]
9417
- })]
9618
+ hasDiff || pushable ? /* @__PURE__ */ jsxs("button", {
9619
+ type: "button",
9620
+ style: {
9621
+ ...diffTrigger,
9622
+ ...merged ? diffTriggerMuted : {}
9623
+ },
9624
+ onClick: () => openDiff(),
9625
+ "aria-label": t("diffOpen"),
9626
+ children: [hasDiff && repository.diff !== void 0 ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9627
+ style: merged ? diffAddMuted : diffAdd,
9628
+ children: ["+", repository.diff.additions]
9629
+ }), /* @__PURE__ */ jsxs("span", {
9630
+ style: merged ? diffDeleteMuted : diffDelete,
9631
+ children: ["−", repository.diff.deletions]
9632
+ })] }) : null, pushable ? /* @__PURE__ */ jsxs("span", {
9633
+ style: merged ? diffAheadMuted : diffAhead,
9634
+ children: ["↑", aheadCount > 0 ? aheadCount : ""]
9635
+ }) : null]
9636
+ }) : null,
9637
+ pullRequest === void 0 ? null : merged ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9638
+ style: repositoryMergedStatus,
9639
+ children: [
9640
+ /* @__PURE__ */ jsx("span", {
9641
+ style: repositoryMergedDot,
9642
+ "aria-hidden": "true"
9643
+ }),
9644
+ t("repositoryState_merged"),
9645
+ mergedAge === void 0 ? null : /* @__PURE__ */ jsxs("span", {
9646
+ style: repositoryMergedAge,
9647
+ children: ["· ", t("repositoryMergedAgo", { age: mergedAge })]
9648
+ })
9649
+ ]
9650
+ }), /* @__PURE__ */ jsx(CleanupControl, {
9651
+ repository,
9652
+ t,
9653
+ report,
9654
+ ...deleteWorkspace === void 0 ? {} : { deleteWorkspace }
9655
+ })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
9656
+ pullRequest.checks === "failing" ? /* @__PURE__ */ jsx(FailingChecksControl, {
9657
+ sessionId,
9658
+ pullNumber: pullRequest.number,
9659
+ t,
9660
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9661
+ }) : pullRequest.checks === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9662
+ label: t(`repositoryChecks_${pullRequest.checks}`),
9663
+ tone: pullRequest.checks === "passing" ? "success" : "warning",
9664
+ children: /* @__PURE__ */ jsx(ChecksGlyph, { state: pullRequest.checks })
9665
+ }),
9666
+ pullRequest.review === "none" ? null : /* @__PURE__ */ jsx(StatusGlyph, {
9667
+ label: t(`repositoryReview_${pullRequest.review}`),
9668
+ tone: pullRequest.review === "approved" ? "success" : pullRequest.review === "changes-requested" ? "error" : "neutral",
9669
+ children: /* @__PURE__ */ jsx(ReviewGlyph, {})
9670
+ }),
9671
+ /* @__PURE__ */ jsx(AutoFixControl, {
9672
+ sessionId,
9673
+ repository,
9674
+ running,
9675
+ t,
9676
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9677
+ }),
9678
+ /* @__PURE__ */ jsx(UpdateBranchControl, {
9679
+ sessionId,
9680
+ repository,
9681
+ t,
9682
+ report,
9683
+ ...submitPrompt === void 0 ? {} : { submitPrompt }
9684
+ }),
9685
+ /* @__PURE__ */ jsx(MergePullRequestControl, {
9686
+ sessionId,
9687
+ repository,
9688
+ t,
9689
+ report
9690
+ })
9691
+ ] })
9692
+ ]
9693
+ })
9694
+ ]
9695
+ })
9696
+ ]
9418
9697
  });
9419
9698
  }
9420
9699
  //#endregion
@@ -9593,6 +9872,165 @@ window.__ModuleLoader__.load({
9593
9872
  });
9594
9873
  }
9595
9874
  //#endregion
9875
+ //#region src/client/ClaudeQueueDock.tsx
9876
+ /** Replaces the Host queue strip (same slot cell, lower priority) with one
9877
+ * styled like the repository status bar. Behaviour mirrors the Host strip:
9878
+ * one row inline, several rows behind a collapsible count, edit/remove/steer. */
9879
+ function ClaudeQueueDock({ useSession, t, updateQueue, notify }) {
9880
+ const inbox = useSession((session) => session.queue);
9881
+ const queue = useMemo(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
9882
+ const running = useSession((session) => session.running);
9883
+ const mutable = useSession((session) => session.subagent === null);
9884
+ const [editing, setEditing] = useState();
9885
+ const [busy, setBusy] = useState();
9886
+ const [collapsed, setCollapsed] = useState(true);
9887
+ const listId = useId();
9888
+ useEffect(() => {
9889
+ if (queue.length === 0 && !collapsed) setCollapsed(true);
9890
+ if (editing !== void 0 && (!mutable || !queue.some((row) => row.id === editing.id))) setEditing(void 0);
9891
+ }, [
9892
+ collapsed,
9893
+ editing,
9894
+ mutable,
9895
+ queue
9896
+ ]);
9897
+ if (queue.length === 0) return null;
9898
+ const interacting = mutable && (editing !== void 0 || busy !== void 0);
9899
+ const expanded = !collapsed || interacting;
9900
+ const listVisible = queue.length === 1 || expanded;
9901
+ const apply = async (id, action, failure) => {
9902
+ setBusy(id);
9903
+ try {
9904
+ await updateQueue(id, action);
9905
+ return true;
9906
+ } catch {
9907
+ notify("error", failure);
9908
+ return false;
9909
+ } finally {
9910
+ setBusy((current) => current === id ? void 0 : current);
9911
+ }
9912
+ };
9913
+ const saveEdit = async () => {
9914
+ if (editing === void 0 || editing.text.trim() === "") return;
9915
+ if (await apply(editing.id, {
9916
+ kind: "edit",
9917
+ content: [{
9918
+ type: "text",
9919
+ text: editing.text
9920
+ }]
9921
+ }, t("queueEditFailed"))) setEditing(void 0);
9922
+ };
9923
+ const action = (label, icon, onClick, disabled, hint) => /* @__PURE__ */ jsx(Tooltip, {
9924
+ label: hint ?? label,
9925
+ side: "bottom",
9926
+ delayMs: 400,
9927
+ children: /* @__PURE__ */ jsx("button", {
9928
+ type: "button",
9929
+ className: panelIconButtonClass,
9930
+ "aria-label": label,
9931
+ disabled,
9932
+ onClick,
9933
+ children: icon
9934
+ })
9935
+ });
9936
+ return /* @__PURE__ */ jsxs("div", {
9937
+ style: repositoryBarFrame,
9938
+ "data-claude-queue-dock": "",
9939
+ children: [/* @__PURE__ */ jsx("style", {
9940
+ "data-dsh-claude-queue-styles": true,
9941
+ children: panelIconButtonCss
9942
+ }), /* @__PURE__ */ jsxs("div", {
9943
+ style: queueBar,
9944
+ children: [queue.length > 1 ? /* @__PURE__ */ jsxs("button", {
9945
+ type: "button",
9946
+ style: queueHeader,
9947
+ "aria-controls": listId,
9948
+ "aria-expanded": expanded,
9949
+ disabled: interacting,
9950
+ onClick: () => {
9951
+ setCollapsed((value) => !value);
9952
+ },
9953
+ children: [
9954
+ /* @__PURE__ */ jsx("span", {
9955
+ style: queueLead,
9956
+ "aria-hidden": "true",
9957
+ children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
9958
+ }),
9959
+ /* @__PURE__ */ jsx("span", {
9960
+ style: queueCount,
9961
+ children: t("queueCount", { n: queue.length })
9962
+ }),
9963
+ /* @__PURE__ */ jsx("span", {
9964
+ style: queueLead,
9965
+ "aria-hidden": "true",
9966
+ children: expanded ? /* @__PURE__ */ jsx(IconChevronDownOutline14, {}) : /* @__PURE__ */ jsx(IconChevronUpOutline14, {})
9967
+ })
9968
+ ]
9969
+ }) : null, /* @__PURE__ */ jsx("ul", {
9970
+ id: listId,
9971
+ style: queueList,
9972
+ hidden: !listVisible,
9973
+ children: listVisible ? queue.map((row, index) => /* @__PURE__ */ jsxs("li", {
9974
+ style: {
9975
+ ...queueRow,
9976
+ ...index > 0 ? queueRowDivider : {}
9977
+ },
9978
+ children: [
9979
+ queue.length === 1 ? /* @__PURE__ */ jsx("span", {
9980
+ style: queueLead,
9981
+ "aria-hidden": "true",
9982
+ children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
9983
+ }) : null,
9984
+ editing?.id === row.id ? /* @__PURE__ */ jsx("input", {
9985
+ autoFocus: true,
9986
+ style: queueEditor,
9987
+ "aria-label": t("queueEdit"),
9988
+ value: editing.text,
9989
+ onChange: (event) => {
9990
+ setEditing({
9991
+ id: row.id,
9992
+ text: event.currentTarget.value
9993
+ });
9994
+ },
9995
+ onKeyDown: (event) => {
9996
+ if (event.key === "Escape") setEditing(void 0);
9997
+ else if (event.key === "Enter" && !event.nativeEvent.isComposing) {
9998
+ event.preventDefault();
9999
+ saveEdit();
10000
+ }
10001
+ }
10002
+ }) : /* @__PURE__ */ jsx("span", {
10003
+ style: queuePreview,
10004
+ children: row.preview
10005
+ }),
10006
+ mutable ? /* @__PURE__ */ jsx("span", {
10007
+ style: queueActions,
10008
+ children: editing?.id === row.id ? /* @__PURE__ */ jsxs(Fragment$1, { children: [action(t("queueSave"), /* @__PURE__ */ jsx(IconCheckOutline16, { size: 14 }), () => {
10009
+ saveEdit();
10010
+ }, busy !== void 0 || editing.text.trim() === ""), action(t("queueCancelEdit"), /* @__PURE__ */ jsx(IconCloseOutline16, { size: 14 }), () => {
10011
+ setEditing(void 0);
10012
+ }, busy !== void 0)] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
10013
+ action(t("queueEdit"), /* @__PURE__ */ jsx(IconEditOutline16, { size: 14 }), () => {
10014
+ if (row.text !== null) setEditing({
10015
+ id: row.id,
10016
+ text: row.text
10017
+ });
10018
+ }, busy !== void 0 || row.text === null, row.text === null ? t("queueEditUnsupported") : void 0),
10019
+ action(t("queueRemove"), /* @__PURE__ */ jsx(IconTrashOutline16, { size: 14 }), () => {
10020
+ apply(row.id, { kind: "remove" }, t("queueRemoveFailed"));
10021
+ }, busy !== void 0),
10022
+ action(t("queueSteer"), /* @__PURE__ */ jsx(IconSendOutline14, {}), () => {
10023
+ apply(row.id, { kind: "steer" }, t("queueSteerFailed"));
10024
+ }, busy !== void 0 || !running, running ? void 0 : t("queueSteerUnavailable"))
10025
+ ] })
10026
+ }) : null
10027
+ ]
10028
+ }, row.id)) : null
10029
+ })]
10030
+ })]
10031
+ });
10032
+ }
10033
+ //#endregion
9596
10034
  //#region node_modules/.pnpm/dompurify@3.4.14/node_modules/dompurify/dist/purify.es.mjs
9597
10035
  /*! @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 */
9598
10036
  function _arrayLikeToArray(r, a) {
@@ -14644,12 +15082,50 @@ window.__ModuleLoader__.load({
14644
15082
  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" })
14645
15083
  });
14646
15084
  }
14647
- function ClaudeDiffPanel({ useClaudeProjection, t, sessionId, maximized, closeDetails, toggleMaximized, submitPrompt }) {
15085
+ function hasChanges(repository) {
15086
+ return (repository?.diff?.additions ?? 0) > 0 || (repository?.diff?.deletions ?? 0) > 0;
15087
+ }
15088
+ /** The checkouts a session can show: its own first, then the ones it wrote into. */
15089
+ function panelRepositories(projection) {
15090
+ return [projection.repository, ...projection.repositories ?? []].filter((item) => item !== void 0);
15091
+ }
15092
+ function ClaudeDiffPanel({ useClaudeProjection, t, sessionId, maximized, closeDetails, toggleMaximized, submitPrompt, initialRoot }) {
14648
15093
  const projection = useClaudeProjection((value) => value);
14649
- const repository = projection.repository;
15094
+ const repositories = useMemo(() => panelRepositories(projection), [projection]);
15095
+ const [selectedRoot, setSelectedRoot] = useState(() => initialRoot ?? (hasChanges(projection.repository) ? void 0 : repositories.find(hasChanges)?.root));
15096
+ const repository = repositories.find((item) => item.root === selectedRoot) ?? projection.repository;
15097
+ const root = repository === projection.repository ? void 0 : repository?.root;
14650
15098
  const diff = repository?.diff;
14651
15099
  const files = useMemo(() => parseUnifiedDiff(diff?.patch ?? ""), [diff?.patch]);
14652
15100
  const [menuOpen, setMenuOpen] = useState(false);
15101
+ const [repositoryMenuOpen, setRepositoryMenuOpen] = useState(false);
15102
+ const repositoryItems = useMemo(() => repositories.map((item) => ({
15103
+ id: item.root ?? item.cwd,
15104
+ label: /* @__PURE__ */ jsxs("span", {
15105
+ style: diffRepositoryRow,
15106
+ children: [
15107
+ /* @__PURE__ */ jsx("span", {
15108
+ style: diffRepositoryRowName,
15109
+ children: repositoryLabel(item)
15110
+ }),
15111
+ " ",
15112
+ /* @__PURE__ */ jsxs("span", {
15113
+ style: diffRepositoryRowCounts,
15114
+ children: [
15115
+ /* @__PURE__ */ jsxs("span", {
15116
+ style: diffAdd,
15117
+ children: ["+", item.diff?.additions ?? 0]
15118
+ }),
15119
+ " ",
15120
+ /* @__PURE__ */ jsxs("span", {
15121
+ style: diffDelete,
15122
+ children: ["−", item.diff?.deletions ?? 0]
15123
+ })
15124
+ ]
15125
+ })
15126
+ ]
15127
+ })
15128
+ })), [repositories]);
14653
15129
  const { toast, report } = useActionToast();
14654
15130
  const [dialog, setDialog] = useState();
14655
15131
  const [message, setMessage] = useState("");
@@ -14672,14 +15148,18 @@ window.__ModuleLoader__.load({
14672
15148
  setGhThreads([]);
14673
15149
  if (pullNumber === void 0) return;
14674
15150
  const controller = new AbortController();
14675
- loadPullRequestThreads(sessionId, pullNumber, controller.signal).then((threads) => {
15151
+ loadPullRequestThreads(sessionId, pullNumber, controller.signal, root).then((threads) => {
14676
15152
  setGhThreads(threads);
14677
15153
  setThreadsLoadedAt(Date.now());
14678
15154
  }, () => void 0);
14679
15155
  return () => {
14680
15156
  controller.abort();
14681
15157
  };
14682
- }, [pullNumber, sessionId]);
15158
+ }, [
15159
+ pullNumber,
15160
+ root,
15161
+ sessionId
15162
+ ]);
14683
15163
  const openThreadCount = ghThreads.filter((thread) => !thread.resolved).length;
14684
15164
  const bodyRef = useRef(null);
14685
15165
  const [targetIndex, setTargetIndex] = useState(0);
@@ -14690,25 +15170,37 @@ window.__ModuleLoader__.load({
14690
15170
  const setFileOpen = useCallback((path, open) => {
14691
15171
  setOpenFiles((current) => new Map(current).set(path, open));
14692
15172
  }, []);
14693
- const suggestMention = useCallback(async (query) => pullNumber === void 0 ? [] : loadMentionableUsers(sessionId, pullNumber, query).catch(() => []), [pullNumber, sessionId]);
15173
+ const suggestMention = useCallback(async (query) => pullNumber === void 0 ? [] : loadMentionableUsers(sessionId, pullNumber, query, void 0, root).catch(() => []), [
15174
+ pullNumber,
15175
+ root,
15176
+ sessionId
15177
+ ]);
14694
15178
  const replyToThread = useCallback(async (thread, body) => {
14695
15179
  if (pullNumber === void 0) return;
14696
15180
  const anchor = thread.comments[0];
14697
15181
  if (anchor === void 0) return;
14698
- const posted = await replyToReviewThread(sessionId, pullNumber, anchor.id, body);
15182
+ const posted = await replyToReviewThread(sessionId, pullNumber, anchor.id, body, root);
14699
15183
  setGhThreads((list) => list.map((item) => item.id === thread.id ? {
14700
15184
  ...item,
14701
15185
  comments: [...item.comments, posted]
14702
15186
  } : item));
14703
- }, [pullNumber, sessionId]);
15187
+ }, [
15188
+ pullNumber,
15189
+ root,
15190
+ sessionId
15191
+ ]);
14704
15192
  const changeThreadResolved = useCallback(async (thread, resolved) => {
14705
15193
  if (pullNumber === void 0) return;
14706
- const state = await setReviewThreadResolved(sessionId, pullNumber, thread.id, resolved);
15194
+ const state = await setReviewThreadResolved(sessionId, pullNumber, thread.id, resolved, root);
14707
15195
  setGhThreads((list) => list.map((item) => item.id === thread.id ? {
14708
15196
  ...item,
14709
15197
  resolved: state
14710
15198
  } : item));
14711
- }, [pullNumber, sessionId]);
15199
+ }, [
15200
+ pullNumber,
15201
+ root,
15202
+ sessionId
15203
+ ]);
14712
15204
  const diffViewportObserver = useRef();
14713
15205
  const diffBodyRef = useCallback((element) => {
14714
15206
  diffViewportObserver.current?.disconnect();
@@ -14743,7 +15235,7 @@ window.__ModuleLoader__.load({
14743
15235
  setDraft(true);
14744
15236
  const controller = new AbortController();
14745
15237
  actionController.current = controller;
14746
- loadRepositoryActionPreview(sessionId, controller.signal).then(async (preview) => {
15238
+ loadRepositoryActionPreview(sessionId, controller.signal, root).then(async (preview) => {
14747
15239
  setIncludeUnstaged(preview.hasUnstaged || preview.hasUntracked);
14748
15240
  if (action === "push") {
14749
15241
  setDialog({
@@ -14760,7 +15252,7 @@ window.__ModuleLoader__.load({
14760
15252
  loading: true,
14761
15253
  submitting: false
14762
15254
  });
14763
- const generated = await generateCommitMessage(sessionId, preview.fingerprint, controller.signal);
15255
+ const generated = await generateCommitMessage(sessionId, preview.fingerprint, controller.signal, root);
14764
15256
  setMessage((current) => current.trim() === "" ? generated : current);
14765
15257
  setPrTitle((current) => current.trim() === "" ? generated : current);
14766
15258
  setPrBody((current) => current.trim() === "" ? `Summary: ${generated}\n\nChanges:\n- ${generated}` : current);
@@ -14776,7 +15268,11 @@ window.__ModuleLoader__.load({
14776
15268
  error: error instanceof Error ? error.message : t("diffActionFailed")
14777
15269
  });
14778
15270
  });
14779
- }, [sessionId, t]);
15271
+ }, [
15272
+ root,
15273
+ sessionId,
15274
+ t
15275
+ ]);
14780
15276
  const confirm = useCallback(async () => {
14781
15277
  if (dialog?.preview === void 0 || dialog.action !== "push" && message.trim().length === 0) return;
14782
15278
  const { error: _error, ...pending } = dialog;
@@ -14796,7 +15292,7 @@ window.__ModuleLoader__.load({
14796
15292
  ...baseBranch.trim() === "" ? {} : { baseBranch },
14797
15293
  draft
14798
15294
  } : {}
14799
- });
15295
+ }, root);
14800
15296
  report(result.pullRequestUrl === void 0 ? t(dialog.action === "push" ? "diffPushCompleted" : "diffCommitCompleted", { commit: result.commit.slice(0, 8) }) : t("diffPrCompleted"));
14801
15297
  setDialog(void 0);
14802
15298
  } catch (error) {
@@ -14817,6 +15313,7 @@ window.__ModuleLoader__.load({
14817
15313
  prBody,
14818
15314
  prTitle,
14819
15315
  report,
15316
+ root,
14820
15317
  sessionId,
14821
15318
  t
14822
15319
  ]);
@@ -15015,7 +15512,8 @@ window.__ModuleLoader__.load({
15015
15512
  diffModalCss,
15016
15513
  panelIconButtonCss,
15017
15514
  diffCommentCss,
15018
- diffCommentMarkdownCss
15515
+ diffCommentMarkdownCss,
15516
+ diffRepositoryCss
15019
15517
  ]
15020
15518
  }),
15021
15519
  /* @__PURE__ */ jsxs("div", {
@@ -15029,20 +15527,34 @@ window.__ModuleLoader__.load({
15029
15527
  style: diffHeader,
15030
15528
  children: [/* @__PURE__ */ jsxs("div", {
15031
15529
  style: diffHeaderTitle,
15032
- children: [
15033
- /* @__PURE__ */ jsx("span", {
15034
- style: diffHeaderBranch,
15035
- children: branch
15036
- }),
15037
- /* @__PURE__ */ jsx("span", {
15038
- "aria-hidden": "true",
15039
- children: "›"
15040
- }),
15041
- /* @__PURE__ */ jsx("span", {
15042
- style: diffHeaderLabel,
15043
- children: t("diffWorkingTree")
15530
+ children: [repositories.length < 2 ? null : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Menu, {
15531
+ open: repositoryMenuOpen,
15532
+ items: repositoryItems,
15533
+ selectedId: repository.root ?? repository.cwd,
15534
+ onSelect: (id) => {
15535
+ setSelectedRoot(id);
15536
+ setRepositoryMenuOpen(false);
15537
+ },
15538
+ onClose: () => setRepositoryMenuOpen(false),
15539
+ portal: true,
15540
+ compact: true,
15541
+ anchor: /* @__PURE__ */ jsxs("button", {
15542
+ type: "button",
15543
+ className: diffRepositoryTriggerClass,
15544
+ "aria-label": t("diffRepository"),
15545
+ "aria-haspopup": "menu",
15546
+ "aria-expanded": repositoryMenuOpen,
15547
+ onClick: () => setRepositoryMenuOpen((value) => !value),
15548
+ children: [/* @__PURE__ */ jsx("span", { children: repositoryLabel(repository) }), /* @__PURE__ */ jsx(IconChevronDownOutline14, {})]
15044
15549
  })
15045
- ]
15550
+ }), /* @__PURE__ */ jsx("span", {
15551
+ "aria-hidden": "true",
15552
+ children: "›"
15553
+ })] }), /* @__PURE__ */ jsx("span", {
15554
+ style: diffHeaderBranch,
15555
+ title: branch,
15556
+ children: branch
15557
+ })]
15046
15558
  }), /* @__PURE__ */ jsxs("div", {
15047
15559
  style: diffHeaderActions,
15048
15560
  children: [
@@ -15182,35 +15694,45 @@ window.__ModuleLoader__.load({
15182
15694
  /* @__PURE__ */ jsxs("div", {
15183
15695
  ref: diffBodyRef,
15184
15696
  style: diffBody,
15185
- children: [diff.truncated ? /* @__PURE__ */ jsx("p", {
15186
- style: diffNotice,
15187
- children: t("diffTruncated")
15188
- }) : null, files.length === 0 ? /* @__PURE__ */ jsx("p", {
15189
- style: diffEmpty,
15190
- children: t("diffEmpty")
15191
- }) : files.map((file, index) => /* @__PURE__ */ jsx(DiffFileSection, {
15192
- file,
15193
- root: repository.root,
15194
- open: fileOpen(file.path, index),
15195
- onOpenChange: (open) => {
15196
- setFileOpen(file.path, open);
15197
- },
15198
- t,
15199
- comments: reviewComments.filter((comment) => comment.path === file.path),
15200
- ghThreads: ghThreads.filter((thread) => thread.path === file.path),
15201
- suggestMention,
15202
- now: threadsLoadedAt,
15203
- activeTargetKey,
15204
- onReplyToThread: replyToThread,
15205
- onThreadResolvedChange: changeThreadResolved,
15206
- onSendThreadToAi: submitPrompt === void 0 ? void 0 : (thread) => {
15207
- submitPrompt(composeCommentsPrompt([thread]));
15208
- },
15209
- editorAnchor: commentEditor !== void 0 && commentEditor.path === file.path ? commentEditor : void 0,
15210
- editorNode: commentEditorNode,
15211
- onOpenEditor: (anchor) => openCommentEditor(file.path, anchor),
15212
- onRemoveComment: removeComment
15213
- }, file.path))]
15697
+ children: [
15698
+ diff.truncated ? /* @__PURE__ */ jsx("p", {
15699
+ style: diffNotice,
15700
+ children: t("diffTruncated")
15701
+ }) : null,
15702
+ diff.elided !== void 0 && diff.elided.length > 0 ? /* @__PURE__ */ jsx("p", {
15703
+ style: diffNotice,
15704
+ children: t("diffElided", {
15705
+ count: diff.elided.length,
15706
+ files: diff.elided.join(", ")
15707
+ })
15708
+ }) : null,
15709
+ files.length === 0 ? /* @__PURE__ */ jsx("p", {
15710
+ style: diffEmpty,
15711
+ children: t("diffEmpty")
15712
+ }) : files.map((file, index) => /* @__PURE__ */ jsx(DiffFileSection, {
15713
+ file,
15714
+ root: repository.root,
15715
+ open: fileOpen(file.path, index),
15716
+ onOpenChange: (open) => {
15717
+ setFileOpen(file.path, open);
15718
+ },
15719
+ t,
15720
+ comments: reviewComments.filter((comment) => comment.path === file.path),
15721
+ ghThreads: ghThreads.filter((thread) => thread.path === file.path),
15722
+ suggestMention,
15723
+ now: threadsLoadedAt,
15724
+ activeTargetKey,
15725
+ onReplyToThread: replyToThread,
15726
+ onThreadResolvedChange: changeThreadResolved,
15727
+ onSendThreadToAi: submitPrompt === void 0 ? void 0 : (thread) => {
15728
+ submitPrompt(composeCommentsPrompt([thread]));
15729
+ },
15730
+ editorAnchor: commentEditor !== void 0 && commentEditor.path === file.path ? commentEditor : void 0,
15731
+ editorNode: commentEditorNode,
15732
+ onOpenEditor: (anchor) => openCommentEditor(file.path, anchor),
15733
+ onRemoveComment: removeComment
15734
+ }, file.path))
15735
+ ]
15214
15736
  })
15215
15737
  ]
15216
15738
  }),
@@ -15366,247 +15888,202 @@ window.__ModuleLoader__.load({
15366
15888
  ] });
15367
15889
  }
15368
15890
  //#endregion
15369
- //#region src/client/ClaudePanelOverlay.tsx
15370
- const useClientLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
15371
- function shouldRestoreFromEscape(event, root = document) {
15372
- return event.key === "Escape" && root.querySelector("[role=\"dialog\"][aria-modal=\"true\"]") === null;
15373
- }
15374
- function workspaceBounds(overlay) {
15375
- const frame = overlay.parentElement;
15376
- if (frame === null) return void 0;
15377
- const overlayRect = overlay.getBoundingClientRect();
15378
- const candidates = Array.from(frame.children).filter((element) => element !== overlay).map((element) => element.getBoundingClientRect()).filter((rect) => rect.width > 0 && rect.height > 0);
15379
- const insetCandidates = candidates.filter((rect) => rect.left > overlayRect.left);
15380
- const workspace = (insetCandidates.length > 0 ? insetCandidates : candidates).reduce((largest, rect) => {
15381
- if (largest === void 0 || rect.width * rect.height > largest.width * largest.height) return rect;
15382
- return largest;
15383
- }, void 0);
15384
- if (workspace === void 0) return void 0;
15385
- const left = Math.max(0, workspace.left - overlayRect.left);
15386
- const top = Math.max(0, workspace.top - overlayRect.top);
15387
- return {
15388
- left,
15389
- top,
15390
- width: Math.max(0, Math.min(workspace.width, overlayRect.width - left)),
15391
- height: Math.max(0, Math.min(workspace.height, overlayRect.height - top))
15392
- };
15393
- }
15394
- function observeWorkspaceBounds(overlay, listener) {
15395
- const frame = overlay.parentElement;
15396
- if (frame === null) return () => {};
15397
- const update = () => {
15398
- const next = workspaceBounds(overlay);
15399
- if (next !== void 0) listener(next);
15400
- };
15401
- update();
15402
- const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(update);
15403
- observer?.observe(frame);
15404
- for (const element of Array.from(frame.children)) observer?.observe(element);
15405
- const mutation = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(update);
15406
- mutation?.observe(frame, {
15407
- attributes: true,
15408
- childList: true
15409
- });
15410
- window.addEventListener("resize", update);
15411
- return () => {
15412
- observer?.disconnect();
15413
- mutation?.disconnect();
15414
- window.removeEventListener("resize", update);
15415
- };
15416
- }
15417
- function ClaudePanelOverlay({ children, onRestore }) {
15418
- const ref = useRef(null);
15419
- const [bounds, setBounds] = useState();
15420
- useClientLayoutEffect(() => {
15421
- const overlay = ref.current?.closest("[data-shell-overlay]");
15422
- if (overlay === void 0 || overlay === null) return;
15423
- return observeWorkspaceBounds(overlay, (next) => {
15424
- setBounds((current) => current?.left === next.left && current.top === next.top && current.width === next.width && current.height === next.height ? current : next);
15425
- });
15426
- }, []);
15891
+ //#region src/client/sidebar-tabs.tsx
15892
+ /**
15893
+ * The plugin's panels as tabs of the Host's right sidebar.
15894
+ *
15895
+ * Host 0.1.5 removed the `details` column this package used to register a
15896
+ * single panel into, and replaced it with a tabbed, splittable, floatable right
15897
+ * sidebar (`@deepseek-ai/dsh-client-ui-sidebar-right`). A panel there is a
15898
+ * tab *type*: declared once in `ctx.sidebarRightTabs`, with its body registered
15899
+ * into `sidebar.right.pane.tab` under the type's id, and opened per session
15900
+ * through `ctx.sidebarRight.openTabIn`. Reopening a type that is already open
15901
+ * focuses that tab and re-delivers the navigation params, so a second "show
15902
+ * tasks" press for another turn lands in the same tab.
15903
+ *
15904
+ * The Host owns fullscreen and closing now, so the maximized overlays this
15905
+ * package used to draw are gone: a panel's maximize button toggles the
15906
+ * sidebar's own expanded mode, and its close button closes the tab.
15907
+ */
15908
+ const CLAUDE_TAB_KINDS = {
15909
+ diff: "claude-diff",
15910
+ plan: "claude-plan",
15911
+ tasks: "claude-tasks",
15912
+ overview: "claude-overview"
15913
+ };
15914
+ /** Which tab (if any) each kind currently occupies, per session. */
15915
+ var TabOccupancy = class TabOccupancy {
15916
+ #byKey = /* @__PURE__ */ new Map();
15917
+ #listeners = /* @__PURE__ */ new Map();
15918
+ static key(kind, sessionId) {
15919
+ return `${kind}${sessionId}`;
15920
+ }
15921
+ tabId(kind, sessionId) {
15922
+ return this.#byKey.get(TabOccupancy.key(kind, sessionId));
15923
+ }
15924
+ note(kind, sessionId, tabId) {
15925
+ const key = TabOccupancy.key(kind, sessionId);
15926
+ const previous = this.#byKey.get(key);
15927
+ if (tabId === void 0) this.#byKey.delete(key);
15928
+ else this.#byKey.set(key, tabId);
15929
+ if (previous === tabId) return;
15930
+ for (const listener of this.#listeners.get(key) ?? []) listener();
15931
+ }
15932
+ source(kind, sessionId) {
15933
+ const key = TabOccupancy.key(kind, sessionId);
15934
+ return {
15935
+ subscribe: (listener) => {
15936
+ let set = this.#listeners.get(key);
15937
+ if (set === void 0) {
15938
+ set = /* @__PURE__ */ new Set();
15939
+ this.#listeners.set(key, set);
15940
+ }
15941
+ set.add(listener);
15942
+ return () => {
15943
+ set.delete(listener);
15944
+ };
15945
+ },
15946
+ getSnapshot: () => this.#byKey.has(key)
15947
+ };
15948
+ }
15949
+ };
15950
+ /** Bind a panel body to the tab it lives in: report occupancy, and map the
15951
+ * tab surface onto the panel's close / maximize props. */
15952
+ function useClaudeTab(useTabInfo, noteTab) {
15953
+ const info = useTabInfo();
15954
+ const tabId = info.tab.id;
15427
15955
  useEffect(() => {
15428
- const escape = (event) => {
15429
- if (shouldRestoreFromEscape(event)) onRestore();
15956
+ noteTab(tabId);
15957
+ return () => {
15958
+ noteTab(void 0);
15430
15959
  };
15431
- window.addEventListener("keydown", escape);
15432
- return () => window.removeEventListener("keydown", escape);
15433
- }, [onRestore]);
15434
- return /* @__PURE__ */ jsx("div", {
15435
- ref,
15436
- "data-dsh-claude-panel-overlay": true,
15437
- style: {
15438
- position: "absolute",
15439
- left: bounds?.left ?? 0,
15440
- top: bounds?.top ?? 0,
15441
- width: bounds?.width ?? 0,
15442
- height: bounds?.height ?? 0,
15443
- visibility: bounds === void 0 ? "hidden" : "visible",
15444
- padding: 8,
15445
- boxSizing: "border-box",
15446
- background: "var(--dsw-alias-bg-base)",
15447
- pointerEvents: bounds === void 0 ? "none" : "auto"
15448
- },
15449
- children
15960
+ }, [noteTab, tabId]);
15961
+ const actions = info.tab.actions;
15962
+ return {
15963
+ closeDetails: useCallback(() => {
15964
+ actions.close();
15965
+ }, [actions]),
15966
+ maximized: info.sidebar.fullscreen,
15967
+ params: info.tab.navigation.params ?? {}
15968
+ };
15969
+ }
15970
+ function ClaudeDiffTab({ useTabInfo, noteTab, toggleMaximized, ...panel }) {
15971
+ const { closeDetails, maximized, params } = useClaudeTab(useTabInfo, noteTab);
15972
+ return /* @__PURE__ */ jsx(ClaudeDiffPanel, {
15973
+ ...panel,
15974
+ closeDetails,
15975
+ maximized,
15976
+ toggleMaximized,
15977
+ ...params.initialRoot === void 0 ? {} : { initialRoot: params.initialRoot }
15450
15978
  });
15451
15979
  }
15452
- //#endregion
15453
- //#region src/client/ClaudeQueueDock.tsx
15454
- /** Replaces the Host queue strip (same slot cell, lower priority) with one
15455
- * styled like the repository status bar. Behaviour mirrors the Host strip:
15456
- * one row inline, several rows behind a collapsible count, edit/remove/steer. */
15457
- function ClaudeQueueDock({ useSession, t, updateQueue, notify }) {
15458
- const inbox = useSession((session) => session.queue);
15459
- const queue = useMemo(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
15460
- const running = useSession((session) => session.running);
15461
- const mutable = useSession((session) => session.subagent === null);
15462
- const [editing, setEditing] = useState();
15463
- const [busy, setBusy] = useState();
15464
- const [collapsed, setCollapsed] = useState(true);
15465
- const listId = useId();
15466
- useEffect(() => {
15467
- if (queue.length === 0 && !collapsed) setCollapsed(true);
15468
- if (editing !== void 0 && (!mutable || !queue.some((row) => row.id === editing.id))) setEditing(void 0);
15469
- }, [
15470
- collapsed,
15471
- editing,
15472
- mutable,
15473
- queue
15474
- ]);
15475
- if (queue.length === 0) return null;
15476
- const interacting = mutable && (editing !== void 0 || busy !== void 0);
15477
- const expanded = !collapsed || interacting;
15478
- const listVisible = queue.length === 1 || expanded;
15479
- const apply = async (id, action, failure) => {
15480
- setBusy(id);
15481
- try {
15482
- await updateQueue(id, action);
15483
- return true;
15484
- } catch {
15485
- notify("error", failure);
15486
- return false;
15487
- } finally {
15488
- setBusy((current) => current === id ? void 0 : current);
15489
- }
15980
+ function ClaudePlanTab({ useTabInfo, noteTab, toggleMaximized, ...panel }) {
15981
+ const { closeDetails, maximized } = useClaudeTab(useTabInfo, noteTab);
15982
+ return /* @__PURE__ */ jsx(ClaudePlanPanel, {
15983
+ ...panel,
15984
+ closeDetails,
15985
+ maximized,
15986
+ toggleMaximized
15987
+ });
15988
+ }
15989
+ function ClaudeTasksTab({ useTabInfo, noteTab, ...panel }) {
15990
+ const { closeDetails, params } = useClaudeTab(useTabInfo, noteTab);
15991
+ return /* @__PURE__ */ jsx(ClaudeTasksPanel, {
15992
+ ...panel,
15993
+ closeDetails,
15994
+ turn: params.turn ?? 0
15995
+ });
15996
+ }
15997
+ function ClaudeOverviewTab({ useTabInfo, noteTab, face }) {
15998
+ const { closeDetails } = useClaudeTab(useTabInfo, noteTab);
15999
+ if (face === void 0) return null;
16000
+ return /* @__PURE__ */ jsx(ClaudePullRequestsPanel, {
16001
+ ...face,
16002
+ closeDetails
16003
+ });
16004
+ }
16005
+ /** Declare the four tab types and their bodies; returns the per-session
16006
+ * open / close / toggle face the rest of the client drives them through. */
16007
+ function registerClaudeSidebarTabs(ctx, options) {
16008
+ const { t, namespace } = options;
16009
+ const occupancy = new TabOccupancy();
16010
+ const toggleMaximized = () => {
16011
+ ctx.sidebarRight.toggleExpanded();
15490
16012
  };
15491
- const saveEdit = async () => {
15492
- if (editing === void 0 || editing.text.trim() === "") return;
15493
- if (await apply(editing.id, {
15494
- kind: "edit",
15495
- content: [{
15496
- type: "text",
15497
- text: editing.text
15498
- }]
15499
- }, t("queueEditFailed"))) setEditing(void 0);
16013
+ const faceFor = (kind, sessionId) => ({
16014
+ noteTab: (tabId) => {
16015
+ occupancy.note(kind, sessionId, tabId);
16016
+ },
16017
+ toggleMaximized
16018
+ });
16019
+ const titles = {
16020
+ [CLAUDE_TAB_KINDS.diff]: () => t("diffTabTitle"),
16021
+ [CLAUDE_TAB_KINDS.plan]: () => t("planPanelTitle"),
16022
+ [CLAUDE_TAB_KINDS.tasks]: () => t("tasksPanel"),
16023
+ [CLAUDE_TAB_KINDS.overview]: () => t("overviewTitle")
15500
16024
  };
15501
- const action = (label, icon, onClick, disabled, hint) => /* @__PURE__ */ jsx(Tooltip, {
15502
- label: hint ?? label,
15503
- side: "bottom",
15504
- delayMs: 400,
15505
- children: /* @__PURE__ */ jsx("button", {
15506
- type: "button",
15507
- className: panelIconButtonClass,
15508
- "aria-label": label,
15509
- disabled,
15510
- onClick,
15511
- children: icon
16025
+ for (const kind of Object.values(CLAUDE_TAB_KINDS)) ctx.effect(() => ctx.sidebarRightTabs.register({
16026
+ id: kind,
16027
+ kind,
16028
+ priority: "extension",
16029
+ title: titles[kind]
16030
+ }), `dsh-claude: ${kind} tab type`);
16031
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16032
+ name: "sidebar.right.pane.tab",
16033
+ key: CLAUDE_TAB_KINDS.diff,
16034
+ locale: namespace,
16035
+ inject: (sessionId) => ({
16036
+ t,
16037
+ sessionId,
16038
+ ...faceFor(CLAUDE_TAB_KINDS.diff, sessionId),
16039
+ ...options.diffFace(sessionId)
15512
16040
  })
15513
- });
15514
- return /* @__PURE__ */ jsxs("div", {
15515
- style: repositoryBarFrame,
15516
- "data-claude-queue-dock": "",
15517
- children: [/* @__PURE__ */ jsx("style", {
15518
- "data-dsh-claude-queue-styles": true,
15519
- children: panelIconButtonCss
15520
- }), /* @__PURE__ */ jsxs("div", {
15521
- style: queueBar,
15522
- children: [queue.length > 1 ? /* @__PURE__ */ jsxs("button", {
15523
- type: "button",
15524
- style: queueHeader,
15525
- "aria-controls": listId,
15526
- "aria-expanded": expanded,
15527
- disabled: interacting,
15528
- onClick: () => {
15529
- setCollapsed((value) => !value);
15530
- },
15531
- children: [
15532
- /* @__PURE__ */ jsx("span", {
15533
- style: queueLead,
15534
- "aria-hidden": "true",
15535
- children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
15536
- }),
15537
- /* @__PURE__ */ jsx("span", {
15538
- style: queueCount,
15539
- children: t("queueCount", { n: queue.length })
15540
- }),
15541
- /* @__PURE__ */ jsx("span", {
15542
- style: queueLead,
15543
- "aria-hidden": "true",
15544
- children: expanded ? /* @__PURE__ */ jsx(IconChevronDownOutline14, {}) : /* @__PURE__ */ jsx(IconChevronUpOutline14, {})
15545
- })
15546
- ]
15547
- }) : null, /* @__PURE__ */ jsx("ul", {
15548
- id: listId,
15549
- style: queueList,
15550
- hidden: !listVisible,
15551
- children: listVisible ? queue.map((row, index) => /* @__PURE__ */ jsxs("li", {
15552
- style: {
15553
- ...queueRow,
15554
- ...index > 0 ? queueRowDivider : {}
15555
- },
15556
- children: [
15557
- queue.length === 1 ? /* @__PURE__ */ jsx("span", {
15558
- style: queueLead,
15559
- "aria-hidden": "true",
15560
- children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
15561
- }) : null,
15562
- editing?.id === row.id ? /* @__PURE__ */ jsx("input", {
15563
- autoFocus: true,
15564
- style: queueEditor,
15565
- "aria-label": t("queueEdit"),
15566
- value: editing.text,
15567
- onChange: (event) => {
15568
- setEditing({
15569
- id: row.id,
15570
- text: event.currentTarget.value
15571
- });
15572
- },
15573
- onKeyDown: (event) => {
15574
- if (event.key === "Escape") setEditing(void 0);
15575
- else if (event.key === "Enter" && !event.nativeEvent.isComposing) {
15576
- event.preventDefault();
15577
- saveEdit();
15578
- }
15579
- }
15580
- }) : /* @__PURE__ */ jsx("span", {
15581
- style: queuePreview,
15582
- children: row.preview
15583
- }),
15584
- mutable ? /* @__PURE__ */ jsx("span", {
15585
- style: queueActions,
15586
- children: editing?.id === row.id ? /* @__PURE__ */ jsxs(Fragment$1, { children: [action(t("queueSave"), /* @__PURE__ */ jsx(IconCheckOutline16, { size: 14 }), () => {
15587
- saveEdit();
15588
- }, busy !== void 0 || editing.text.trim() === ""), action(t("queueCancelEdit"), /* @__PURE__ */ jsx(IconCloseOutline16, { size: 14 }), () => {
15589
- setEditing(void 0);
15590
- }, busy !== void 0)] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
15591
- action(t("queueEdit"), /* @__PURE__ */ jsx(IconEditOutline16, { size: 14 }), () => {
15592
- if (row.text !== null) setEditing({
15593
- id: row.id,
15594
- text: row.text
15595
- });
15596
- }, busy !== void 0 || row.text === null, row.text === null ? t("queueEditUnsupported") : void 0),
15597
- action(t("queueRemove"), /* @__PURE__ */ jsx(IconTrashOutline16, { size: 14 }), () => {
15598
- apply(row.id, { kind: "remove" }, t("queueRemoveFailed"));
15599
- }, busy !== void 0),
15600
- action(t("queueSteer"), /* @__PURE__ */ jsx(IconSendOutline14, {}), () => {
15601
- apply(row.id, { kind: "steer" }, t("queueSteerFailed"));
15602
- }, busy !== void 0 || !running, running ? void 0 : t("queueSteerUnavailable"))
15603
- ] })
15604
- }) : null
15605
- ]
15606
- }, row.id)) : null
15607
- })]
15608
- })]
15609
- });
16041
+ }, ClaudeDiffTab));
16042
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16043
+ name: "sidebar.right.pane.tab",
16044
+ key: CLAUDE_TAB_KINDS.plan,
16045
+ locale: namespace,
16046
+ inject: (sessionId) => ({
16047
+ t,
16048
+ sessionId,
16049
+ ...faceFor(CLAUDE_TAB_KINDS.plan, sessionId)
16050
+ })
16051
+ }, ClaudePlanTab));
16052
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16053
+ name: "sidebar.right.pane.tab",
16054
+ key: CLAUDE_TAB_KINDS.tasks,
16055
+ locale: namespace,
16056
+ inject: (sessionId) => ({
16057
+ t,
16058
+ ...faceFor(CLAUDE_TAB_KINDS.tasks, sessionId)
16059
+ })
16060
+ }, ClaudeTasksTab));
16061
+ ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
16062
+ name: "sidebar.right.pane.tab",
16063
+ key: CLAUDE_TAB_KINDS.overview,
16064
+ locale: namespace,
16065
+ inject: (sessionId) => ({
16066
+ ...faceFor(CLAUDE_TAB_KINDS.overview, sessionId),
16067
+ face: options.overviewFace(sessionId)
16068
+ })
16069
+ }, ClaudeOverviewTab));
16070
+ const open = (kind, sessionId, params) => {
16071
+ ctx.sidebarRight.openTabIn(sessionId, kind, { params });
16072
+ };
16073
+ const close = (kind, sessionId) => {
16074
+ const tabId = occupancy.tabId(kind, sessionId);
16075
+ if (tabId !== void 0) ctx.sidebarRight.closeIn(sessionId, tabId);
16076
+ };
16077
+ return {
16078
+ open,
16079
+ close,
16080
+ toggle: (kind, sessionId, params) => {
16081
+ if (occupancy.tabId(kind, sessionId) === void 0) open(kind, sessionId, params);
16082
+ else close(kind, sessionId);
16083
+ },
16084
+ isOpen: (kind, sessionId) => occupancy.tabId(kind, sessionId) !== void 0,
16085
+ sourceFor: (kind, sessionId) => occupancy.source(kind, sessionId)
16086
+ };
15610
16087
  }
15611
16088
  //#endregion
15612
16089
  //#region src/client/ask-api.ts
@@ -17557,10 +18034,10 @@ window.__ModuleLoader__.load({
17557
18034
  ".dsh-claude-header-diff-add{color:var(--dsw-alias-state-success-primary)}",
17558
18035
  ".dsh-claude-header-diff-del{color:var(--dsw-alias-state-error-primary)}"
17559
18036
  ].join("");
17560
- let cssInjected$2 = false;
17561
- function ensureCss$2() {
17562
- if (cssInjected$2 || typeof document === "undefined") return;
17563
- cssInjected$2 = true;
18037
+ let cssInjected$1 = false;
18038
+ function ensureCss$1() {
18039
+ if (cssInjected$1 || typeof document === "undefined") return;
18040
+ cssInjected$1 = true;
17564
18041
  const element = document.createElement("style");
17565
18042
  element.dataset.dshClaudeHeaderDiff = "";
17566
18043
  element.textContent = ACTION_CSS;
@@ -17589,13 +18066,16 @@ window.__ModuleLoader__.load({
17589
18066
  })
17590
18067
  });
17591
18068
  }
18069
+ function diffTotal(projection, key) {
18070
+ return [projection.repository, ...projection.repositories ?? []].reduce((sum, repository) => sum + (repository?.diff?.[key] ?? 0), 0);
18071
+ }
17592
18072
  function ClaudeDiffHeaderAction({ t, sessionId, toggleDiff, diffOpen, useClaudeProjection }) {
17593
18073
  const owned = useClaudeProjection((projection) => projection.owned);
17594
- const additions = useClaudeProjection((projection) => projection.repository?.diff?.additions ?? 0);
17595
- const deletions = useClaudeProjection((projection) => projection.repository?.diff?.deletions ?? 0);
18074
+ const additions = useClaudeProjection((projection) => diffTotal(projection, "additions"));
18075
+ const deletions = useClaudeProjection((projection) => diffTotal(projection, "deletions"));
17596
18076
  const open = useSyncExternalStore(diffOpen.subscribe, diffOpen.getSnapshot, diffOpen.getSnapshot);
17597
18077
  if (!owned) return null;
17598
- ensureCss$2();
18078
+ ensureCss$1();
17599
18079
  const hasCounts = additions > 0 || deletions > 0;
17600
18080
  const label = t(open ? "diffClose" : "diffOpen");
17601
18081
  return /* @__PURE__ */ jsx(Tooltip, {
@@ -17621,168 +18101,6 @@ window.__ModuleLoader__.load({
17621
18101
  });
17622
18102
  }
17623
18103
  //#endregion
17624
- //#region src/client/editor-open-api.ts
17625
- /** Launch the session's project in a desktop editor on the host machine. */
17626
- async function openProjectInEditor(sessionId, editor) {
17627
- await pluginWrite(CLAUDE_EDITOR_OPEN_PATH, "fast", void 0, { query: {
17628
- sessionId,
17629
- editor
17630
- } });
17631
- }
17632
- //#endregion
17633
- //#region src/client/ClaudeSessionMenu.tsx
17634
- /** Trigger matches the neighbouring diff action; the cards reproduce the
17635
- * primitives' menu surface (r12, inverted hairline, shadow-lv3, 4px inset).
17636
- *
17637
- * Hand-rolled rather than the `Menu` primitive because this menu needs its
17638
- * submenu on the LEFT: the trigger is the header's rightmost control, and the
17639
- * primitive's nested card is pinned to `left: 100%`, which would open the
17640
- * editor list off the right edge of the window. */
17641
- const MENU_CSS = [
17642
- ".dsh-claude-header-menu-root{position:relative;display:inline-flex}",
17643
- ".dsh-claude-header-menu{flex:none;display:inline-flex;align-items:center;justify-content:center;",
17644
- "width:32px;height:32px;padding:0;border:0;border-radius:9px;background:transparent;",
17645
- "color:var(--dsw-alias-label-secondary);cursor:pointer;transition:background .12s ease,color .12s ease}",
17646
- ".dsh-claude-header-menu:hover,.dsh-claude-header-menu:focus-visible,.dsh-claude-header-menu[aria-expanded=\"true\"]{",
17647
- "background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
17648
- ".dsh-claude-header-menu:active{background:var(--dsw-alias-interactive-bg-active)}",
17649
- ".dsh-claude-header-menu:focus-visible{outline:none}",
17650
- ".dsh-claude-header-menu>svg{display:block;transform:rotate(90deg)}",
17651
- ".dsh-claude-header-menu-card,.dsh-claude-header-menu-sub{box-sizing:border-box;position:absolute;",
17652
- "display:flex;flex-direction:column;padding:2px;border:1px solid var(--dsw-alias-border-inverted);",
17653
- "border-radius:7px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3)}",
17654
- ".dsh-claude-header-menu-card{top:calc(100% + 4px);right:0;z-index:100;min-width:164px;max-width:360px}",
17655
- ".dsh-claude-header-menu-sub{top:-2px;right:calc(100% + 10px);z-index:101;min-width:164px}",
17656
- ".dsh-claude-header-menu-sub::after{content:\"\";position:absolute;top:0;bottom:0;right:-10px;width:10px}",
17657
- ".dsh-claude-header-menu-row{position:relative}",
17658
- ".dsh-claude-header-menu-item{display:flex;align-items:center;gap:6px;width:100%;min-height:26px;",
17659
- "padding:3px 7px;border:none;border-radius:5px;background:transparent;cursor:pointer;text-align:left;",
17660
- "font-family:var(--dsw-font-family);font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary)}",
17661
- ".dsh-claude-header-menu-item:hover,.dsh-claude-header-menu-item:focus-visible,",
17662
- ".dsh-claude-header-menu-item[aria-expanded=\"true\"]{background:var(--dsw-alias-interactive-bg-hover)}",
17663
- ".dsh-claude-header-menu-item:focus-visible{outline:none}",
17664
- ".dsh-claude-header-menu-item>span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
17665
- ".dsh-claude-header-menu-item>svg{flex:none;color:var(--dsw-alias-label-tertiary)}",
17666
- ".dsh-claude-header-menu-heading{padding:4px 7px;font-family:var(--dsw-font-family);font-size:11px;",
17667
- "line-height:16px;color:var(--dsw-alias-label-tertiary)}"
17668
- ].join("");
17669
- let cssInjected$1 = false;
17670
- function ensureCss$1() {
17671
- if (cssInjected$1 || typeof document === "undefined") return;
17672
- cssInjected$1 = true;
17673
- for (const stale of document.querySelectorAll("style[data-dsh-claude-header-menu]")) stale.remove();
17674
- const element = document.createElement("style");
17675
- element.dataset.dshClaudeHeaderMenu = "";
17676
- element.textContent = MENU_CSS;
17677
- document.head.appendChild(element);
17678
- }
17679
- const EDITORS = [{
17680
- id: "cursor",
17681
- label: "Cursor"
17682
- }, {
17683
- id: "idea",
17684
- label: "IntelliJ IDEA"
17685
- }];
17686
- /** The open menu, split out so the SSR tests can render it without a DOM to
17687
- * drive the hover that opens the submenu. */
17688
- function ClaudeSessionMenuCard({ openIn, submenuOpen, failure, onSubmenuOpen, onSelect }) {
17689
- return /* @__PURE__ */ jsxs("div", {
17690
- className: "dsh-claude-header-menu-card",
17691
- role: "menu",
17692
- children: [/* @__PURE__ */ jsxs("div", {
17693
- className: "dsh-claude-header-menu-row",
17694
- onMouseEnter: () => onSubmenuOpen(true),
17695
- onMouseLeave: () => onSubmenuOpen(false),
17696
- children: [/* @__PURE__ */ jsxs("button", {
17697
- type: "button",
17698
- role: "menuitem",
17699
- className: "dsh-claude-header-menu-item",
17700
- "aria-haspopup": "menu",
17701
- "aria-expanded": submenuOpen,
17702
- onFocus: () => onSubmenuOpen(true),
17703
- onClick: () => onSubmenuOpen(true),
17704
- children: [/* @__PURE__ */ jsx("span", { children: openIn }), /* @__PURE__ */ jsx(IconChevronRightOutline14, {})]
17705
- }), submenuOpen ? /* @__PURE__ */ jsxs("div", {
17706
- className: "dsh-claude-header-menu-sub",
17707
- role: "menu",
17708
- children: [/* @__PURE__ */ jsx("div", {
17709
- className: "dsh-claude-header-menu-heading",
17710
- children: openIn
17711
- }), EDITORS.map((editor) => /* @__PURE__ */ jsx("button", {
17712
- type: "button",
17713
- role: "menuitem",
17714
- className: "dsh-claude-header-menu-item",
17715
- onClick: () => onSelect(editor.id),
17716
- children: /* @__PURE__ */ jsx("span", { children: editor.label })
17717
- }, editor.id))]
17718
- }) : null]
17719
- }), failure === void 0 ? null : /* @__PURE__ */ jsx("div", {
17720
- className: "dsh-claude-header-menu-heading",
17721
- children: failure
17722
- })]
17723
- });
17724
- }
17725
- function ClaudeSessionMenu({ t, sessionId, useClaudeProjection, openInEditor = openProjectInEditor }) {
17726
- const owned = useClaudeProjection((projection) => projection.owned);
17727
- const root = useRef(null);
17728
- const [open, setOpen] = useState(false);
17729
- const [submenuOpen, setSubmenuOpen] = useState(false);
17730
- const [failure, setFailure] = useState(void 0);
17731
- const close = () => {
17732
- setOpen(false);
17733
- setSubmenuOpen(false);
17734
- setFailure(void 0);
17735
- };
17736
- useDismissOnOutsidePointer(root, open, (next) => {
17737
- if (!next) close();
17738
- });
17739
- useEffect(() => {
17740
- if (!open) return void 0;
17741
- const onKeyDown = (event) => {
17742
- if (event.key === "Escape") close();
17743
- };
17744
- document.addEventListener("keydown", onKeyDown);
17745
- return () => document.removeEventListener("keydown", onKeyDown);
17746
- }, [open]);
17747
- if (!owned) return null;
17748
- ensureCss$1();
17749
- const label = t("sessionMenu");
17750
- const openIn = t("sessionMenuOpenIn");
17751
- const select = (editor) => {
17752
- setFailure(void 0);
17753
- openInEditor(sessionId, editor).then(() => close(), (error) => setFailure(t("sessionMenuOpenFailed", { message: error instanceof Error ? error.message : String(error) })));
17754
- };
17755
- return /* @__PURE__ */ jsxs("span", {
17756
- className: "dsh-claude-header-menu-root",
17757
- ref: root,
17758
- children: [/* @__PURE__ */ jsx(Tooltip, {
17759
- label,
17760
- side: "bottom",
17761
- delayMs: 250,
17762
- disabled: open,
17763
- children: /* @__PURE__ */ jsx("button", {
17764
- type: "button",
17765
- className: "dsh-claude-header-menu",
17766
- "aria-label": label,
17767
- "aria-haspopup": "menu",
17768
- "aria-expanded": open,
17769
- "data-session": sessionId,
17770
- onClick: () => {
17771
- if (open) close();
17772
- else setOpen(true);
17773
- },
17774
- children: /* @__PURE__ */ jsx(IconEllipsisOutline16, {})
17775
- })
17776
- }), open ? /* @__PURE__ */ jsx(ClaudeSessionMenuCard, {
17777
- openIn,
17778
- submenuOpen,
17779
- failure,
17780
- onSubmenuOpen: setSubmenuOpen,
17781
- onSelect: select
17782
- }) : null]
17783
- });
17784
- }
17785
- //#endregion
17786
18104
  //#region src/client/claude-mark.ts
17787
18105
  /** The Claude wordmark glyph, lifted verbatim from the brand SVG.
17788
18106
  *
@@ -17929,41 +18247,6 @@ window.__ModuleLoader__.load({
17929
18247
  });
17930
18248
  }
17931
18249
  //#endregion
17932
- //#region src/client/panel-open-store.ts
17933
- var PanelOpenStore = class {
17934
- #sessionId;
17935
- #listeners = /* @__PURE__ */ new Set();
17936
- /** Mark this panel open for one session, replacing any previous holder. */
17937
- open(sessionId) {
17938
- this.#set(sessionId);
17939
- }
17940
- /** Mark this panel closed. */
17941
- close() {
17942
- this.#set(void 0);
17943
- }
17944
- /** Whether this panel is currently open for this session. */
17945
- isOpen(sessionId) {
17946
- return this.#sessionId === sessionId;
17947
- }
17948
- /** A per-session snapshot source for `useSyncExternalStore`. */
17949
- sourceFor(sessionId) {
17950
- return {
17951
- subscribe: (listener) => {
17952
- this.#listeners.add(listener);
17953
- return () => {
17954
- this.#listeners.delete(listener);
17955
- };
17956
- },
17957
- getSnapshot: () => this.isOpen(sessionId)
17958
- };
17959
- }
17960
- #set(sessionId) {
17961
- if (this.#sessionId === sessionId) return;
17962
- this.#sessionId = sessionId;
17963
- for (const listener of this.#listeners) listener();
17964
- }
17965
- };
17966
- //#endregion
17967
18250
  //#region src/client/claude-command-source.ts
17968
18251
  const SOURCE_NAME = "Claude Code";
17969
18252
  function commandsFor(store, session) {
@@ -18523,7 +18806,7 @@ window.__ModuleLoader__.load({
18523
18806
  for (const seat of document.querySelectorAll(`[${CLAUDE_SEAT_ATTRIBUTE}]`)) seat.removeAttribute(CLAUDE_SEAT_ATTRIBUTE);
18524
18807
  };
18525
18808
  }
18526
- 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("")}${[
18809
+ 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("")}${[
18527
18810
  `button[${CLAUDE_SEAT_ATTRIBUTE}]>[class*="seatIcon"]{display:none}`,
18528
18811
  `button[${CLAUDE_SEAT_ATTRIBUTE}]::before{content:"";flex:none;width:16px;height:16px;`,
18529
18812
  `background:${claudeMarkUrl()} center/contain no-repeat}`
@@ -18543,170 +18826,6 @@ window.__ModuleLoader__.load({
18543
18826
  };
18544
18827
  }
18545
18828
  //#endregion
18546
- //#region src/client/details-resize.ts
18547
- const DETAILS_MIN_WIDTH = 300;
18548
- const DETAILS_DEFAULT_WIDTH = 720;
18549
- const DETAILS_MAX_RATIO = .5;
18550
- const DRAG_THRESHOLD = 4;
18551
- function clampDetailsWidth(width, frameWidth) {
18552
- const maximum = Math.max(DETAILS_MIN_WIDTH, Math.floor(frameWidth * DETAILS_MAX_RATIO));
18553
- return Math.min(maximum, Math.max(DETAILS_MIN_WIDTH, Math.round(width)));
18554
- }
18555
- function defaultDetailsWidth(frameWidth) {
18556
- return clampDetailsWidth(DETAILS_DEFAULT_WIDTH, frameWidth);
18557
- }
18558
- function pixelValue(value) {
18559
- const parsed = Number.parseFloat(value);
18560
- return Number.isFinite(parsed) ? parsed : void 0;
18561
- }
18562
- /**
18563
- * Extends the native Details drag handle for the plugin Diff panel without
18564
- * depending on DSH's private layout store. The returned cleanup restores every
18565
- * inline value owned by the native frame.
18566
- */
18567
- function enableExpandedDetailsResize() {
18568
- if (typeof document === "undefined" || typeof window === "undefined") return () => void 0;
18569
- let frame;
18570
- let handle;
18571
- let width;
18572
- let dragStartX = 0;
18573
- let dragStartWidth = 0;
18574
- let pointerId;
18575
- let dragged = false;
18576
- let originalGrid = "";
18577
- let originalHandleLeft = "";
18578
- let frameWasDragging = false;
18579
- let handleWasDragging = false;
18580
- let resizeObserver;
18581
- let mutationObserver;
18582
- let animationFrame;
18583
- const applyWidth = () => {
18584
- if (frame === void 0 || handle === void 0 || width === void 0) return;
18585
- const frameWidth = frame.getBoundingClientRect().width;
18586
- if (frameWidth <= 0) return;
18587
- width = clampDetailsWidth(width, frameWidth);
18588
- const grid = `${pixelValue(getComputedStyle(frame).gridTemplateColumns.split(/\s+/u)[0] ?? "") ?? 0}px minmax(0, 1fr) ${width}px`;
18589
- const left = `${frameWidth - width}px`;
18590
- if (frame.style.gridTemplateColumns !== grid) frame.style.gridTemplateColumns = grid;
18591
- if (handle.style.left !== left) handle.style.left = left;
18592
- };
18593
- const scheduleApply = () => {
18594
- if (animationFrame !== void 0) return;
18595
- animationFrame = window.requestAnimationFrame(() => {
18596
- animationFrame = void 0;
18597
- applyWidth();
18598
- });
18599
- };
18600
- const finishDrag = () => {
18601
- if (pointerId === void 0) return;
18602
- pointerId = void 0;
18603
- if (frame !== void 0 && !frameWasDragging) frame.removeAttribute("data-dragging");
18604
- if (handle !== void 0 && !handleWasDragging) handle.removeAttribute("data-dragging");
18605
- };
18606
- const onPointerMove = (event) => {
18607
- if (event.pointerId !== pointerId || frame === void 0) return;
18608
- const delta = event.clientX - dragStartX;
18609
- if (!dragged && Math.abs(delta) < DRAG_THRESHOLD) return;
18610
- dragged = true;
18611
- width = clampDetailsWidth(dragStartWidth - delta, frame.getBoundingClientRect().width);
18612
- applyWidth();
18613
- event.preventDefault();
18614
- };
18615
- const onPointerEnd = (event) => {
18616
- if (event.pointerId !== pointerId) return;
18617
- finishDrag();
18618
- };
18619
- const onPointerDown = (event) => {
18620
- const target = event.target;
18621
- if (!(target instanceof Element)) return;
18622
- const candidate = target.closest("[data-side=\"details\"]");
18623
- const candidateFrame = candidate?.parentElement;
18624
- if (candidate === null || candidate === void 0 || candidateFrame === null || candidateFrame === void 0) return;
18625
- const currentFrame = candidateFrame;
18626
- const currentHandle = candidate;
18627
- const detailsColumn = currentFrame.children.item(2);
18628
- const measuredWidth = detailsColumn instanceof HTMLElement ? detailsColumn.getBoundingClientRect().width : 0;
18629
- const frameWidth = currentFrame.getBoundingClientRect().width;
18630
- if (measuredWidth <= 0 || frameWidth <= 0) return;
18631
- const firstDrag = frame === void 0;
18632
- frame = currentFrame;
18633
- handle = currentHandle;
18634
- width = clampDetailsWidth(width ?? measuredWidth, frameWidth);
18635
- dragStartWidth = width;
18636
- dragStartX = event.clientX;
18637
- pointerId = event.pointerId;
18638
- dragged = false;
18639
- if (firstDrag) {
18640
- originalGrid = currentFrame.style.gridTemplateColumns;
18641
- originalHandleLeft = currentHandle.style.left;
18642
- frameWasDragging = currentFrame.hasAttribute("data-dragging");
18643
- handleWasDragging = currentHandle.hasAttribute("data-dragging");
18644
- }
18645
- currentFrame.setAttribute("data-dragging", "");
18646
- currentHandle.setAttribute("data-dragging", "true");
18647
- if (firstDrag) {
18648
- resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleApply);
18649
- resizeObserver?.observe(currentFrame);
18650
- mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(scheduleApply);
18651
- mutationObserver?.observe(currentFrame, {
18652
- attributes: true,
18653
- attributeFilter: ["style"]
18654
- });
18655
- mutationObserver?.observe(currentHandle, {
18656
- attributes: true,
18657
- attributeFilter: ["style"]
18658
- });
18659
- }
18660
- event.preventDefault();
18661
- event.stopPropagation();
18662
- event.stopImmediatePropagation();
18663
- };
18664
- const applyDefaultWidth = () => {
18665
- const initialHandle = document.querySelector("[data-side=\"details\"]");
18666
- const initialFrame = initialHandle?.parentElement;
18667
- if (initialHandle === null || initialHandle === void 0 || initialFrame === null || initialFrame === void 0) return;
18668
- frame = initialFrame;
18669
- handle = initialHandle;
18670
- originalGrid = frame.style.gridTemplateColumns;
18671
- originalHandleLeft = handle.style.left;
18672
- frameWasDragging = frame.hasAttribute("data-dragging");
18673
- handleWasDragging = handle.hasAttribute("data-dragging");
18674
- width = defaultDetailsWidth(frame.getBoundingClientRect().width);
18675
- resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleApply);
18676
- resizeObserver?.observe(frame);
18677
- mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(scheduleApply);
18678
- mutationObserver?.observe(frame, {
18679
- attributes: true,
18680
- attributeFilter: ["style"]
18681
- });
18682
- mutationObserver?.observe(handle, {
18683
- attributes: true,
18684
- attributeFilter: ["style"]
18685
- });
18686
- applyWidth();
18687
- };
18688
- animationFrame = window.requestAnimationFrame(() => {
18689
- animationFrame = void 0;
18690
- applyDefaultWidth();
18691
- });
18692
- document.addEventListener("pointerdown", onPointerDown, true);
18693
- window.addEventListener("pointermove", onPointerMove, true);
18694
- window.addEventListener("pointerup", onPointerEnd, true);
18695
- window.addEventListener("pointercancel", onPointerEnd, true);
18696
- return () => {
18697
- document.removeEventListener("pointerdown", onPointerDown, true);
18698
- window.removeEventListener("pointermove", onPointerMove, true);
18699
- window.removeEventListener("pointerup", onPointerEnd, true);
18700
- window.removeEventListener("pointercancel", onPointerEnd, true);
18701
- resizeObserver?.disconnect();
18702
- mutationObserver?.disconnect();
18703
- if (animationFrame !== void 0) window.cancelAnimationFrame(animationFrame);
18704
- finishDrag();
18705
- if (frame !== void 0) frame.style.gridTemplateColumns = originalGrid;
18706
- if (handle !== void 0) handle.style.left = originalHandleLeft;
18707
- };
18708
- }
18709
- //#endregion
18710
18829
  //#region src/client/locales.ts
18711
18830
  const zh = {
18712
18831
  nav: "Claude Code",
@@ -18899,6 +19018,7 @@ window.__ModuleLoader__.load({
18899
19018
  yes: "是",
18900
19019
  no: "否",
18901
19020
  diffOpen: "查看分支改动",
19021
+ diffTabTitle: "分支改动",
18902
19022
  planPanelTitle: "Plan",
18903
19023
  planClose: "关闭方案面板",
18904
19024
  planOpen: "查看方案",
@@ -18921,9 +19041,6 @@ window.__ModuleLoader__.load({
18921
19041
  planEmpty: "本会话还没有待查看的方案。",
18922
19042
  planPendingHint: "在 DSH 的审批弹框里批准或拒绝这个方案。",
18923
19043
  diffClose: "关闭 Diff 面板",
18924
- sessionMenu: "会话菜单",
18925
- sessionMenuOpenIn: "打开方式",
18926
- sessionMenuOpenFailed: "无法打开:{message}",
18927
19044
  promptSource: "常用提示词",
18928
19045
  promptSave: "存为常用提示词",
18929
19046
  promptSaveName: "名字",
@@ -18939,11 +19056,13 @@ window.__ModuleLoader__.load({
18939
19056
  promptSaveExists: "已有同名提示词,换个名字。",
18940
19057
  promptSaveFailed: "保存失败:{message}",
18941
19058
  presetHeaderHint: "当前会话运行的 Agent 预设",
18942
- diffWorkingTree: "分支改动",
19059
+ diffRepository: "仓库",
19060
+ repositoryLinked: "关联仓库:本会话改动过",
18943
19061
  diffFiles: "{count} 个已修改文件",
18944
19062
  diffFilesShort: "{count} 个文件",
18945
19063
  diffTruncated: "Diff 超出安全显示上限。请在终端中查看完整内容。",
18946
19064
  diffEmpty: "没有可显示的 tracked 文件改动",
19065
+ diffElided: "{count} 个文件过大未展示:{files}",
18947
19066
  diffMaximize: "最大化 Diff 面板",
18948
19067
  diffCommentPrevious: "上一条评论",
18949
19068
  diffCommentNext: "下一条评论",
@@ -18990,6 +19109,7 @@ window.__ModuleLoader__.load({
18990
19109
  diffMerge_squash: "Squash 合并",
18991
19110
  diffMerge_rebase: "Rebase 合并",
18992
19111
  diffMergeDescription: "使用所选方式合并当前分支的 Pull Request。",
19112
+ diffMergeAdmin: "以管理员身份合并(绕过分支保护)",
18993
19113
  diffMergeCompleted: "已合并 PR #{number}",
18994
19114
  diffUpdateBranch: "Update branch…",
18995
19115
  repositoryUpdateBranch: "Update branch",
@@ -19341,6 +19461,7 @@ window.__ModuleLoader__.load({
19341
19461
  yes: "Yes",
19342
19462
  no: "No",
19343
19463
  diffOpen: "View branch changes",
19464
+ diffTabTitle: "Branch changes",
19344
19465
  planPanelTitle: "Plan",
19345
19466
  planClose: "Close plan panel",
19346
19467
  planOpen: "View plan",
@@ -19363,9 +19484,6 @@ window.__ModuleLoader__.load({
19363
19484
  planEmpty: "This session has no plan to read yet.",
19364
19485
  planPendingHint: "Approve or reject this plan in the DSH approval dialog.",
19365
19486
  diffClose: "Close diff panel",
19366
- sessionMenu: "Session menu",
19367
- sessionMenuOpenIn: "Open in",
19368
- sessionMenuOpenFailed: "Could not open: {message}",
19369
19487
  promptSource: "Prompts",
19370
19488
  promptSave: "Save as a prompt",
19371
19489
  promptSaveName: "Name",
@@ -19381,11 +19499,13 @@ window.__ModuleLoader__.load({
19381
19499
  promptSaveExists: "A prompt with that name already exists. Try another.",
19382
19500
  promptSaveFailed: "Could not save: {message}",
19383
19501
  presetHeaderHint: "The agent preset this session runs",
19384
- diffWorkingTree: "Branch changes",
19502
+ diffRepository: "Repository",
19503
+ repositoryLinked: "Linked repository: changed by this session",
19385
19504
  diffFiles: "{count} modified file(s)",
19386
19505
  diffFilesShort: "{count} files",
19387
19506
  diffTruncated: "The diff exceeds the safe display limit. Use the terminal to inspect the complete content.",
19388
19507
  diffEmpty: "No tracked file changes to display",
19508
+ diffElided: "Skipped {count} oversized file(s): {files}",
19389
19509
  diffMaximize: "Maximize diff panel",
19390
19510
  diffCommentPrevious: "Previous comment",
19391
19511
  diffCommentNext: "Next comment",
@@ -19432,6 +19552,7 @@ window.__ModuleLoader__.load({
19432
19552
  diffMerge_squash: "Squash and merge",
19433
19553
  diffMerge_rebase: "Rebase and merge",
19434
19554
  diffMergeDescription: "Merge the pull request for this branch with the selected method.",
19555
+ diffMergeAdmin: "Merge as administrator (bypass branch protection)",
19435
19556
  diffMergeCompleted: "Merged PR #{number}",
19436
19557
  diffUpdateBranch: "Update branch…",
19437
19558
  repositoryUpdateBranch: "Update branch",
@@ -19594,38 +19715,7 @@ window.__ModuleLoader__.load({
19594
19715
  };
19595
19716
  //#endregion
19596
19717
  //#region src/client/index.tsx
19597
- function MaximizedDiff({ source, t, sessionId, closeDetails, restore, submitPrompt }) {
19598
- const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
19599
- const useClaudeProjection = (selector) => selector(snapshot);
19600
- return /* @__PURE__ */ jsx(ClaudePanelOverlay, {
19601
- onRestore: restore,
19602
- children: /* @__PURE__ */ jsx(ClaudeDiffPanel, {
19603
- useClaudeProjection,
19604
- t,
19605
- sessionId,
19606
- maximized: true,
19607
- closeDetails,
19608
- toggleMaximized: restore,
19609
- ...submitPrompt === void 0 ? {} : { submitPrompt }
19610
- })
19611
- });
19612
- }
19613
19718
  const name = "dsh-claude-client";
19614
- function MaximizedPlan({ source, t, sessionId, closeDetails, restore }) {
19615
- const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
19616
- const useClaudeProjection = (selector) => selector(snapshot);
19617
- return /* @__PURE__ */ jsx(ClaudePanelOverlay, {
19618
- onRestore: restore,
19619
- children: /* @__PURE__ */ jsx(ClaudePlanPanel, {
19620
- useClaudeProjection,
19621
- t,
19622
- sessionId,
19623
- maximized: true,
19624
- closeDetails,
19625
- toggleMaximized: restore
19626
- })
19627
- });
19628
- }
19629
19719
  const inject = [
19630
19720
  "slots",
19631
19721
  "locale",
@@ -19637,7 +19727,9 @@ window.__ModuleLoader__.load({
19637
19727
  "workspaces",
19638
19728
  "inputTriggers",
19639
19729
  "conversation",
19640
- "connection"
19730
+ "connection",
19731
+ "sidebarRight",
19732
+ "sidebarRightTabs"
19641
19733
  ];
19642
19734
  /** Resolve one session's composer facade.
19643
19735
  *
@@ -19737,222 +19829,39 @@ window.__ModuleLoader__.load({
19737
19829
  key: "claude-activity-step",
19738
19830
  locale: namespace
19739
19831
  }, ClaudeActivityNode));
19740
- const layout = ctx.get("layout");
19741
- let disposePluginDetails;
19742
- let disposeDiffOverlay;
19743
- let disposePlanOverlay;
19744
- let disposeExpandedDetailsResize;
19745
- let detailsSessionId;
19746
- const diffOpen = new PanelOpenStore();
19747
- const planOpen = new PanelOpenStore();
19748
- const restoreDiff = () => {
19749
- if (disposeDiffOverlay === void 0) return;
19750
- disposeDiffOverlay();
19751
- disposeDiffOverlay = void 0;
19752
- layout?.openDetails();
19753
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19832
+ const sidebarTabs = registerClaudeSidebarTabs(ctx, {
19833
+ t,
19834
+ namespace,
19835
+ diffFace: (sessionId) => {
19836
+ const submitPrompt = submitPromptFor(sessionId);
19837
+ return submitPrompt === void 0 ? {} : { submitPrompt };
19838
+ },
19839
+ overviewFace: () => sessions === void 0 ? void 0 : {
19840
+ t,
19841
+ openSession: (id) => {
19842
+ sessions.open(id);
19843
+ },
19844
+ loadStatus: loadRepositoryStatusFor,
19845
+ sessions: sessions.list,
19846
+ ...workspaces === void 0 ? {} : { workspaces: workspaces.list },
19847
+ projectionFor: (id) => projections.source(id)
19848
+ }
19849
+ });
19850
+ const openTasksPanel = (sessionId, turn) => {
19851
+ sidebarTabs.open(CLAUDE_TAB_KINDS.tasks, sessionId, { turn });
19754
19852
  };
19755
- const restorePlan = () => {
19756
- if (disposePlanOverlay === void 0) return;
19757
- disposePlanOverlay();
19758
- disposePlanOverlay = void 0;
19759
- layout?.openDetails();
19760
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19853
+ const openOverviewPanel = (sessionId) => {
19854
+ sidebarTabs.open(CLAUDE_TAB_KINDS.overview, sessionId, {});
19761
19855
  };
19762
- const closePluginDetails = () => {
19763
- if (disposePluginDetails === void 0 && disposeDiffOverlay === void 0 && disposePlanOverlay === void 0 && disposeExpandedDetailsResize === void 0 && detailsSessionId === void 0) return;
19764
- disposeDiffOverlay?.();
19765
- disposeDiffOverlay = void 0;
19766
- disposePlanOverlay?.();
19767
- disposePlanOverlay = void 0;
19768
- disposeExpandedDetailsResize?.();
19769
- disposeExpandedDetailsResize = void 0;
19770
- disposePluginDetails?.();
19771
- disposePluginDetails = void 0;
19772
- detailsSessionId = void 0;
19773
- diffOpen.close();
19774
- planOpen.close();
19775
- layout?.closeDetails();
19856
+ const openDiffPanel = (sessionId, initialRoot) => {
19857
+ sidebarTabs.open(CLAUDE_TAB_KINDS.diff, sessionId, initialRoot === void 0 ? {} : { initialRoot });
19776
19858
  };
19777
19859
  ctx.effect(() => ctx.slots.onEntryError((key, entry, error) => {
19778
19860
  const id = entry.options.id === void 0 ? "" : ` id="${String(entry.options.id)}"`;
19779
19861
  const message = error instanceof Error ? `${error.message}
19780
19862
  ${error.stack ?? ""}` : String(error);
19781
19863
  diagnostics.report("slot-entry-crashed", `slot "${key}"${id}: ${message}`);
19782
- if (key === "shell.overlay" && entry.options.id === "claude-diff-overlay") restoreDiff();
19783
- if (key === "shell.overlay" && entry.options.id === "claude-plan-overlay") restorePlan();
19784
19864
  }), "dsh-claude: Slot entry failure reporting");
19785
- const openTasksPanel = (sessionId, turn) => {
19786
- closePluginDetails();
19787
- try {
19788
- disposePluginDetails = ctx.slots.register({
19789
- name: "details",
19790
- priority: -10,
19791
- locale: namespace,
19792
- inject: () => ({
19793
- t,
19794
- turn,
19795
- closeDetails: closePluginDetails
19796
- })
19797
- }, ClaudeTasksPanel);
19798
- } catch {
19799
- return;
19800
- }
19801
- detailsSessionId = sessionId;
19802
- layout?.openDetails();
19803
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19804
- };
19805
- const openPlanPanel = (sessionId) => {
19806
- closePluginDetails();
19807
- const maximizePlan = () => {
19808
- if (disposePlanOverlay !== void 0) {
19809
- restorePlan();
19810
- return;
19811
- }
19812
- disposeExpandedDetailsResize?.();
19813
- disposeExpandedDetailsResize = void 0;
19814
- layout?.closeDetails();
19815
- try {
19816
- disposePlanOverlay = ctx.slots.register({
19817
- name: "shell.overlay",
19818
- id: "claude-plan-overlay",
19819
- locale: namespace
19820
- }, () => /* @__PURE__ */ jsx(MaximizedPlan, {
19821
- source: projections.source(sessionId),
19822
- t,
19823
- sessionId,
19824
- closeDetails: closePluginDetails,
19825
- restore: restorePlan
19826
- }));
19827
- } catch {
19828
- disposePlanOverlay = void 0;
19829
- layout?.openDetails();
19830
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19831
- }
19832
- };
19833
- try {
19834
- disposePluginDetails = ctx.slots.register({
19835
- name: "details",
19836
- priority: -10,
19837
- locale: namespace,
19838
- inject: () => ({
19839
- t,
19840
- sessionId,
19841
- closeDetails: closePluginDetails,
19842
- maximized: false,
19843
- toggleMaximized: maximizePlan
19844
- })
19845
- }, ClaudePlanPanel);
19846
- } catch {
19847
- return;
19848
- }
19849
- detailsSessionId = sessionId;
19850
- planOpen.open(sessionId);
19851
- layout?.openDetails();
19852
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19853
- };
19854
- const openOverviewPanel = (sessionId) => {
19855
- if (sessions === void 0) return;
19856
- closePluginDetails();
19857
- try {
19858
- disposePluginDetails = ctx.slots.register({
19859
- name: "details",
19860
- priority: -10,
19861
- locale: namespace,
19862
- inject: () => ({
19863
- t,
19864
- closeDetails: closePluginDetails,
19865
- openSession: (id) => {
19866
- sessions.open(id);
19867
- },
19868
- loadStatus: loadRepositoryStatusFor,
19869
- sessions: sessions.list,
19870
- ...workspaces === void 0 ? {} : { workspaces: workspaces.list },
19871
- projectionFor: (id) => projections.source(id)
19872
- })
19873
- }, ClaudePullRequestsPanel);
19874
- } catch {
19875
- return;
19876
- }
19877
- detailsSessionId = sessionId;
19878
- layout?.openDetails();
19879
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19880
- };
19881
- const openDiffPanel = (sessionId) => {
19882
- closePluginDetails();
19883
- detailsSessionId = sessionId;
19884
- const submitPrompt = submitPromptFor(sessionId);
19885
- const registerDetails = () => {
19886
- try {
19887
- disposePluginDetails = ctx.slots.register({
19888
- name: "details",
19889
- priority: -10,
19890
- locale: namespace,
19891
- inject: () => ({
19892
- t,
19893
- sessionId,
19894
- maximized: false,
19895
- closeDetails: closePluginDetails,
19896
- toggleMaximized: maximizeDiff,
19897
- ...submitPrompt === void 0 ? {} : { submitPrompt }
19898
- })
19899
- }, ClaudeDiffPanel);
19900
- } catch {
19901
- disposePluginDetails = void 0;
19902
- return false;
19903
- }
19904
- layout?.openDetails();
19905
- return true;
19906
- };
19907
- const maximizeDiff = () => {
19908
- if (disposeDiffOverlay !== void 0) {
19909
- restoreDiff();
19910
- return;
19911
- }
19912
- disposeExpandedDetailsResize?.();
19913
- disposeExpandedDetailsResize = void 0;
19914
- layout?.closeDetails();
19915
- try {
19916
- disposeDiffOverlay = ctx.slots.register({
19917
- name: "shell.overlay",
19918
- id: "claude-diff-overlay",
19919
- locale: namespace
19920
- }, () => /* @__PURE__ */ jsx(MaximizedDiff, {
19921
- source: projections.source(sessionId),
19922
- t,
19923
- sessionId,
19924
- closeDetails: closePluginDetails,
19925
- restore: restoreDiff,
19926
- ...submitPrompt === void 0 ? {} : { submitPrompt }
19927
- }));
19928
- } catch {
19929
- disposeDiffOverlay = void 0;
19930
- layout?.openDetails();
19931
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19932
- }
19933
- };
19934
- if (!registerDetails()) {
19935
- detailsSessionId = void 0;
19936
- return;
19937
- }
19938
- diffOpen.open(sessionId);
19939
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19940
- };
19941
- ctx.effect(() => {
19942
- if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => closePluginDetails();
19943
- const observer = new MutationObserver(() => {
19944
- if (detailsSessionId !== void 0 && disposeDiffOverlay === void 0 && disposePlanOverlay === void 0 && document.querySelector("[data-details-collapsed]") !== null) closePluginDetails();
19945
- });
19946
- observer.observe(document.body, {
19947
- attributes: true,
19948
- attributeFilter: ["data-details-collapsed"],
19949
- subtree: true
19950
- });
19951
- return () => {
19952
- observer.disconnect();
19953
- closePluginDetails();
19954
- };
19955
- }, "dsh-claude: details panel lifecycle");
19956
19865
  ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
19957
19866
  name: "conversation.chat.node",
19958
19867
  key: "claude-active-tasks",
@@ -19995,10 +19904,9 @@ window.__ModuleLoader__.load({
19995
19904
  inject: (sessionId) => ({
19996
19905
  t,
19997
19906
  togglePlan: () => {
19998
- if (planOpen.isOpen(sessionId)) closePluginDetails();
19999
- else openPlanPanel(sessionId);
19907
+ sidebarTabs.toggle(CLAUDE_TAB_KINDS.plan, sessionId, {});
20000
19908
  },
20001
- planOpen: planOpen.sourceFor(sessionId)
19909
+ planOpen: sidebarTabs.sourceFor(CLAUDE_TAB_KINDS.plan, sessionId)
20002
19910
  })
20003
19911
  }, ClaudePlanHeaderAction));
20004
19912
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
@@ -20009,19 +19917,11 @@ window.__ModuleLoader__.load({
20009
19917
  inject: (sessionId) => ({
20010
19918
  t,
20011
19919
  toggleDiff: () => {
20012
- if (diffOpen.isOpen(sessionId)) closePluginDetails();
20013
- else openDiffPanel(sessionId);
19920
+ sidebarTabs.toggle(CLAUDE_TAB_KINDS.diff, sessionId, {});
20014
19921
  },
20015
- diffOpen: diffOpen.sourceFor(sessionId)
19922
+ diffOpen: sidebarTabs.sourceFor(CLAUDE_TAB_KINDS.diff, sessionId)
20016
19923
  })
20017
19924
  }, ClaudeDiffHeaderAction));
20018
- ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
20019
- name: "conversation.session.header.utilities",
20020
- id: "claude-session-menu",
20021
- order: 31,
20022
- locale: namespace,
20023
- inject: () => ({ t })
20024
- }, ClaudeSessionMenu));
20025
19925
  ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
20026
19926
  name: "conversation.input.left",
20027
19927
  id: "claude-prompt-save",
@@ -20076,7 +19976,7 @@ window.__ModuleLoader__.load({
20076
19976
  const submitPrompt = submitPromptFor(sessionId);
20077
19977
  return {
20078
19978
  t,
20079
- openDiff: () => openDiffPanel(sessionId),
19979
+ openDiff: (root) => openDiffPanel(sessionId, root),
20080
19980
  ...submitPrompt === void 0 ? {} : { submitPrompt },
20081
19981
  ...sessions === void 0 ? {} : { openOverview: () => openOverviewPanel(sessionId) },
20082
19982
  ...workspaces === void 0 ? {} : { deleteWorkspace: async () => {
@@ -20209,7 +20109,7 @@ window.__ModuleLoader__.load({
20209
20109
  const sourceInput = sessionInput(conversation, sourceScope);
20210
20110
  const rawDraft = sourceInput.state.getSnapshot().draft;
20211
20111
  const draft = ticket === void 0 ? rawDraft : rawDraft.trim() === "" ? ticketPrompt(ticket) : `${rawDraft.trimEnd()}\n\n${ticketContext(ticket)}`;
20212
- const imageIds = sourceInput.state.getSnapshot().imageIds;
20112
+ const imageIds = sourceInput.state.getSnapshot().attachmentIds;
20213
20113
  const prepared = await prepareRepository(cwd, branch, useWorktree, ticket?.key, onProgress, ticket === void 0 ? draft : void 0);
20214
20114
  if (ticket !== void 0) assignJiraTicket(ticket.key).catch((reason) => {
20215
20115
  console.warn(`dsh-claude: could not assign ${ticket.key}: ${reason instanceof Error ? reason.message : String(reason)}`);
@@ -20230,14 +20130,14 @@ window.__ModuleLoader__.load({
20230
20130
  sessions.noteAgentPreset?.(targetSessionId, presetResponse.value);
20231
20131
  const targetInput = sessionInput(conversation, targetScope);
20232
20132
  onProgress("transferring-draft");
20233
- if (imageIds.length > 0 && !targetInput.addImages(imageIds)) throw new Error(t("repositoryDraftTransferFailed"));
20133
+ if (imageIds.length > 0 && !targetInput.addAttachments(imageIds)) throw new Error(t("repositoryDraftTransferFailed"));
20234
20134
  if (draft !== "") targetInput.setDraft(draft);
20235
20135
  bindLease(prepared.leaseId, targetSessionId);
20236
20136
  sessions.open(targetSessionId);
20237
20137
  onProgress("submitting");
20238
20138
  targetInput.submit();
20239
20139
  sourceInput.setDraft("");
20240
- for (const imageId of imageIds) sourceInput.removeImage(imageId);
20140
+ for (const imageId of imageIds) sourceInput.removeAttachment(imageId);
20241
20141
  },
20242
20142
  prepareMany: async (cwd, branch, tickets, onProgress) => {
20243
20143
  const sourceScope = sessions.scope(sourceSessionId);
@@ -20285,9 +20185,6 @@ window.__ModuleLoader__.load({
20285
20185
  })
20286
20186
  }, ClaudeHeroRepositoryControls));
20287
20187
  }
20288
- if (sessions !== void 0) ctx.effect(() => sessions.list.subscribe(() => {
20289
- if (detailsSessionId !== void 0 && sessions.list.getSnapshot().current !== detailsSessionId) closePluginDetails();
20290
- }), "dsh-claude: details panel session tracking");
20291
20188
  ctx.slots.inject("settings.section", () => ctx.slots.register({
20292
20189
  name: "settings.section",
20293
20190
  id: "claude-code",