@norman-else/dsh-claude 0.1.46 → 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";
@@ -4457,7 +4456,7 @@ window.__ModuleLoader__.load({
4457
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;
4458
4457
  if (repository.diff !== void 0) {
4459
4458
  const diff = record$7(repository.diff);
4460
- if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
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;
4461
4460
  }
4462
4461
  if (repository.pullRequest === void 0) return true;
4463
4462
  const pullRequest = record$7(repository.pullRequest);
@@ -5060,10 +5059,10 @@ window.__ModuleLoader__.load({
5060
5059
  ".dsh-claude-act-running{animation:dsh-claude-act-pulse 1.2s ease-in-out infinite}",
5061
5060
  "@keyframes dsh-claude-act-pulse{0%,100%{opacity:1}50%{opacity:.3}}"
5062
5061
  ].join("");
5063
- let cssInjected$4 = false;
5064
- function ensureCss$4() {
5065
- if (cssInjected$4 || typeof document === "undefined") return;
5066
- cssInjected$4 = true;
5062
+ let cssInjected$3 = false;
5063
+ function ensureCss$3() {
5064
+ if (cssInjected$3 || typeof document === "undefined") return;
5065
+ cssInjected$3 = true;
5067
5066
  const element = document.createElement("style");
5068
5067
  element.dataset.dshClaudeActivity = "";
5069
5068
  element.textContent = ACTIVITY_CSS;
@@ -5474,7 +5473,7 @@ window.__ModuleLoader__.load({
5474
5473
  * the steps the Host never had a message for. Mounted at the turn's foot, so
5475
5474
  * it closes the turn under the task badge rather than above it. */
5476
5475
  function ClaudeTurnUsage({ usage, t }) {
5477
- ensureCss$4();
5476
+ ensureCss$3();
5478
5477
  const parts = turnUsageParts(usage, t);
5479
5478
  if (parts.length === 0) return null;
5480
5479
  return /* @__PURE__ */ jsxs("div", {
@@ -5493,7 +5492,7 @@ window.__ModuleLoader__.load({
5493
5492
  });
5494
5493
  }
5495
5494
  function ClaudeActivityNode({ node, useClaudeProjection, t }) {
5496
- ensureCss$4();
5495
+ ensureCss$3();
5497
5496
  const marker = node.data;
5498
5497
  const activities = useClaudeProjection((value) => selectStepActivities(value, marker.turn, marker.step));
5499
5498
  const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS$1);
@@ -7970,10 +7969,10 @@ window.__ModuleLoader__.load({
7970
7969
  "width:6px;height:6px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#e2a03f);",
7971
7970
  "box-shadow:0 0 0 2px var(--dsw-alias-bg-base)}"
7972
7971
  ].join("");
7973
- let cssInjected$3 = false;
7974
- function ensureCss$3() {
7975
- if (cssInjected$3 || typeof document === "undefined") return;
7976
- cssInjected$3 = true;
7972
+ let cssInjected$2 = false;
7973
+ function ensureCss$2() {
7974
+ if (cssInjected$2 || typeof document === "undefined") return;
7975
+ cssInjected$2 = true;
7977
7976
  const element = document.createElement("style");
7978
7977
  element.dataset.dshClaudeHeaderPlan = "";
7979
7978
  element.textContent = ACTION_CSS$1;
@@ -8009,7 +8008,7 @@ window.__ModuleLoader__.load({
8009
8008
  if (!open) togglePlan();
8010
8009
  });
8011
8010
  if (!owned || review === void 0) return null;
8012
- ensureCss$3();
8011
+ ensureCss$2();
8013
8012
  const label = t(open ? "planClose" : "planOpen");
8014
8013
  return /* @__PURE__ */ jsx(Tooltip, {
8015
8014
  label,
@@ -8321,7 +8320,13 @@ window.__ModuleLoader__.load({
8321
8320
  ],
8322
8321
  uiConversation: ["binding"],
8323
8322
  uiSession: ["provide"],
8324
- inputTriggers: ["registerSource"]
8323
+ inputTriggers: ["registerSource"],
8324
+ sidebarRight: [
8325
+ "openTabIn",
8326
+ "closeIn",
8327
+ "toggleExpanded"
8328
+ ],
8329
+ sidebarRightTabs: ["register"]
8325
8330
  };
8326
8331
  /** One line per service the Host no longer provides, or provides without a
8327
8332
  * method this plugin calls on it; empty when everything resolves. */
@@ -9867,6 +9872,165 @@ window.__ModuleLoader__.load({
9867
9872
  });
9868
9873
  }
9869
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
9870
10034
  //#region node_modules/.pnpm/dompurify@3.4.14/node_modules/dompurify/dist/purify.es.mjs
9871
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 */
9872
10036
  function _arrayLikeToArray(r, a) {
@@ -15530,35 +15694,45 @@ window.__ModuleLoader__.load({
15530
15694
  /* @__PURE__ */ jsxs("div", {
15531
15695
  ref: diffBodyRef,
15532
15696
  style: diffBody,
15533
- children: [diff.truncated ? /* @__PURE__ */ jsx("p", {
15534
- style: diffNotice,
15535
- children: t("diffTruncated")
15536
- }) : null, files.length === 0 ? /* @__PURE__ */ jsx("p", {
15537
- style: diffEmpty,
15538
- children: t("diffEmpty")
15539
- }) : files.map((file, index) => /* @__PURE__ */ jsx(DiffFileSection, {
15540
- file,
15541
- root: repository.root,
15542
- open: fileOpen(file.path, index),
15543
- onOpenChange: (open) => {
15544
- setFileOpen(file.path, open);
15545
- },
15546
- t,
15547
- comments: reviewComments.filter((comment) => comment.path === file.path),
15548
- ghThreads: ghThreads.filter((thread) => thread.path === file.path),
15549
- suggestMention,
15550
- now: threadsLoadedAt,
15551
- activeTargetKey,
15552
- onReplyToThread: replyToThread,
15553
- onThreadResolvedChange: changeThreadResolved,
15554
- onSendThreadToAi: submitPrompt === void 0 ? void 0 : (thread) => {
15555
- submitPrompt(composeCommentsPrompt([thread]));
15556
- },
15557
- editorAnchor: commentEditor !== void 0 && commentEditor.path === file.path ? commentEditor : void 0,
15558
- editorNode: commentEditorNode,
15559
- onOpenEditor: (anchor) => openCommentEditor(file.path, anchor),
15560
- onRemoveComment: removeComment
15561
- }, file.path))]
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
+ ]
15562
15736
  })
15563
15737
  ]
15564
15738
  }),
@@ -15714,247 +15888,202 @@ window.__ModuleLoader__.load({
15714
15888
  ] });
15715
15889
  }
15716
15890
  //#endregion
15717
- //#region src/client/ClaudePanelOverlay.tsx
15718
- const useClientLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
15719
- function shouldRestoreFromEscape(event, root = document) {
15720
- return event.key === "Escape" && root.querySelector("[role=\"dialog\"][aria-modal=\"true\"]") === null;
15721
- }
15722
- function workspaceBounds(overlay) {
15723
- const frame = overlay.parentElement;
15724
- if (frame === null) return void 0;
15725
- const overlayRect = overlay.getBoundingClientRect();
15726
- const candidates = Array.from(frame.children).filter((element) => element !== overlay).map((element) => element.getBoundingClientRect()).filter((rect) => rect.width > 0 && rect.height > 0);
15727
- const insetCandidates = candidates.filter((rect) => rect.left > overlayRect.left);
15728
- const workspace = (insetCandidates.length > 0 ? insetCandidates : candidates).reduce((largest, rect) => {
15729
- if (largest === void 0 || rect.width * rect.height > largest.width * largest.height) return rect;
15730
- return largest;
15731
- }, void 0);
15732
- if (workspace === void 0) return void 0;
15733
- const left = Math.max(0, workspace.left - overlayRect.left);
15734
- const top = Math.max(0, workspace.top - overlayRect.top);
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;
15955
+ useEffect(() => {
15956
+ noteTab(tabId);
15957
+ return () => {
15958
+ noteTab(void 0);
15959
+ };
15960
+ }, [noteTab, tabId]);
15961
+ const actions = info.tab.actions;
15735
15962
  return {
15736
- left,
15737
- top,
15738
- width: Math.max(0, Math.min(workspace.width, overlayRect.width - left)),
15739
- height: Math.max(0, Math.min(workspace.height, overlayRect.height - top))
15963
+ closeDetails: useCallback(() => {
15964
+ actions.close();
15965
+ }, [actions]),
15966
+ maximized: info.sidebar.fullscreen,
15967
+ params: info.tab.navigation.params ?? {}
15740
15968
  };
15741
15969
  }
15742
- function observeWorkspaceBounds(overlay, listener) {
15743
- const frame = overlay.parentElement;
15744
- if (frame === null) return () => {};
15745
- const update = () => {
15746
- const next = workspaceBounds(overlay);
15747
- if (next !== void 0) listener(next);
15748
- };
15749
- update();
15750
- const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(update);
15751
- observer?.observe(frame);
15752
- for (const element of Array.from(frame.children)) observer?.observe(element);
15753
- const mutation = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(update);
15754
- mutation?.observe(frame, {
15755
- attributes: true,
15756
- childList: true
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 }
15757
15978
  });
15758
- window.addEventListener("resize", update);
15759
- return () => {
15760
- observer?.disconnect();
15761
- mutation?.disconnect();
15762
- window.removeEventListener("resize", update);
15763
- };
15764
15979
  }
15765
- function ClaudePanelOverlay({ children, onRestore }) {
15766
- const ref = useRef(null);
15767
- const [bounds, setBounds] = useState();
15768
- useClientLayoutEffect(() => {
15769
- const overlay = ref.current?.closest("[data-shell-overlay]");
15770
- if (overlay === void 0 || overlay === null) return;
15771
- return observeWorkspaceBounds(overlay, (next) => {
15772
- setBounds((current) => current?.left === next.left && current.top === next.top && current.width === next.width && current.height === next.height ? current : next);
15773
- });
15774
- }, []);
15775
- useEffect(() => {
15776
- const escape = (event) => {
15777
- if (shouldRestoreFromEscape(event)) onRestore();
15778
- };
15779
- window.addEventListener("keydown", escape);
15780
- return () => window.removeEventListener("keydown", escape);
15781
- }, [onRestore]);
15782
- return /* @__PURE__ */ jsx("div", {
15783
- ref,
15784
- "data-dsh-claude-panel-overlay": true,
15785
- style: {
15786
- position: "absolute",
15787
- left: bounds?.left ?? 0,
15788
- top: bounds?.top ?? 0,
15789
- width: bounds?.width ?? 0,
15790
- height: bounds?.height ?? 0,
15791
- visibility: bounds === void 0 ? "hidden" : "visible",
15792
- padding: 8,
15793
- boxSizing: "border-box",
15794
- background: "var(--dsw-alias-bg-base)",
15795
- pointerEvents: bounds === void 0 ? "none" : "auto"
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();
16012
+ };
16013
+ const faceFor = (kind, sessionId) => ({
16014
+ noteTab: (tabId) => {
16015
+ occupancy.note(kind, sessionId, tabId);
15796
16016
  },
15797
- children
15798
- });
15799
- }
15800
- //#endregion
15801
- //#region src/client/ClaudeQueueDock.tsx
15802
- /** Replaces the Host queue strip (same slot cell, lower priority) with one
15803
- * styled like the repository status bar. Behaviour mirrors the Host strip:
15804
- * one row inline, several rows behind a collapsible count, edit/remove/steer. */
15805
- function ClaudeQueueDock({ useSession, t, updateQueue, notify }) {
15806
- const inbox = useSession((session) => session.queue);
15807
- const queue = useMemo(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
15808
- const running = useSession((session) => session.running);
15809
- const mutable = useSession((session) => session.subagent === null);
15810
- const [editing, setEditing] = useState();
15811
- const [busy, setBusy] = useState();
15812
- const [collapsed, setCollapsed] = useState(true);
15813
- const listId = useId();
15814
- useEffect(() => {
15815
- if (queue.length === 0 && !collapsed) setCollapsed(true);
15816
- if (editing !== void 0 && (!mutable || !queue.some((row) => row.id === editing.id))) setEditing(void 0);
15817
- }, [
15818
- collapsed,
15819
- editing,
15820
- mutable,
15821
- queue
15822
- ]);
15823
- if (queue.length === 0) return null;
15824
- const interacting = mutable && (editing !== void 0 || busy !== void 0);
15825
- const expanded = !collapsed || interacting;
15826
- const listVisible = queue.length === 1 || expanded;
15827
- const apply = async (id, action, failure) => {
15828
- setBusy(id);
15829
- try {
15830
- await updateQueue(id, action);
15831
- return true;
15832
- } catch {
15833
- notify("error", failure);
15834
- return false;
15835
- } finally {
15836
- setBusy((current) => current === id ? void 0 : current);
15837
- }
15838
- };
15839
- const saveEdit = async () => {
15840
- if (editing === void 0 || editing.text.trim() === "") return;
15841
- if (await apply(editing.id, {
15842
- kind: "edit",
15843
- content: [{
15844
- type: "text",
15845
- text: editing.text
15846
- }]
15847
- }, t("queueEditFailed"))) setEditing(void 0);
15848
- };
15849
- const action = (label, icon, onClick, disabled, hint) => /* @__PURE__ */ jsx(Tooltip, {
15850
- label: hint ?? label,
15851
- side: "bottom",
15852
- delayMs: 400,
15853
- children: /* @__PURE__ */ jsx("button", {
15854
- type: "button",
15855
- className: panelIconButtonClass,
15856
- "aria-label": label,
15857
- disabled,
15858
- onClick,
15859
- children: icon
15860
- })
15861
- });
15862
- return /* @__PURE__ */ jsxs("div", {
15863
- style: repositoryBarFrame,
15864
- "data-claude-queue-dock": "",
15865
- children: [/* @__PURE__ */ jsx("style", {
15866
- "data-dsh-claude-queue-styles": true,
15867
- children: panelIconButtonCss
15868
- }), /* @__PURE__ */ jsxs("div", {
15869
- style: queueBar,
15870
- children: [queue.length > 1 ? /* @__PURE__ */ jsxs("button", {
15871
- type: "button",
15872
- style: queueHeader,
15873
- "aria-controls": listId,
15874
- "aria-expanded": expanded,
15875
- disabled: interacting,
15876
- onClick: () => {
15877
- setCollapsed((value) => !value);
15878
- },
15879
- children: [
15880
- /* @__PURE__ */ jsx("span", {
15881
- style: queueLead,
15882
- "aria-hidden": "true",
15883
- children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
15884
- }),
15885
- /* @__PURE__ */ jsx("span", {
15886
- style: queueCount,
15887
- children: t("queueCount", { n: queue.length })
15888
- }),
15889
- /* @__PURE__ */ jsx("span", {
15890
- style: queueLead,
15891
- "aria-hidden": "true",
15892
- children: expanded ? /* @__PURE__ */ jsx(IconChevronDownOutline14, {}) : /* @__PURE__ */ jsx(IconChevronUpOutline14, {})
15893
- })
15894
- ]
15895
- }) : null, /* @__PURE__ */ jsx("ul", {
15896
- id: listId,
15897
- style: queueList,
15898
- hidden: !listVisible,
15899
- children: listVisible ? queue.map((row, index) => /* @__PURE__ */ jsxs("li", {
15900
- style: {
15901
- ...queueRow,
15902
- ...index > 0 ? queueRowDivider : {}
15903
- },
15904
- children: [
15905
- queue.length === 1 ? /* @__PURE__ */ jsx("span", {
15906
- style: queueLead,
15907
- "aria-hidden": "true",
15908
- children: /* @__PURE__ */ jsx(IconQueueOutline14, {})
15909
- }) : null,
15910
- editing?.id === row.id ? /* @__PURE__ */ jsx("input", {
15911
- autoFocus: true,
15912
- style: queueEditor,
15913
- "aria-label": t("queueEdit"),
15914
- value: editing.text,
15915
- onChange: (event) => {
15916
- setEditing({
15917
- id: row.id,
15918
- text: event.currentTarget.value
15919
- });
15920
- },
15921
- onKeyDown: (event) => {
15922
- if (event.key === "Escape") setEditing(void 0);
15923
- else if (event.key === "Enter" && !event.nativeEvent.isComposing) {
15924
- event.preventDefault();
15925
- saveEdit();
15926
- }
15927
- }
15928
- }) : /* @__PURE__ */ jsx("span", {
15929
- style: queuePreview,
15930
- children: row.preview
15931
- }),
15932
- mutable ? /* @__PURE__ */ jsx("span", {
15933
- style: queueActions,
15934
- children: editing?.id === row.id ? /* @__PURE__ */ jsxs(Fragment$1, { children: [action(t("queueSave"), /* @__PURE__ */ jsx(IconCheckOutline16, { size: 14 }), () => {
15935
- saveEdit();
15936
- }, busy !== void 0 || editing.text.trim() === ""), action(t("queueCancelEdit"), /* @__PURE__ */ jsx(IconCloseOutline16, { size: 14 }), () => {
15937
- setEditing(void 0);
15938
- }, busy !== void 0)] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
15939
- action(t("queueEdit"), /* @__PURE__ */ jsx(IconEditOutline16, { size: 14 }), () => {
15940
- if (row.text !== null) setEditing({
15941
- id: row.id,
15942
- text: row.text
15943
- });
15944
- }, busy !== void 0 || row.text === null, row.text === null ? t("queueEditUnsupported") : void 0),
15945
- action(t("queueRemove"), /* @__PURE__ */ jsx(IconTrashOutline16, { size: 14 }), () => {
15946
- apply(row.id, { kind: "remove" }, t("queueRemoveFailed"));
15947
- }, busy !== void 0),
15948
- action(t("queueSteer"), /* @__PURE__ */ jsx(IconSendOutline14, {}), () => {
15949
- apply(row.id, { kind: "steer" }, t("queueSteerFailed"));
15950
- }, busy !== void 0 || !running, running ? void 0 : t("queueSteerUnavailable"))
15951
- ] })
15952
- }) : null
15953
- ]
15954
- }, row.id)) : null
15955
- })]
15956
- })]
16017
+ toggleMaximized
15957
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")
16024
+ };
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)
16040
+ })
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
+ };
15958
16087
  }
15959
16088
  //#endregion
15960
16089
  //#region src/client/ask-api.ts
@@ -17905,10 +18034,10 @@ window.__ModuleLoader__.load({
17905
18034
  ".dsh-claude-header-diff-add{color:var(--dsw-alias-state-success-primary)}",
17906
18035
  ".dsh-claude-header-diff-del{color:var(--dsw-alias-state-error-primary)}"
17907
18036
  ].join("");
17908
- let cssInjected$2 = false;
17909
- function ensureCss$2() {
17910
- if (cssInjected$2 || typeof document === "undefined") return;
17911
- cssInjected$2 = true;
18037
+ let cssInjected$1 = false;
18038
+ function ensureCss$1() {
18039
+ if (cssInjected$1 || typeof document === "undefined") return;
18040
+ cssInjected$1 = true;
17912
18041
  const element = document.createElement("style");
17913
18042
  element.dataset.dshClaudeHeaderDiff = "";
17914
18043
  element.textContent = ACTION_CSS;
@@ -17946,7 +18075,7 @@ window.__ModuleLoader__.load({
17946
18075
  const deletions = useClaudeProjection((projection) => diffTotal(projection, "deletions"));
17947
18076
  const open = useSyncExternalStore(diffOpen.subscribe, diffOpen.getSnapshot, diffOpen.getSnapshot);
17948
18077
  if (!owned) return null;
17949
- ensureCss$2();
18078
+ ensureCss$1();
17950
18079
  const hasCounts = additions > 0 || deletions > 0;
17951
18080
  const label = t(open ? "diffClose" : "diffOpen");
17952
18081
  return /* @__PURE__ */ jsx(Tooltip, {
@@ -17972,168 +18101,6 @@ window.__ModuleLoader__.load({
17972
18101
  });
17973
18102
  }
17974
18103
  //#endregion
17975
- //#region src/client/editor-open-api.ts
17976
- /** Launch the session's project in a desktop editor on the host machine. */
17977
- async function openProjectInEditor(sessionId, editor) {
17978
- await pluginWrite(CLAUDE_EDITOR_OPEN_PATH, "fast", void 0, { query: {
17979
- sessionId,
17980
- editor
17981
- } });
17982
- }
17983
- //#endregion
17984
- //#region src/client/ClaudeSessionMenu.tsx
17985
- /** Trigger matches the neighbouring diff action; the cards reproduce the
17986
- * primitives' menu surface (r12, inverted hairline, shadow-lv3, 4px inset).
17987
- *
17988
- * Hand-rolled rather than the `Menu` primitive because this menu needs its
17989
- * submenu on the LEFT: the trigger is the header's rightmost control, and the
17990
- * primitive's nested card is pinned to `left: 100%`, which would open the
17991
- * editor list off the right edge of the window. */
17992
- const MENU_CSS = [
17993
- ".dsh-claude-header-menu-root{position:relative;display:inline-flex}",
17994
- ".dsh-claude-header-menu{flex:none;display:inline-flex;align-items:center;justify-content:center;",
17995
- "width:32px;height:32px;padding:0;border:0;border-radius:9px;background:transparent;",
17996
- "color:var(--dsw-alias-label-secondary);cursor:pointer;transition:background .12s ease,color .12s ease}",
17997
- ".dsh-claude-header-menu:hover,.dsh-claude-header-menu:focus-visible,.dsh-claude-header-menu[aria-expanded=\"true\"]{",
17998
- "background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
17999
- ".dsh-claude-header-menu:active{background:var(--dsw-alias-interactive-bg-active)}",
18000
- ".dsh-claude-header-menu:focus-visible{outline:none}",
18001
- ".dsh-claude-header-menu>svg{display:block;transform:rotate(90deg)}",
18002
- ".dsh-claude-header-menu-card,.dsh-claude-header-menu-sub{box-sizing:border-box;position:absolute;",
18003
- "display:flex;flex-direction:column;padding:2px;border:1px solid var(--dsw-alias-border-inverted);",
18004
- "border-radius:7px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3)}",
18005
- ".dsh-claude-header-menu-card{top:calc(100% + 4px);right:0;z-index:100;min-width:164px;max-width:360px}",
18006
- ".dsh-claude-header-menu-sub{top:-2px;right:calc(100% + 10px);z-index:101;min-width:164px}",
18007
- ".dsh-claude-header-menu-sub::after{content:\"\";position:absolute;top:0;bottom:0;right:-10px;width:10px}",
18008
- ".dsh-claude-header-menu-row{position:relative}",
18009
- ".dsh-claude-header-menu-item{display:flex;align-items:center;gap:6px;width:100%;min-height:26px;",
18010
- "padding:3px 7px;border:none;border-radius:5px;background:transparent;cursor:pointer;text-align:left;",
18011
- "font-family:var(--dsw-font-family);font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary)}",
18012
- ".dsh-claude-header-menu-item:hover,.dsh-claude-header-menu-item:focus-visible,",
18013
- ".dsh-claude-header-menu-item[aria-expanded=\"true\"]{background:var(--dsw-alias-interactive-bg-hover)}",
18014
- ".dsh-claude-header-menu-item:focus-visible{outline:none}",
18015
- ".dsh-claude-header-menu-item>span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
18016
- ".dsh-claude-header-menu-item>svg{flex:none;color:var(--dsw-alias-label-tertiary)}",
18017
- ".dsh-claude-header-menu-heading{padding:4px 7px;font-family:var(--dsw-font-family);font-size:11px;",
18018
- "line-height:16px;color:var(--dsw-alias-label-tertiary)}"
18019
- ].join("");
18020
- let cssInjected$1 = false;
18021
- function ensureCss$1() {
18022
- if (cssInjected$1 || typeof document === "undefined") return;
18023
- cssInjected$1 = true;
18024
- for (const stale of document.querySelectorAll("style[data-dsh-claude-header-menu]")) stale.remove();
18025
- const element = document.createElement("style");
18026
- element.dataset.dshClaudeHeaderMenu = "";
18027
- element.textContent = MENU_CSS;
18028
- document.head.appendChild(element);
18029
- }
18030
- const EDITORS = [{
18031
- id: "cursor",
18032
- label: "Cursor"
18033
- }, {
18034
- id: "idea",
18035
- label: "IntelliJ IDEA"
18036
- }];
18037
- /** The open menu, split out so the SSR tests can render it without a DOM to
18038
- * drive the hover that opens the submenu. */
18039
- function ClaudeSessionMenuCard({ openIn, submenuOpen, failure, onSubmenuOpen, onSelect }) {
18040
- return /* @__PURE__ */ jsxs("div", {
18041
- className: "dsh-claude-header-menu-card",
18042
- role: "menu",
18043
- children: [/* @__PURE__ */ jsxs("div", {
18044
- className: "dsh-claude-header-menu-row",
18045
- onMouseEnter: () => onSubmenuOpen(true),
18046
- onMouseLeave: () => onSubmenuOpen(false),
18047
- children: [/* @__PURE__ */ jsxs("button", {
18048
- type: "button",
18049
- role: "menuitem",
18050
- className: "dsh-claude-header-menu-item",
18051
- "aria-haspopup": "menu",
18052
- "aria-expanded": submenuOpen,
18053
- onFocus: () => onSubmenuOpen(true),
18054
- onClick: () => onSubmenuOpen(true),
18055
- children: [/* @__PURE__ */ jsx("span", { children: openIn }), /* @__PURE__ */ jsx(IconChevronRightOutline14, {})]
18056
- }), submenuOpen ? /* @__PURE__ */ jsxs("div", {
18057
- className: "dsh-claude-header-menu-sub",
18058
- role: "menu",
18059
- children: [/* @__PURE__ */ jsx("div", {
18060
- className: "dsh-claude-header-menu-heading",
18061
- children: openIn
18062
- }), EDITORS.map((editor) => /* @__PURE__ */ jsx("button", {
18063
- type: "button",
18064
- role: "menuitem",
18065
- className: "dsh-claude-header-menu-item",
18066
- onClick: () => onSelect(editor.id),
18067
- children: /* @__PURE__ */ jsx("span", { children: editor.label })
18068
- }, editor.id))]
18069
- }) : null]
18070
- }), failure === void 0 ? null : /* @__PURE__ */ jsx("div", {
18071
- className: "dsh-claude-header-menu-heading",
18072
- children: failure
18073
- })]
18074
- });
18075
- }
18076
- function ClaudeSessionMenu({ t, sessionId, useClaudeProjection, openInEditor = openProjectInEditor }) {
18077
- const owned = useClaudeProjection((projection) => projection.owned);
18078
- const root = useRef(null);
18079
- const [open, setOpen] = useState(false);
18080
- const [submenuOpen, setSubmenuOpen] = useState(false);
18081
- const [failure, setFailure] = useState(void 0);
18082
- const close = () => {
18083
- setOpen(false);
18084
- setSubmenuOpen(false);
18085
- setFailure(void 0);
18086
- };
18087
- useDismissOnOutsidePointer(root, open, (next) => {
18088
- if (!next) close();
18089
- });
18090
- useEffect(() => {
18091
- if (!open) return void 0;
18092
- const onKeyDown = (event) => {
18093
- if (event.key === "Escape") close();
18094
- };
18095
- document.addEventListener("keydown", onKeyDown);
18096
- return () => document.removeEventListener("keydown", onKeyDown);
18097
- }, [open]);
18098
- if (!owned) return null;
18099
- ensureCss$1();
18100
- const label = t("sessionMenu");
18101
- const openIn = t("sessionMenuOpenIn");
18102
- const select = (editor) => {
18103
- setFailure(void 0);
18104
- openInEditor(sessionId, editor).then(() => close(), (error) => setFailure(t("sessionMenuOpenFailed", { message: error instanceof Error ? error.message : String(error) })));
18105
- };
18106
- return /* @__PURE__ */ jsxs("span", {
18107
- className: "dsh-claude-header-menu-root",
18108
- ref: root,
18109
- children: [/* @__PURE__ */ jsx(Tooltip, {
18110
- label,
18111
- side: "bottom",
18112
- delayMs: 250,
18113
- disabled: open,
18114
- children: /* @__PURE__ */ jsx("button", {
18115
- type: "button",
18116
- className: "dsh-claude-header-menu",
18117
- "aria-label": label,
18118
- "aria-haspopup": "menu",
18119
- "aria-expanded": open,
18120
- "data-session": sessionId,
18121
- onClick: () => {
18122
- if (open) close();
18123
- else setOpen(true);
18124
- },
18125
- children: /* @__PURE__ */ jsx(IconEllipsisOutline16, {})
18126
- })
18127
- }), open ? /* @__PURE__ */ jsx(ClaudeSessionMenuCard, {
18128
- openIn,
18129
- submenuOpen,
18130
- failure,
18131
- onSubmenuOpen: setSubmenuOpen,
18132
- onSelect: select
18133
- }) : null]
18134
- });
18135
- }
18136
- //#endregion
18137
18104
  //#region src/client/claude-mark.ts
18138
18105
  /** The Claude wordmark glyph, lifted verbatim from the brand SVG.
18139
18106
  *
@@ -18280,41 +18247,6 @@ window.__ModuleLoader__.load({
18280
18247
  });
18281
18248
  }
18282
18249
  //#endregion
18283
- //#region src/client/panel-open-store.ts
18284
- var PanelOpenStore = class {
18285
- #sessionId;
18286
- #listeners = /* @__PURE__ */ new Set();
18287
- /** Mark this panel open for one session, replacing any previous holder. */
18288
- open(sessionId) {
18289
- this.#set(sessionId);
18290
- }
18291
- /** Mark this panel closed. */
18292
- close() {
18293
- this.#set(void 0);
18294
- }
18295
- /** Whether this panel is currently open for this session. */
18296
- isOpen(sessionId) {
18297
- return this.#sessionId === sessionId;
18298
- }
18299
- /** A per-session snapshot source for `useSyncExternalStore`. */
18300
- sourceFor(sessionId) {
18301
- return {
18302
- subscribe: (listener) => {
18303
- this.#listeners.add(listener);
18304
- return () => {
18305
- this.#listeners.delete(listener);
18306
- };
18307
- },
18308
- getSnapshot: () => this.isOpen(sessionId)
18309
- };
18310
- }
18311
- #set(sessionId) {
18312
- if (this.#sessionId === sessionId) return;
18313
- this.#sessionId = sessionId;
18314
- for (const listener of this.#listeners) listener();
18315
- }
18316
- };
18317
- //#endregion
18318
18250
  //#region src/client/claude-command-source.ts
18319
18251
  const SOURCE_NAME = "Claude Code";
18320
18252
  function commandsFor(store, session) {
@@ -18874,7 +18806,7 @@ window.__ModuleLoader__.load({
18874
18806
  for (const seat of document.querySelectorAll(`[${CLAUDE_SEAT_ATTRIBUTE}]`)) seat.removeAttribute(CLAUDE_SEAT_ATTRIBUTE);
18875
18807
  };
18876
18808
  }
18877
- const HOST_CHROME_CSS = `button[class*="sessionLogButton"]{display:none}${["header:has(.dsh-claude-header-diff)>[role=\"tablist\"]{display:none}", "header:has(.dsh-claude-header-diff){padding-bottom:10px}"].join("")}${[
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("")}${[
18878
18810
  `button[${CLAUDE_SEAT_ATTRIBUTE}]>[class*="seatIcon"]{display:none}`,
18879
18811
  `button[${CLAUDE_SEAT_ATTRIBUTE}]::before{content:"";flex:none;width:16px;height:16px;`,
18880
18812
  `background:${claudeMarkUrl()} center/contain no-repeat}`
@@ -18894,170 +18826,6 @@ window.__ModuleLoader__.load({
18894
18826
  };
18895
18827
  }
18896
18828
  //#endregion
18897
- //#region src/client/details-resize.ts
18898
- const DETAILS_MIN_WIDTH = 300;
18899
- const DETAILS_DEFAULT_WIDTH = 720;
18900
- const DETAILS_MAX_RATIO = .5;
18901
- const DRAG_THRESHOLD = 4;
18902
- function clampDetailsWidth(width, frameWidth) {
18903
- const maximum = Math.max(DETAILS_MIN_WIDTH, Math.floor(frameWidth * DETAILS_MAX_RATIO));
18904
- return Math.min(maximum, Math.max(DETAILS_MIN_WIDTH, Math.round(width)));
18905
- }
18906
- function defaultDetailsWidth(frameWidth) {
18907
- return clampDetailsWidth(DETAILS_DEFAULT_WIDTH, frameWidth);
18908
- }
18909
- function pixelValue(value) {
18910
- const parsed = Number.parseFloat(value);
18911
- return Number.isFinite(parsed) ? parsed : void 0;
18912
- }
18913
- /**
18914
- * Extends the native Details drag handle for the plugin Diff panel without
18915
- * depending on DSH's private layout store. The returned cleanup restores every
18916
- * inline value owned by the native frame.
18917
- */
18918
- function enableExpandedDetailsResize() {
18919
- if (typeof document === "undefined" || typeof window === "undefined") return () => void 0;
18920
- let frame;
18921
- let handle;
18922
- let width;
18923
- let dragStartX = 0;
18924
- let dragStartWidth = 0;
18925
- let pointerId;
18926
- let dragged = false;
18927
- let originalGrid = "";
18928
- let originalHandleLeft = "";
18929
- let frameWasDragging = false;
18930
- let handleWasDragging = false;
18931
- let resizeObserver;
18932
- let mutationObserver;
18933
- let animationFrame;
18934
- const applyWidth = () => {
18935
- if (frame === void 0 || handle === void 0 || width === void 0) return;
18936
- const frameWidth = frame.getBoundingClientRect().width;
18937
- if (frameWidth <= 0) return;
18938
- width = clampDetailsWidth(width, frameWidth);
18939
- const grid = `${pixelValue(getComputedStyle(frame).gridTemplateColumns.split(/\s+/u)[0] ?? "") ?? 0}px minmax(0, 1fr) ${width}px`;
18940
- const left = `${frameWidth - width}px`;
18941
- if (frame.style.gridTemplateColumns !== grid) frame.style.gridTemplateColumns = grid;
18942
- if (handle.style.left !== left) handle.style.left = left;
18943
- };
18944
- const scheduleApply = () => {
18945
- if (animationFrame !== void 0) return;
18946
- animationFrame = window.requestAnimationFrame(() => {
18947
- animationFrame = void 0;
18948
- applyWidth();
18949
- });
18950
- };
18951
- const finishDrag = () => {
18952
- if (pointerId === void 0) return;
18953
- pointerId = void 0;
18954
- if (frame !== void 0 && !frameWasDragging) frame.removeAttribute("data-dragging");
18955
- if (handle !== void 0 && !handleWasDragging) handle.removeAttribute("data-dragging");
18956
- };
18957
- const onPointerMove = (event) => {
18958
- if (event.pointerId !== pointerId || frame === void 0) return;
18959
- const delta = event.clientX - dragStartX;
18960
- if (!dragged && Math.abs(delta) < DRAG_THRESHOLD) return;
18961
- dragged = true;
18962
- width = clampDetailsWidth(dragStartWidth - delta, frame.getBoundingClientRect().width);
18963
- applyWidth();
18964
- event.preventDefault();
18965
- };
18966
- const onPointerEnd = (event) => {
18967
- if (event.pointerId !== pointerId) return;
18968
- finishDrag();
18969
- };
18970
- const onPointerDown = (event) => {
18971
- const target = event.target;
18972
- if (!(target instanceof Element)) return;
18973
- const candidate = target.closest("[data-side=\"details\"]");
18974
- const candidateFrame = candidate?.parentElement;
18975
- if (candidate === null || candidate === void 0 || candidateFrame === null || candidateFrame === void 0) return;
18976
- const currentFrame = candidateFrame;
18977
- const currentHandle = candidate;
18978
- const detailsColumn = currentFrame.children.item(2);
18979
- const measuredWidth = detailsColumn instanceof HTMLElement ? detailsColumn.getBoundingClientRect().width : 0;
18980
- const frameWidth = currentFrame.getBoundingClientRect().width;
18981
- if (measuredWidth <= 0 || frameWidth <= 0) return;
18982
- const firstDrag = frame === void 0;
18983
- frame = currentFrame;
18984
- handle = currentHandle;
18985
- width = clampDetailsWidth(width ?? measuredWidth, frameWidth);
18986
- dragStartWidth = width;
18987
- dragStartX = event.clientX;
18988
- pointerId = event.pointerId;
18989
- dragged = false;
18990
- if (firstDrag) {
18991
- originalGrid = currentFrame.style.gridTemplateColumns;
18992
- originalHandleLeft = currentHandle.style.left;
18993
- frameWasDragging = currentFrame.hasAttribute("data-dragging");
18994
- handleWasDragging = currentHandle.hasAttribute("data-dragging");
18995
- }
18996
- currentFrame.setAttribute("data-dragging", "");
18997
- currentHandle.setAttribute("data-dragging", "true");
18998
- if (firstDrag) {
18999
- resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleApply);
19000
- resizeObserver?.observe(currentFrame);
19001
- mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(scheduleApply);
19002
- mutationObserver?.observe(currentFrame, {
19003
- attributes: true,
19004
- attributeFilter: ["style"]
19005
- });
19006
- mutationObserver?.observe(currentHandle, {
19007
- attributes: true,
19008
- attributeFilter: ["style"]
19009
- });
19010
- }
19011
- event.preventDefault();
19012
- event.stopPropagation();
19013
- event.stopImmediatePropagation();
19014
- };
19015
- const applyDefaultWidth = () => {
19016
- const initialHandle = document.querySelector("[data-side=\"details\"]");
19017
- const initialFrame = initialHandle?.parentElement;
19018
- if (initialHandle === null || initialHandle === void 0 || initialFrame === null || initialFrame === void 0) return;
19019
- frame = initialFrame;
19020
- handle = initialHandle;
19021
- originalGrid = frame.style.gridTemplateColumns;
19022
- originalHandleLeft = handle.style.left;
19023
- frameWasDragging = frame.hasAttribute("data-dragging");
19024
- handleWasDragging = handle.hasAttribute("data-dragging");
19025
- width = defaultDetailsWidth(frame.getBoundingClientRect().width);
19026
- resizeObserver = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(scheduleApply);
19027
- resizeObserver?.observe(frame);
19028
- mutationObserver = typeof MutationObserver === "undefined" ? void 0 : new MutationObserver(scheduleApply);
19029
- mutationObserver?.observe(frame, {
19030
- attributes: true,
19031
- attributeFilter: ["style"]
19032
- });
19033
- mutationObserver?.observe(handle, {
19034
- attributes: true,
19035
- attributeFilter: ["style"]
19036
- });
19037
- applyWidth();
19038
- };
19039
- animationFrame = window.requestAnimationFrame(() => {
19040
- animationFrame = void 0;
19041
- applyDefaultWidth();
19042
- });
19043
- document.addEventListener("pointerdown", onPointerDown, true);
19044
- window.addEventListener("pointermove", onPointerMove, true);
19045
- window.addEventListener("pointerup", onPointerEnd, true);
19046
- window.addEventListener("pointercancel", onPointerEnd, true);
19047
- return () => {
19048
- document.removeEventListener("pointerdown", onPointerDown, true);
19049
- window.removeEventListener("pointermove", onPointerMove, true);
19050
- window.removeEventListener("pointerup", onPointerEnd, true);
19051
- window.removeEventListener("pointercancel", onPointerEnd, true);
19052
- resizeObserver?.disconnect();
19053
- mutationObserver?.disconnect();
19054
- if (animationFrame !== void 0) window.cancelAnimationFrame(animationFrame);
19055
- finishDrag();
19056
- if (frame !== void 0) frame.style.gridTemplateColumns = originalGrid;
19057
- if (handle !== void 0) handle.style.left = originalHandleLeft;
19058
- };
19059
- }
19060
- //#endregion
19061
18829
  //#region src/client/locales.ts
19062
18830
  const zh = {
19063
18831
  nav: "Claude Code",
@@ -19250,6 +19018,7 @@ window.__ModuleLoader__.load({
19250
19018
  yes: "是",
19251
19019
  no: "否",
19252
19020
  diffOpen: "查看分支改动",
19021
+ diffTabTitle: "分支改动",
19253
19022
  planPanelTitle: "Plan",
19254
19023
  planClose: "关闭方案面板",
19255
19024
  planOpen: "查看方案",
@@ -19272,9 +19041,6 @@ window.__ModuleLoader__.load({
19272
19041
  planEmpty: "本会话还没有待查看的方案。",
19273
19042
  planPendingHint: "在 DSH 的审批弹框里批准或拒绝这个方案。",
19274
19043
  diffClose: "关闭 Diff 面板",
19275
- sessionMenu: "会话菜单",
19276
- sessionMenuOpenIn: "打开方式",
19277
- sessionMenuOpenFailed: "无法打开:{message}",
19278
19044
  promptSource: "常用提示词",
19279
19045
  promptSave: "存为常用提示词",
19280
19046
  promptSaveName: "名字",
@@ -19296,6 +19062,7 @@ window.__ModuleLoader__.load({
19296
19062
  diffFilesShort: "{count} 个文件",
19297
19063
  diffTruncated: "Diff 超出安全显示上限。请在终端中查看完整内容。",
19298
19064
  diffEmpty: "没有可显示的 tracked 文件改动",
19065
+ diffElided: "{count} 个文件过大未展示:{files}",
19299
19066
  diffMaximize: "最大化 Diff 面板",
19300
19067
  diffCommentPrevious: "上一条评论",
19301
19068
  diffCommentNext: "下一条评论",
@@ -19694,6 +19461,7 @@ window.__ModuleLoader__.load({
19694
19461
  yes: "Yes",
19695
19462
  no: "No",
19696
19463
  diffOpen: "View branch changes",
19464
+ diffTabTitle: "Branch changes",
19697
19465
  planPanelTitle: "Plan",
19698
19466
  planClose: "Close plan panel",
19699
19467
  planOpen: "View plan",
@@ -19716,9 +19484,6 @@ window.__ModuleLoader__.load({
19716
19484
  planEmpty: "This session has no plan to read yet.",
19717
19485
  planPendingHint: "Approve or reject this plan in the DSH approval dialog.",
19718
19486
  diffClose: "Close diff panel",
19719
- sessionMenu: "Session menu",
19720
- sessionMenuOpenIn: "Open in",
19721
- sessionMenuOpenFailed: "Could not open: {message}",
19722
19487
  promptSource: "Prompts",
19723
19488
  promptSave: "Save as a prompt",
19724
19489
  promptSaveName: "Name",
@@ -19740,6 +19505,7 @@ window.__ModuleLoader__.load({
19740
19505
  diffFilesShort: "{count} files",
19741
19506
  diffTruncated: "The diff exceeds the safe display limit. Use the terminal to inspect the complete content.",
19742
19507
  diffEmpty: "No tracked file changes to display",
19508
+ diffElided: "Skipped {count} oversized file(s): {files}",
19743
19509
  diffMaximize: "Maximize diff panel",
19744
19510
  diffCommentPrevious: "Previous comment",
19745
19511
  diffCommentNext: "Next comment",
@@ -19949,39 +19715,7 @@ window.__ModuleLoader__.load({
19949
19715
  };
19950
19716
  //#endregion
19951
19717
  //#region src/client/index.tsx
19952
- function MaximizedDiff({ source, t, sessionId, closeDetails, restore, submitPrompt, initialRoot }) {
19953
- const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
19954
- const useClaudeProjection = (selector) => selector(snapshot);
19955
- return /* @__PURE__ */ jsx(ClaudePanelOverlay, {
19956
- onRestore: restore,
19957
- children: /* @__PURE__ */ jsx(ClaudeDiffPanel, {
19958
- useClaudeProjection,
19959
- t,
19960
- sessionId,
19961
- maximized: true,
19962
- closeDetails,
19963
- toggleMaximized: restore,
19964
- ...submitPrompt === void 0 ? {} : { submitPrompt },
19965
- ...initialRoot === void 0 ? {} : { initialRoot }
19966
- })
19967
- });
19968
- }
19969
19718
  const name = "dsh-claude-client";
19970
- function MaximizedPlan({ source, t, sessionId, closeDetails, restore }) {
19971
- const snapshot = useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot);
19972
- const useClaudeProjection = (selector) => selector(snapshot);
19973
- return /* @__PURE__ */ jsx(ClaudePanelOverlay, {
19974
- onRestore: restore,
19975
- children: /* @__PURE__ */ jsx(ClaudePlanPanel, {
19976
- useClaudeProjection,
19977
- t,
19978
- sessionId,
19979
- maximized: true,
19980
- closeDetails,
19981
- toggleMaximized: restore
19982
- })
19983
- });
19984
- }
19985
19719
  const inject = [
19986
19720
  "slots",
19987
19721
  "locale",
@@ -19993,7 +19727,9 @@ window.__ModuleLoader__.load({
19993
19727
  "workspaces",
19994
19728
  "inputTriggers",
19995
19729
  "conversation",
19996
- "connection"
19730
+ "connection",
19731
+ "sidebarRight",
19732
+ "sidebarRightTabs"
19997
19733
  ];
19998
19734
  /** Resolve one session's composer facade.
19999
19735
  *
@@ -20093,224 +19829,39 @@ window.__ModuleLoader__.load({
20093
19829
  key: "claude-activity-step",
20094
19830
  locale: namespace
20095
19831
  }, ClaudeActivityNode));
20096
- const layout = ctx.get("layout");
20097
- let disposePluginDetails;
20098
- let disposeDiffOverlay;
20099
- let disposePlanOverlay;
20100
- let disposeExpandedDetailsResize;
20101
- let detailsSessionId;
20102
- const diffOpen = new PanelOpenStore();
20103
- const planOpen = new PanelOpenStore();
20104
- const restoreDiff = () => {
20105
- if (disposeDiffOverlay === void 0) return;
20106
- disposeDiffOverlay();
20107
- disposeDiffOverlay = void 0;
20108
- layout?.openDetails();
20109
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
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 });
20110
19852
  };
20111
- const restorePlan = () => {
20112
- if (disposePlanOverlay === void 0) return;
20113
- disposePlanOverlay();
20114
- disposePlanOverlay = void 0;
20115
- layout?.openDetails();
20116
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
19853
+ const openOverviewPanel = (sessionId) => {
19854
+ sidebarTabs.open(CLAUDE_TAB_KINDS.overview, sessionId, {});
20117
19855
  };
20118
- const closePluginDetails = () => {
20119
- if (disposePluginDetails === void 0 && disposeDiffOverlay === void 0 && disposePlanOverlay === void 0 && disposeExpandedDetailsResize === void 0 && detailsSessionId === void 0) return;
20120
- disposeDiffOverlay?.();
20121
- disposeDiffOverlay = void 0;
20122
- disposePlanOverlay?.();
20123
- disposePlanOverlay = void 0;
20124
- disposeExpandedDetailsResize?.();
20125
- disposeExpandedDetailsResize = void 0;
20126
- disposePluginDetails?.();
20127
- disposePluginDetails = void 0;
20128
- detailsSessionId = void 0;
20129
- diffOpen.close();
20130
- planOpen.close();
20131
- layout?.closeDetails();
19856
+ const openDiffPanel = (sessionId, initialRoot) => {
19857
+ sidebarTabs.open(CLAUDE_TAB_KINDS.diff, sessionId, initialRoot === void 0 ? {} : { initialRoot });
20132
19858
  };
20133
19859
  ctx.effect(() => ctx.slots.onEntryError((key, entry, error) => {
20134
19860
  const id = entry.options.id === void 0 ? "" : ` id="${String(entry.options.id)}"`;
20135
19861
  const message = error instanceof Error ? `${error.message}
20136
19862
  ${error.stack ?? ""}` : String(error);
20137
19863
  diagnostics.report("slot-entry-crashed", `slot "${key}"${id}: ${message}`);
20138
- if (key === "shell.overlay" && entry.options.id === "claude-diff-overlay") restoreDiff();
20139
- if (key === "shell.overlay" && entry.options.id === "claude-plan-overlay") restorePlan();
20140
19864
  }), "dsh-claude: Slot entry failure reporting");
20141
- const openTasksPanel = (sessionId, turn) => {
20142
- closePluginDetails();
20143
- try {
20144
- disposePluginDetails = ctx.slots.register({
20145
- name: "details",
20146
- priority: -10,
20147
- locale: namespace,
20148
- inject: () => ({
20149
- t,
20150
- turn,
20151
- closeDetails: closePluginDetails
20152
- })
20153
- }, ClaudeTasksPanel);
20154
- } catch {
20155
- return;
20156
- }
20157
- detailsSessionId = sessionId;
20158
- layout?.openDetails();
20159
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20160
- };
20161
- const openPlanPanel = (sessionId) => {
20162
- closePluginDetails();
20163
- const maximizePlan = () => {
20164
- if (disposePlanOverlay !== void 0) {
20165
- restorePlan();
20166
- return;
20167
- }
20168
- disposeExpandedDetailsResize?.();
20169
- disposeExpandedDetailsResize = void 0;
20170
- layout?.closeDetails();
20171
- try {
20172
- disposePlanOverlay = ctx.slots.register({
20173
- name: "shell.overlay",
20174
- id: "claude-plan-overlay",
20175
- locale: namespace
20176
- }, () => /* @__PURE__ */ jsx(MaximizedPlan, {
20177
- source: projections.source(sessionId),
20178
- t,
20179
- sessionId,
20180
- closeDetails: closePluginDetails,
20181
- restore: restorePlan
20182
- }));
20183
- } catch {
20184
- disposePlanOverlay = void 0;
20185
- layout?.openDetails();
20186
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20187
- }
20188
- };
20189
- try {
20190
- disposePluginDetails = ctx.slots.register({
20191
- name: "details",
20192
- priority: -10,
20193
- locale: namespace,
20194
- inject: () => ({
20195
- t,
20196
- sessionId,
20197
- closeDetails: closePluginDetails,
20198
- maximized: false,
20199
- toggleMaximized: maximizePlan
20200
- })
20201
- }, ClaudePlanPanel);
20202
- } catch {
20203
- return;
20204
- }
20205
- detailsSessionId = sessionId;
20206
- planOpen.open(sessionId);
20207
- layout?.openDetails();
20208
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20209
- };
20210
- const openOverviewPanel = (sessionId) => {
20211
- if (sessions === void 0) return;
20212
- closePluginDetails();
20213
- try {
20214
- disposePluginDetails = ctx.slots.register({
20215
- name: "details",
20216
- priority: -10,
20217
- locale: namespace,
20218
- inject: () => ({
20219
- t,
20220
- closeDetails: closePluginDetails,
20221
- openSession: (id) => {
20222
- sessions.open(id);
20223
- },
20224
- loadStatus: loadRepositoryStatusFor,
20225
- sessions: sessions.list,
20226
- ...workspaces === void 0 ? {} : { workspaces: workspaces.list },
20227
- projectionFor: (id) => projections.source(id)
20228
- })
20229
- }, ClaudePullRequestsPanel);
20230
- } catch {
20231
- return;
20232
- }
20233
- detailsSessionId = sessionId;
20234
- layout?.openDetails();
20235
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20236
- };
20237
- const openDiffPanel = (sessionId, initialRoot) => {
20238
- closePluginDetails();
20239
- detailsSessionId = sessionId;
20240
- const submitPrompt = submitPromptFor(sessionId);
20241
- const registerDetails = () => {
20242
- try {
20243
- disposePluginDetails = ctx.slots.register({
20244
- name: "details",
20245
- priority: -10,
20246
- locale: namespace,
20247
- inject: () => ({
20248
- t,
20249
- sessionId,
20250
- maximized: false,
20251
- closeDetails: closePluginDetails,
20252
- toggleMaximized: maximizeDiff,
20253
- ...submitPrompt === void 0 ? {} : { submitPrompt },
20254
- ...initialRoot === void 0 ? {} : { initialRoot }
20255
- })
20256
- }, ClaudeDiffPanel);
20257
- } catch {
20258
- disposePluginDetails = void 0;
20259
- return false;
20260
- }
20261
- layout?.openDetails();
20262
- return true;
20263
- };
20264
- const maximizeDiff = () => {
20265
- if (disposeDiffOverlay !== void 0) {
20266
- restoreDiff();
20267
- return;
20268
- }
20269
- disposeExpandedDetailsResize?.();
20270
- disposeExpandedDetailsResize = void 0;
20271
- layout?.closeDetails();
20272
- try {
20273
- disposeDiffOverlay = ctx.slots.register({
20274
- name: "shell.overlay",
20275
- id: "claude-diff-overlay",
20276
- locale: namespace
20277
- }, () => /* @__PURE__ */ jsx(MaximizedDiff, {
20278
- source: projections.source(sessionId),
20279
- t,
20280
- sessionId,
20281
- closeDetails: closePluginDetails,
20282
- restore: restoreDiff,
20283
- ...submitPrompt === void 0 ? {} : { submitPrompt },
20284
- ...initialRoot === void 0 ? {} : { initialRoot }
20285
- }));
20286
- } catch {
20287
- disposeDiffOverlay = void 0;
20288
- layout?.openDetails();
20289
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20290
- }
20291
- };
20292
- if (!registerDetails()) {
20293
- detailsSessionId = void 0;
20294
- return;
20295
- }
20296
- diffOpen.open(sessionId);
20297
- disposeExpandedDetailsResize = enableExpandedDetailsResize();
20298
- };
20299
- ctx.effect(() => {
20300
- if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => closePluginDetails();
20301
- const observer = new MutationObserver(() => {
20302
- if (detailsSessionId !== void 0 && disposeDiffOverlay === void 0 && disposePlanOverlay === void 0 && document.querySelector("[data-details-collapsed]") !== null) closePluginDetails();
20303
- });
20304
- observer.observe(document.body, {
20305
- attributes: true,
20306
- attributeFilter: ["data-details-collapsed"],
20307
- subtree: true
20308
- });
20309
- return () => {
20310
- observer.disconnect();
20311
- closePluginDetails();
20312
- };
20313
- }, "dsh-claude: details panel lifecycle");
20314
19865
  ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
20315
19866
  name: "conversation.chat.node",
20316
19867
  key: "claude-active-tasks",
@@ -20353,10 +19904,9 @@ window.__ModuleLoader__.load({
20353
19904
  inject: (sessionId) => ({
20354
19905
  t,
20355
19906
  togglePlan: () => {
20356
- if (planOpen.isOpen(sessionId)) closePluginDetails();
20357
- else openPlanPanel(sessionId);
19907
+ sidebarTabs.toggle(CLAUDE_TAB_KINDS.plan, sessionId, {});
20358
19908
  },
20359
- planOpen: planOpen.sourceFor(sessionId)
19909
+ planOpen: sidebarTabs.sourceFor(CLAUDE_TAB_KINDS.plan, sessionId)
20360
19910
  })
20361
19911
  }, ClaudePlanHeaderAction));
20362
19912
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
@@ -20367,19 +19917,11 @@ window.__ModuleLoader__.load({
20367
19917
  inject: (sessionId) => ({
20368
19918
  t,
20369
19919
  toggleDiff: () => {
20370
- if (diffOpen.isOpen(sessionId)) closePluginDetails();
20371
- else openDiffPanel(sessionId);
19920
+ sidebarTabs.toggle(CLAUDE_TAB_KINDS.diff, sessionId, {});
20372
19921
  },
20373
- diffOpen: diffOpen.sourceFor(sessionId)
19922
+ diffOpen: sidebarTabs.sourceFor(CLAUDE_TAB_KINDS.diff, sessionId)
20374
19923
  })
20375
19924
  }, ClaudeDiffHeaderAction));
20376
- ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
20377
- name: "conversation.session.header.utilities",
20378
- id: "claude-session-menu",
20379
- order: 31,
20380
- locale: namespace,
20381
- inject: () => ({ t })
20382
- }, ClaudeSessionMenu));
20383
19925
  ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
20384
19926
  name: "conversation.input.left",
20385
19927
  id: "claude-prompt-save",
@@ -20567,7 +20109,7 @@ window.__ModuleLoader__.load({
20567
20109
  const sourceInput = sessionInput(conversation, sourceScope);
20568
20110
  const rawDraft = sourceInput.state.getSnapshot().draft;
20569
20111
  const draft = ticket === void 0 ? rawDraft : rawDraft.trim() === "" ? ticketPrompt(ticket) : `${rawDraft.trimEnd()}\n\n${ticketContext(ticket)}`;
20570
- const imageIds = sourceInput.state.getSnapshot().imageIds;
20112
+ const imageIds = sourceInput.state.getSnapshot().attachmentIds;
20571
20113
  const prepared = await prepareRepository(cwd, branch, useWorktree, ticket?.key, onProgress, ticket === void 0 ? draft : void 0);
20572
20114
  if (ticket !== void 0) assignJiraTicket(ticket.key).catch((reason) => {
20573
20115
  console.warn(`dsh-claude: could not assign ${ticket.key}: ${reason instanceof Error ? reason.message : String(reason)}`);
@@ -20588,14 +20130,14 @@ window.__ModuleLoader__.load({
20588
20130
  sessions.noteAgentPreset?.(targetSessionId, presetResponse.value);
20589
20131
  const targetInput = sessionInput(conversation, targetScope);
20590
20132
  onProgress("transferring-draft");
20591
- 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"));
20592
20134
  if (draft !== "") targetInput.setDraft(draft);
20593
20135
  bindLease(prepared.leaseId, targetSessionId);
20594
20136
  sessions.open(targetSessionId);
20595
20137
  onProgress("submitting");
20596
20138
  targetInput.submit();
20597
20139
  sourceInput.setDraft("");
20598
- for (const imageId of imageIds) sourceInput.removeImage(imageId);
20140
+ for (const imageId of imageIds) sourceInput.removeAttachment(imageId);
20599
20141
  },
20600
20142
  prepareMany: async (cwd, branch, tickets, onProgress) => {
20601
20143
  const sourceScope = sessions.scope(sourceSessionId);
@@ -20643,9 +20185,6 @@ window.__ModuleLoader__.load({
20643
20185
  })
20644
20186
  }, ClaudeHeroRepositoryControls));
20645
20187
  }
20646
- if (sessions !== void 0) ctx.effect(() => sessions.list.subscribe(() => {
20647
- if (detailsSessionId !== void 0 && sessions.list.getSnapshot().current !== detailsSessionId) closePluginDetails();
20648
- }), "dsh-claude: details panel session tracking");
20649
20188
  ctx.slots.inject("settings.section", () => ctx.slots.register({
20650
20189
  name: "settings.section",
20651
20190
  id: "claude-code",