@norman-else/dsh-claude 0.1.27 → 0.1.29

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 { DiffBlock, DisclosureRow, HoverCard, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconCheckOutline16, IconChevronDownOutline14, IconChevronUpOutline14, IconCloseOutline16, IconEditOutline16, IconFullscreenOutline16, IconQueueOutline14, IconSearchOutline16, IconSendOutline14, IconThinkOutline14, IconTrashOutline16, MarkdownText, Menu, Modal, StateDot, Tooltip } = require("@deepseek-ai/dsh-client-ui-primitives");
7
+ var { DiffBlock, DisclosureRow, HoverCard, IconAgentPresetOutline16, IconApiOutline14, IconBranchOutline16, IconCheckOutline14, IconCheckOutline16, IconChevronDownOutline14, IconChevronUpOutline14, IconCloseOutline16, IconEditOutline16, IconFullscreenOutline16, IconQueueOutline14, IconSearchOutline16, IconSendOutline14, IconThinkOutline14, IconTrashOutline16, MarkdownText, Menu, Modal, StateDot, Tooltip } = 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
@@ -2834,19 +2834,21 @@ window.__ModuleLoader__.load({
2834
2834
  background: var(--dsw-alias-bg-layer-2);
2835
2835
  }
2836
2836
  .dshClaudeRepositoryActionModalContent h2 {
2837
- font-size: 24px;
2838
- line-height: 32px;
2837
+ font-size: 18px;
2838
+ line-height: 26px;
2839
2839
  }
2840
2840
  .dshClaudeRepositoryActionModalContent > p {
2841
- font-size: 18px;
2842
- line-height: 28px;
2841
+ font-size: 14px;
2842
+ line-height: 22px;
2843
2843
  }
2844
- .dshClaudeRepositoryActionModal input[type='checkbox'] {
2845
- width: 18px;
2846
- height: 18px;
2844
+ .dshClaudeRepositoryActionModal input:is([type='checkbox'], [type='radio']) {
2845
+ width: 16px;
2846
+ height: 16px;
2847
+ margin: 0;
2848
+ accent-color: var(--dsw-static-blue-450);
2847
2849
  }
2848
2850
  .dshClaudeRepositoryActionModal textarea:focus,
2849
- .dshClaudeRepositoryActionModal input:not([type='checkbox']):focus {
2851
+ .dshClaudeRepositoryActionModal input:not([type='checkbox'], [type='radio']):focus {
2850
2852
  outline: none;
2851
2853
  box-shadow: inset 0 0 0 1px var(--dsw-static-blue-450);
2852
2854
  }
@@ -2865,8 +2867,8 @@ window.__ModuleLoader__.load({
2865
2867
  gap: 16,
2866
2868
  minWidth: 0,
2867
2869
  color: "var(--dsw-alias-label-secondary)",
2868
- fontSize: 16,
2869
- lineHeight: "24px"
2870
+ fontSize: 14,
2871
+ lineHeight: "22px"
2870
2872
  };
2871
2873
  const diffModalMetaText = {
2872
2874
  minWidth: 0,
@@ -2890,8 +2892,8 @@ window.__ModuleLoader__.load({
2890
2892
  padding: "10px 14px",
2891
2893
  borderBottom: "1px solid var(--dsw-alias-border-l2, color-mix(in srgb, currentColor 16%, transparent))",
2892
2894
  color: "var(--dsw-alias-label-secondary)",
2893
- fontSize: 15,
2894
- lineHeight: "22px"
2895
+ fontSize: 13,
2896
+ lineHeight: "20px"
2895
2897
  };
2896
2898
  const diffModalFilePath = {
2897
2899
  minWidth: 0,
@@ -2905,32 +2907,32 @@ window.__ModuleLoader__.load({
2905
2907
  flexDirection: "column",
2906
2908
  gap: 8,
2907
2909
  color: "var(--dsw-alias-label-secondary)",
2908
- fontSize: 16,
2909
- lineHeight: "24px",
2910
+ fontSize: 14,
2911
+ lineHeight: "22px",
2910
2912
  fontWeight: 550
2911
2913
  };
2912
2914
  const diffModalTextarea = {
2913
2915
  ...settingTextInput,
2914
2916
  minHeight: 140,
2915
2917
  padding: "10px 14px",
2916
- fontSize: 16,
2917
- lineHeight: "24px",
2918
+ fontSize: 14,
2919
+ lineHeight: "22px",
2918
2920
  resize: "vertical"
2919
2921
  };
2920
2922
  const diffModalTextInput = {
2921
2923
  ...settingTextInput,
2922
2924
  minHeight: 44,
2923
2925
  padding: "9px 14px",
2924
- fontSize: 16,
2925
- lineHeight: "24px"
2926
+ fontSize: 14,
2927
+ lineHeight: "22px"
2926
2928
  };
2927
2929
  const diffModalCheckbox = {
2928
2930
  display: "flex",
2929
2931
  alignItems: "center",
2930
2932
  gap: 10,
2931
2933
  color: "var(--dsw-alias-label-secondary)",
2932
- fontSize: 16,
2933
- lineHeight: "24px"
2934
+ fontSize: 14,
2935
+ lineHeight: "22px"
2934
2936
  };
2935
2937
  const diffModalFooter = {
2936
2938
  display: "flex",
@@ -2938,28 +2940,28 @@ window.__ModuleLoader__.load({
2938
2940
  gap: 12
2939
2941
  };
2940
2942
  const diffModalButton = {
2941
- minHeight: 42,
2943
+ minHeight: 36,
2942
2944
  padding: "8px 18px",
2943
- fontSize: 16,
2944
- lineHeight: "24px"
2945
+ fontSize: 14,
2946
+ lineHeight: "22px"
2945
2947
  };
2946
2948
  const diffModalStatus = {
2947
2949
  margin: 0,
2948
2950
  color: "var(--dsw-alias-label-secondary)",
2949
- fontSize: 16,
2950
- lineHeight: "24px"
2951
+ fontSize: 14,
2952
+ lineHeight: "22px"
2951
2953
  };
2952
2954
  const diffModalError = {
2953
2955
  margin: "10px 0 0",
2954
2956
  color: "var(--dsw-alias-state-error-primary)",
2955
- fontSize: 16,
2956
- lineHeight: "24px"
2957
+ fontSize: 14,
2958
+ lineHeight: "22px"
2957
2959
  };
2958
2960
  const diffModalSuccess = {
2959
2961
  margin: 0,
2960
2962
  color: "var(--dsw-alias-state-success-primary)",
2961
- fontSize: 16,
2962
- lineHeight: "24px"
2963
+ fontSize: 14,
2964
+ lineHeight: "22px"
2963
2965
  };
2964
2966
  //#endregion
2965
2967
  //#region src/client/token-format.ts
@@ -3855,10 +3857,10 @@ window.__ModuleLoader__.load({
3855
3857
  ".dsh-claude-act-running{animation:dsh-claude-act-pulse 1.2s ease-in-out infinite}",
3856
3858
  "@keyframes dsh-claude-act-pulse{0%,100%{opacity:1}50%{opacity:.3}}"
3857
3859
  ].join("");
3858
- let cssInjected = false;
3859
- function ensureCss() {
3860
- if (cssInjected || typeof document === "undefined") return;
3861
- cssInjected = true;
3860
+ let cssInjected$2 = false;
3861
+ function ensureCss$2() {
3862
+ if (cssInjected$2 || typeof document === "undefined") return;
3863
+ cssInjected$2 = true;
3862
3864
  const element = document.createElement("style");
3863
3865
  element.dataset.dshClaudeActivity = "";
3864
3866
  element.textContent = ACTIVITY_CSS;
@@ -4183,7 +4185,7 @@ window.__ModuleLoader__.load({
4183
4185
  });
4184
4186
  }
4185
4187
  function ClaudeActivityNode({ node, useClaudeProjection, t }) {
4186
- ensureCss();
4188
+ ensureCss$2();
4187
4189
  const marker = node.data;
4188
4190
  const activities = useClaudeProjection((value) => selectStepActivities(value, marker.turn, marker.step));
4189
4191
  const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS);
@@ -5412,8 +5414,10 @@ window.__ModuleLoader__.load({
5412
5414
  }).join("\n\n")}`;
5413
5415
  }
5414
5416
  /** Draft handed to Claude after an update-branch merge left conflicts behind. */
5415
- function composeConflictsPrompt(baseBranch, conflicts) {
5416
- return `Merging origin/${baseBranch} into the current branch left merge conflicts in the files below. Resolve each conflict preserving the intent of both sides, then commit the merge.\n\n${conflicts.map((file) => `- ${file}`).join("\n")}`;
5417
+ function composeConflictsPrompt(baseBranch, conflicts, method = "merge") {
5418
+ const list = conflicts.map((file) => `- ${file}`).join("\n");
5419
+ if (method === "rebase") return `Rebasing the current branch onto origin/${baseBranch} stopped on conflicts in the files below. Resolve each conflict preserving the intent of both sides, stage the files, run \`git rebase --continue\` until the rebase finishes, then push with \`git push --force-with-lease\`.\n\n${list}`;
5420
+ return `Merging origin/${baseBranch} into the current branch left merge conflicts in the files below. Resolve each conflict preserving the intent of both sides, then commit the merge.\n\n${list}`;
5417
5421
  }
5418
5422
  //#endregion
5419
5423
  //#region src/client/auto-fix.ts
@@ -5908,6 +5912,7 @@ window.__ModuleLoader__.load({
5908
5912
  }
5909
5913
  function UpdateBranchControl({ sessionId, repository, t, submitPrompt }) {
5910
5914
  const [dialog, setDialog] = useState();
5915
+ const [method, setMethod] = useState("rebase");
5911
5916
  const controller = useRef();
5912
5917
  const pullRequest = repository.pullRequest;
5913
5918
  const base = pullRequest?.baseBranch;
@@ -5959,7 +5964,8 @@ window.__ModuleLoader__.load({
5959
5964
  fingerprint: dialog.fingerprint,
5960
5965
  message: "",
5961
5966
  includeUnstaged: false,
5962
- baseBranch: base
5967
+ baseBranch: base,
5968
+ mergeMethod: method
5963
5969
  }).then((result) => {
5964
5970
  setDialog({
5965
5971
  ...pending,
@@ -6042,6 +6048,17 @@ window.__ModuleLoader__.load({
6042
6048
  })
6043
6049
  })]
6044
6050
  }),
6051
+ ["rebase", "merge"].map((option) => /* @__PURE__ */ jsxs("label", {
6052
+ style: diffModalCheckbox,
6053
+ children: [/* @__PURE__ */ jsx("input", {
6054
+ type: "radio",
6055
+ name: "dsh-claude-update-branch-method",
6056
+ value: option,
6057
+ checked: method === option,
6058
+ disabled: settled || dialog.submitting,
6059
+ onChange: () => setMethod(option)
6060
+ }), t(`diffUpdateBranch_${option}`, { base })]
6061
+ }, option)),
6045
6062
  dialog.pushed === void 0 ? null : /* @__PURE__ */ jsx("p", {
6046
6063
  style: diffModalSuccess,
6047
6064
  children: t("diffUpdateBranchCompleted", { commit: dialog.pushed.slice(0, 8) })
@@ -6059,7 +6076,7 @@ window.__ModuleLoader__.load({
6059
6076
  type: "button",
6060
6077
  style: diffModalConflictResolve,
6061
6078
  onClick: () => {
6062
- submitPrompt(composeConflictsPrompt(base, dialog.conflicts ?? []));
6079
+ submitPrompt(composeConflictsPrompt(base, dialog.conflicts ?? [], method));
6063
6080
  closeDialog();
6064
6081
  },
6065
6082
  children: t("diffUpdateBranchResolve")
@@ -9543,6 +9560,274 @@ window.__ModuleLoader__.load({
9543
9560
  }), portal);
9544
9561
  }
9545
9562
  //#endregion
9563
+ //#region src/client/ClaudeDiffHeaderAction.tsx
9564
+ /** Quiet by default, lit on hover, press, keyboard focus, and for as long as
9565
+ * the panel it opened is on screen. The header action row sits on the app
9566
+ * background, so a resting background would read as a filled control. */
9567
+ const ACTION_CSS = [
9568
+ ".dsh-claude-header-diff{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;",
9569
+ "min-width:32px;height:32px;padding:0;border:0;border-radius:9px;background:transparent;",
9570
+ "color:var(--dsw-alias-label-secondary);cursor:pointer;",
9571
+ "font-family:var(--dsw-font-family);font-size:12px;line-height:20px;font-weight:650;",
9572
+ "transition:background .12s ease,color .12s ease}",
9573
+ ".dsh-claude-header-diff[data-counts]{padding:0 10px 0 8px}",
9574
+ ".dsh-claude-header-diff:hover,.dsh-claude-header-diff:focus-visible,.dsh-claude-header-diff[aria-pressed=\"true\"]{",
9575
+ "background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
9576
+ ".dsh-claude-header-diff:active{background:var(--dsw-alias-interactive-bg-active)}",
9577
+ ".dsh-claude-header-diff:focus-visible{outline:none}",
9578
+ ".dsh-claude-header-diff>*{flex:none}",
9579
+ ".dsh-claude-header-diff>svg.dsh-claude-header-diff-glyph{",
9580
+ "width:18px;height:18px;display:block;overflow:visible;opacity:1;",
9581
+ "fill:none;stroke:currentColor;stroke-width:2.1;stroke-linecap:round}",
9582
+ ".dsh-claude-header-diff-add,.dsh-claude-header-diff-del{line-height:1}",
9583
+ ".dsh-claude-header-diff-add{color:var(--dsw-alias-state-success-primary)}",
9584
+ ".dsh-claude-header-diff-del{color:var(--dsw-alias-state-error-primary)}"
9585
+ ].join("");
9586
+ let cssInjected$1 = false;
9587
+ function ensureCss$1() {
9588
+ if (cssInjected$1 || typeof document === "undefined") return;
9589
+ cssInjected$1 = true;
9590
+ const element = document.createElement("style");
9591
+ element.dataset.dshClaudeHeaderDiff = "";
9592
+ element.textContent = ACTION_CSS;
9593
+ document.head.appendChild(element);
9594
+ }
9595
+ /** Plus over minus: the diff glyph. Drawn inline because the primitives icon
9596
+ * set ships no diff icon.
9597
+ *
9598
+ * The ink spans y 3.5 to 14.5 — symmetric about the box's own centre line, so
9599
+ * `align-items:center` lands the drawn shape on the centre rather than the
9600
+ * bounding box that happens to contain it. */
9601
+ function DiffGlyph() {
9602
+ return /* @__PURE__ */ jsx("svg", {
9603
+ className: "dsh-claude-header-diff-glyph",
9604
+ width: "18",
9605
+ height: "18",
9606
+ viewBox: "0 0 18 18",
9607
+ fill: "none",
9608
+ "aria-hidden": "true",
9609
+ focusable: "false",
9610
+ children: /* @__PURE__ */ jsx("path", {
9611
+ d: "M9 3.5v5.5M6.25 6.25h5.5M6.25 14.5h5.5",
9612
+ stroke: "currentColor",
9613
+ strokeWidth: "2.1",
9614
+ strokeLinecap: "round"
9615
+ })
9616
+ });
9617
+ }
9618
+ function ClaudeDiffHeaderAction({ t, sessionId, toggleDiff, diffOpen, useClaudeProjection }) {
9619
+ const owned = useClaudeProjection((projection) => projection.owned);
9620
+ const additions = useClaudeProjection((projection) => projection.repository?.diff?.additions ?? 0);
9621
+ const deletions = useClaudeProjection((projection) => projection.repository?.diff?.deletions ?? 0);
9622
+ const open = useSyncExternalStore(diffOpen.subscribe, diffOpen.getSnapshot, diffOpen.getSnapshot);
9623
+ if (!owned) return null;
9624
+ ensureCss$1();
9625
+ const hasCounts = additions > 0 || deletions > 0;
9626
+ const label = t(open ? "diffClose" : "diffOpen");
9627
+ return /* @__PURE__ */ jsx(Tooltip, {
9628
+ label,
9629
+ side: "bottom",
9630
+ delayMs: 250,
9631
+ children: /* @__PURE__ */ jsxs("button", {
9632
+ type: "button",
9633
+ className: "dsh-claude-header-diff",
9634
+ "aria-label": label,
9635
+ "aria-pressed": open,
9636
+ "data-counts": hasCounts || void 0,
9637
+ "data-session": sessionId,
9638
+ onClick: toggleDiff,
9639
+ children: [/* @__PURE__ */ jsx(DiffGlyph, {}), hasCounts ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
9640
+ className: "dsh-claude-header-diff-add",
9641
+ children: ["+", additions]
9642
+ }), /* @__PURE__ */ jsxs("span", {
9643
+ className: "dsh-claude-header-diff-del",
9644
+ children: ["−", deletions]
9645
+ })] }) : null]
9646
+ })
9647
+ });
9648
+ }
9649
+ //#endregion
9650
+ //#region src/client/claude-mark.ts
9651
+ /** The Claude wordmark glyph, lifted verbatim from the brand SVG.
9652
+ *
9653
+ * Kept as raw geometry rather than a component because its only consumer
9654
+ * paints it through a CSS `background-image`: the Host draws the agent-preset
9655
+ * icon itself, so the swap has to happen in CSS, not in JSX. */
9656
+ const MARK_PATH = "M 233.959793 800.214905 L 468.644287 668.536987 L 472.590637 657.100647 L 468.644287 650.738403 L 457.208069 650.738403 L 417.986633 648.322144 L 283.892639 644.69812 L 167.597321 639.865845 L 54.926208 633.825623 L 26.577238 627.785339 L 3.3e-05 592.751709 L 2.73832 575.27533 L 26.577238 559.248352 L 60.724873 562.228149 L 136.187973 567.382629 L 249.422867 575.194763 L 331.570496 580.026978 L 453.261841 592.671082 L 472.590637 592.671082 L 475.328857 584.859009 L 468.724915 580.026978 L 463.570557 575.194763 L 346.389313 495.785217 L 219.543671 411.865906 L 153.100723 363.543762 L 117.181267 339.060425 L 99.060455 316.107361 L 91.248367 266.01355 L 123.865784 230.093994 L 167.677887 233.073853 L 178.872513 236.053772 L 223.248367 270.201477 L 318.040283 343.570496 L 441.825592 434.738342 L 459.946411 449.798706 L 467.194672 444.64447 L 468.080597 441.020203 L 459.946411 427.409485 L 392.617493 305.718323 L 320.778564 181.932983 L 288.80542 130.630859 L 280.348999 99.865845 C 277.369171 87.221436 275.194641 76.590698 275.194641 63.624268 L 312.322174 13.20813 L 332.8591 6.604126 L 382.389313 13.20813 L 403.248352 31.328979 L 434.013519 101.71814 L 483.865753 212.537048 L 561.181274 363.221497 L 583.812134 407.919434 L 595.892639 449.315491 L 600.40271 461.959839 L 608.214783 461.959839 L 608.214783 454.711609 L 614.577271 369.825623 L 626.335632 265.61084 L 637.771851 131.516846 L 641.718201 93.745117 L 660.402832 48.483276 L 697.530334 24.000122 L 726.52356 37.852417 L 750.362549 72 L 747.060486 94.067139 L 732.886047 186.201416 L 705.100708 330.52356 L 686.979919 427.167847 L 697.530334 427.167847 L 709.61084 415.087341 L 758.496704 350.174561 L 840.644348 247.490051 L 876.885925 206.738342 L 919.167847 161.71814 L 946.308838 140.29541 L 997.61084 140.29541 L 1035.38269 196.429626 L 1018.469849 254.416199 L 965.637634 321.422852 L 921.825562 378.201538 L 859.006714 462.765259 L 819.785278 530.41626 L 823.409424 535.812073 L 832.75177 534.92627 L 974.657776 504.724915 L 1051.328979 490.872559 L 1142.818848 475.167786 L 1184.214844 494.496582 L 1188.724854 514.147644 L 1172.456421 554.335693 L 1074.604126 578.496765 L 959.838989 601.449829 L 788.939636 641.879272 L 786.845764 643.409485 L 789.261841 646.389343 L 866.255127 653.637634 L 899.194702 655.409424 L 979.812134 655.409424 L 1129.932861 666.604187 L 1169.154419 692.537109 L 1192.671265 724.268677 L 1188.724854 748.429688 L 1128.322144 779.194641 L 1046.818848 759.865845 L 856.590759 714.604126 L 791.355774 698.335754 L 782.335693 698.335754 L 782.335693 703.731567 L 836.69812 756.885986 L 936.322205 846.845581 L 1061.073975 962.81897 L 1067.436279 991.490112 L 1051.409424 1014.120911 L 1034.496704 1011.704712 L 924.885986 929.234924 L 882.604126 892.107544 L 786.845764 811.48999 L 780.483276 811.48999 L 780.483276 819.946289 L 802.550415 852.241699 L 919.087341 1027.409424 L 925.127625 1081.127686 L 916.671204 1098.604126 L 886.469849 1109.154419 L 853.288696 1103.114136 L 785.073914 1007.355835 L 714.684631 899.516785 L 657.906067 802.872498 L 650.979858 806.81897 L 617.476624 1167.704834 L 601.771851 1186.147705 L 565.530212 1200 L 535.328857 1177.046997 L 519.302124 1139.919556 L 535.328857 1066.550537 L 554.657776 970.792053 L 570.362488 894.68457 L 584.536926 800.134277 L 592.993347 768.724976 L 592.429626 766.630859 L 585.503479 767.516968 L 514.22821 865.369263 L 405.825531 1011.865906 L 320.053711 1103.677979 L 299.516815 1111.812256 L 263.919525 1093.369263 L 267.221497 1060.429688 L 287.114136 1031.114136 L 405.825531 880.107361 L 477.422913 786.52356 L 523.651062 732.483276 L 523.328918 724.671265 L 520.590698 724.671265 L 205.288605 929.395935 L 149.154434 936.644409 L 124.993355 914.01355 L 127.973183 876.885986 L 139.409409 864.80542 L 234.201385 799.570435 L 233.879227 799.8927 Z";
9657
+ const MARK_COLOR = "#d97757";
9658
+ /** The mark as a CSS `url()` value.
9659
+ * @returns a percent-encoded `data:` URI, safe to inline in a stylesheet. */
9660
+ function claudeMarkUrl() {
9661
+ return `url("data:image/svg+xml;utf8,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1200"><path fill="${MARK_COLOR}" d="${MARK_PATH}"/></svg>`)}")`;
9662
+ }
9663
+ //#endregion
9664
+ //#region src/client/agent-preset-roster.ts
9665
+ /** Locale keys the Host uses for the presets it ships, mirrored so a shadowed
9666
+ * label still shows their translated copy instead of a bare id. Every entry is
9667
+ * a lookup into the Host's own `settings.agentPreset` namespace; an id absent
9668
+ * here (this plugin's preset included) falls through to file metadata, which
9669
+ * is what the Host does too. */
9670
+ const BUILT_IN_KEYS = {
9671
+ standard: {
9672
+ name: "presetStandardName",
9673
+ description: "presetStandardDescription"
9674
+ },
9675
+ code: {
9676
+ name: "presetCodeName",
9677
+ description: "presetCodeDescription"
9678
+ },
9679
+ minimal: {
9680
+ name: "presetMinimalName",
9681
+ description: "presetMinimalDescription"
9682
+ },
9683
+ cordis: {
9684
+ name: "presetCordisName",
9685
+ description: "presetCordisDescription"
9686
+ }
9687
+ };
9688
+ /**
9689
+ * Resolve one preset's display copy the way the Host does: translated copy for
9690
+ * a preset it ships, file metadata otherwise.
9691
+ * @param row - the roster row, or undefined when the roster has not arrived.
9692
+ * @param presetId - the id recorded on the session.
9693
+ * @param hostT - lookup bound to the Host's preset namespace.
9694
+ * @returns the name to render and the description to put in the tooltip.
9695
+ */
9696
+ function presetDisplayText(row, presetId, hostT) {
9697
+ const keys = row?.trust === "system" ? BUILT_IN_KEYS[row.id] : void 0;
9698
+ if (keys !== void 0) {
9699
+ const name = hostT(keys.name);
9700
+ const description = hostT(keys.description);
9701
+ if (name !== keys.name) return {
9702
+ name,
9703
+ ...description === keys.description ? {} : { description }
9704
+ };
9705
+ }
9706
+ return {
9707
+ name: row?.name ?? presetId,
9708
+ ...row?.description === void 0 ? {} : { description: row.description }
9709
+ };
9710
+ }
9711
+ var AgentPresetRoster = class {
9712
+ #api;
9713
+ #listeners = /* @__PURE__ */ new Set();
9714
+ #rows = [];
9715
+ #inflight;
9716
+ constructor(api) {
9717
+ this.#api = api;
9718
+ }
9719
+ /** Read the roster once. Concurrent calls share the in-flight read, and a
9720
+ * failed read leaves the previous rows in place — the label falls back to
9721
+ * the session's preset id, never to an empty header. */
9722
+ load() {
9723
+ if (this.#inflight !== void 0) return;
9724
+ this.#inflight = (async () => {
9725
+ try {
9726
+ const response = await this.#api.list({});
9727
+ if (!response.result.ok) return;
9728
+ this.#rows = response.result.value.presets;
9729
+ for (const listener of this.#listeners) listener();
9730
+ } catch {} finally {
9731
+ this.#inflight = void 0;
9732
+ }
9733
+ })();
9734
+ }
9735
+ subscribe = (listener) => {
9736
+ this.#listeners.add(listener);
9737
+ return () => {
9738
+ this.#listeners.delete(listener);
9739
+ };
9740
+ };
9741
+ getSnapshot = () => this.#rows;
9742
+ };
9743
+ //#endregion
9744
+ //#region src/client/ClaudeAgentPresetLabel.tsx
9745
+ /** Reproduces the Host label's own metrics — it sits in the same action row
9746
+ * beside the title, so it has to read as the same piece of chrome. */
9747
+ const LABEL_CSS = [
9748
+ ".dsh-claude-preset-label{display:inline-flex;align-items:center;gap:4px;max-width:220px;height:22px;",
9749
+ "padding:0 2px 0 0;border-radius:6px;overflow:hidden;white-space:nowrap;",
9750
+ "color:var(--dsw-alias-label-secondary);font-family:var(--dsw-font-family);font-size:12px;line-height:22px}",
9751
+ ".dsh-claude-preset-label>*{flex:none}",
9752
+ ".dsh-claude-preset-name{min-width:0;overflow:hidden;text-overflow:ellipsis}",
9753
+ ".dsh-claude-preset-icon{opacity:.7}",
9754
+ `.dsh-claude-preset-mark{width:14px;height:14px;background:${claudeMarkUrl()} center/contain no-repeat}`
9755
+ ].join("");
9756
+ let cssInjected = false;
9757
+ function ensureCss() {
9758
+ if (cssInjected || typeof document === "undefined") return;
9759
+ cssInjected = true;
9760
+ const element = document.createElement("style");
9761
+ element.dataset.dshClaudePresetLabel = "";
9762
+ element.textContent = LABEL_CSS;
9763
+ document.head.appendChild(element);
9764
+ }
9765
+ function ClaudeAgentPresetLabel({ t, hostT, roster, sessionId, useSessions }) {
9766
+ const preset = useSessions((state) => state.byId[sessionId]?.agentPreset);
9767
+ const rows = useSyncExternalStore(roster.subscribe, roster.getSnapshot, roster.getSnapshot);
9768
+ const { load } = roster;
9769
+ useEffect(() => {
9770
+ if (preset !== void 0) load();
9771
+ }, [preset, load]);
9772
+ if (preset === void 0) return null;
9773
+ ensureCss();
9774
+ const text = presetDisplayText(rows.find((row) => row.id === preset), preset, hostT);
9775
+ return /* @__PURE__ */ jsx(Tooltip, {
9776
+ label: text.description ?? t("presetHeaderHint"),
9777
+ side: "bottom",
9778
+ delayMs: 250,
9779
+ maxWidth: 280,
9780
+ children: /* @__PURE__ */ jsxs("span", {
9781
+ className: "dsh-claude-preset-label",
9782
+ children: [preset === "claude" ? /* @__PURE__ */ jsx("span", {
9783
+ className: "dsh-claude-preset-mark",
9784
+ "aria-hidden": "true"
9785
+ }) : /* @__PURE__ */ jsx(IconAgentPresetOutline16, {
9786
+ size: 14,
9787
+ className: "dsh-claude-preset-icon"
9788
+ }), /* @__PURE__ */ jsx("span", {
9789
+ className: "dsh-claude-preset-name",
9790
+ children: text.name
9791
+ })]
9792
+ })
9793
+ });
9794
+ }
9795
+ //#endregion
9796
+ //#region src/client/diff-open-store.ts
9797
+ var DiffOpenStore = class {
9798
+ #sessionId;
9799
+ #listeners = /* @__PURE__ */ new Set();
9800
+ /** Mark the diff panel open for one session, replacing any previous holder. */
9801
+ open(sessionId) {
9802
+ this.#set(sessionId);
9803
+ }
9804
+ /** Mark the diff panel closed. */
9805
+ close() {
9806
+ this.#set(void 0);
9807
+ }
9808
+ /** Whether the diff panel is currently open for this session. */
9809
+ isOpen(sessionId) {
9810
+ return this.#sessionId === sessionId;
9811
+ }
9812
+ /** A per-session snapshot source for `useSyncExternalStore`. */
9813
+ sourceFor(sessionId) {
9814
+ return {
9815
+ subscribe: (listener) => {
9816
+ this.#listeners.add(listener);
9817
+ return () => {
9818
+ this.#listeners.delete(listener);
9819
+ };
9820
+ },
9821
+ getSnapshot: () => this.isOpen(sessionId)
9822
+ };
9823
+ }
9824
+ #set(sessionId) {
9825
+ if (this.#sessionId === sessionId) return;
9826
+ this.#sessionId = sessionId;
9827
+ for (const listener of this.#listeners) listener();
9828
+ }
9829
+ };
9830
+ //#endregion
9546
9831
  //#region src/client/claude-command-source.ts
9547
9832
  const SOURCE_NAME = "Claude Code";
9548
9833
  function commandsFor(store, session) {
@@ -9625,6 +9910,81 @@ window.__ModuleLoader__.load({
9625
9910
  }
9626
9911
  }
9627
9912
  //#endregion
9913
+ //#region src/client/preset-seat-mark.ts
9914
+ /** Flags the Host's agent-preset seat while it names the Claude preset.
9915
+ *
9916
+ * The seat is a `single` slot whose occupant owns the whole picker — menu,
9917
+ * staging, the intro animation. Shadowing it to change one glyph would mean
9918
+ * reimplementing all of that, so the glyph is swapped in CSS instead. CSS
9919
+ * cannot read the seat's text, though, and the seat names whichever preset is
9920
+ * currently staged, so the brand mark would otherwise land on `standard` or
9921
+ * `cordis` too. This bridge supplies the one bit CSS is missing.
9922
+ *
9923
+ * Keyed on the seat icon's CSS Module local name plus `aria-haspopup`, both of
9924
+ * which fail open: a Host rename leaves the attribute unset and the stock
9925
+ * glyph in place.
9926
+ */
9927
+ /** Attribute the stylesheet keys the mark swap off. */
9928
+ const CLAUDE_SEAT_ATTRIBUTE = "data-dsh-claude-preset";
9929
+ const SEAT_SELECTOR = "button[aria-haspopup=\"menu\"]";
9930
+ const SEAT_ICON_SELECTOR = "[class*=\"seatIcon\"]";
9931
+ /**
9932
+ * Set or clear the flag on every preset seat currently in the tree.
9933
+ * @param root - subtree to sweep; the document by default.
9934
+ */
9935
+ function markClaudePresetSeats(root) {
9936
+ for (const icon of root.querySelectorAll(SEAT_ICON_SELECTOR)) {
9937
+ const seat = icon.closest(SEAT_SELECTOR);
9938
+ if (seat === null) continue;
9939
+ if (isClaudePresetText(seat.textContent ?? "")) seat.setAttribute(CLAUDE_SEAT_ATTRIBUTE, "");
9940
+ else seat.removeAttribute(CLAUDE_SEAT_ATTRIBUTE);
9941
+ }
9942
+ }
9943
+ /** Keep the flag in step with the Host's own re-renders.
9944
+ * @returns a disposer that stops observing and clears every flag it set. */
9945
+ function trackClaudePresetSeats() {
9946
+ if (typeof document === "undefined" || typeof window === "undefined" || typeof MutationObserver === "undefined") return () => {};
9947
+ let frame;
9948
+ const schedule = () => {
9949
+ if (frame !== void 0) return;
9950
+ frame = window.requestAnimationFrame(() => {
9951
+ frame = void 0;
9952
+ markClaudePresetSeats(document);
9953
+ });
9954
+ };
9955
+ const observer = new MutationObserver(schedule);
9956
+ observer.observe(document.body, {
9957
+ childList: true,
9958
+ subtree: true,
9959
+ characterData: true
9960
+ });
9961
+ markClaudePresetSeats(document);
9962
+ return () => {
9963
+ observer.disconnect();
9964
+ if (frame !== void 0) window.cancelAnimationFrame(frame);
9965
+ for (const seat of document.querySelectorAll(`[${CLAUDE_SEAT_ATTRIBUTE}]`)) seat.removeAttribute(CLAUDE_SEAT_ATTRIBUTE);
9966
+ };
9967
+ }
9968
+ 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("")}${[
9969
+ `button[${CLAUDE_SEAT_ATTRIBUTE}]>[class*="seatIcon"]{display:none}`,
9970
+ `button[${CLAUDE_SEAT_ATTRIBUTE}]::before{content:"";flex:none;width:16px;height:16px;`,
9971
+ `background:${claudeMarkUrl()} center/contain no-repeat}`
9972
+ ].join("")}`;
9973
+ /** Install the stylesheet and the one DOM flag it depends on.
9974
+ * @returns a disposer that removes both again. */
9975
+ function restyleHostChrome() {
9976
+ if (typeof document === "undefined") return () => {};
9977
+ const element = document.createElement("style");
9978
+ element.dataset.dshClaudeHostChrome = "";
9979
+ element.textContent = HOST_CHROME_CSS;
9980
+ document.head.appendChild(element);
9981
+ const untrack = trackClaudePresetSeats();
9982
+ return () => {
9983
+ untrack();
9984
+ element.remove();
9985
+ };
9986
+ }
9987
+ //#endregion
9628
9988
  //#region src/client/details-resize.ts
9629
9989
  const DETAILS_MIN_WIDTH = 300;
9630
9990
  const DETAILS_DEFAULT_WIDTH = 720;
@@ -9963,6 +10323,7 @@ window.__ModuleLoader__.load({
9963
10323
  no: "否",
9964
10324
  diffOpen: "查看分支改动",
9965
10325
  diffClose: "关闭 Diff 面板",
10326
+ presetHeaderHint: "当前会话运行的 Agent 预设",
9966
10327
  diffWorkingTree: "分支改动",
9967
10328
  diffFiles: "{count} 个已修改文件",
9968
10329
  diffFilesShort: "{count} 个文件",
@@ -10008,10 +10369,12 @@ window.__ModuleLoader__.load({
10008
10369
  diffMergeCompleted: "已合并 PR #{number}",
10009
10370
  diffUpdateBranch: "Update branch…",
10010
10371
  repositoryUpdateBranch: "Update branch",
10011
- diffUpdateBranchDescription: " origin/{base} 合并进当前分支并推送;出现冲突时可交给 Claude 解决。",
10372
+ diffUpdateBranchDescription: " origin/{base} 更新当前分支并推送;出现冲突时可交给 Claude 解决。",
10373
+ diffUpdateBranch_rebase: "Rebase 到 origin/{base}(改写历史,以 --force-with-lease 推送)",
10374
+ diffUpdateBranch_merge: "将 origin/{base} 合并进当前分支",
10012
10375
  diffUpdateBranchBehind: "落后 origin/{base} {count} 个 commit",
10013
- diffUpdateBranchCompleted: "已合并并推送 commit {commit}",
10014
- diffUpdateBranchConflicts: "合并产生冲突,以下文件需要解决:",
10376
+ diffUpdateBranchCompleted: "已更新并推送 commit {commit}",
10377
+ diffUpdateBranchConflicts: "更新产生冲突,以下文件需要解决:",
10015
10378
  diffUpdateBranchResolve: "让 Claude 解决冲突",
10016
10379
  repositoryChecksOpen: "查看失败的检查",
10017
10380
  checksCardTitle: "失败的检查",
@@ -10277,6 +10640,7 @@ window.__ModuleLoader__.load({
10277
10640
  no: "No",
10278
10641
  diffOpen: "View branch changes",
10279
10642
  diffClose: "Close diff panel",
10643
+ presetHeaderHint: "The agent preset this session runs",
10280
10644
  diffWorkingTree: "Branch changes",
10281
10645
  diffFiles: "{count} modified file(s)",
10282
10646
  diffFilesShort: "{count} files",
@@ -10322,10 +10686,12 @@ window.__ModuleLoader__.load({
10322
10686
  diffMergeCompleted: "Merged PR #{number}",
10323
10687
  diffUpdateBranch: "Update branch…",
10324
10688
  repositoryUpdateBranch: "Update branch",
10325
- diffUpdateBranchDescription: "Merge origin/{base} into the current branch and push; hand any conflicts to Claude.",
10689
+ diffUpdateBranchDescription: "Update the current branch from origin/{base} and push; hand any conflicts to Claude.",
10690
+ diffUpdateBranch_rebase: "Rebase onto origin/{base} (rewrites history, pushes with --force-with-lease)",
10691
+ diffUpdateBranch_merge: "Merge origin/{base} into the current branch",
10326
10692
  diffUpdateBranchBehind: "{count} commit(s) behind origin/{base}",
10327
- diffUpdateBranchCompleted: "Merged and pushed commit {commit}",
10328
- diffUpdateBranchConflicts: "The merge left conflicts in these files:",
10693
+ diffUpdateBranchCompleted: "Updated and pushed commit {commit}",
10694
+ diffUpdateBranchConflicts: "The update left conflicts in these files:",
10329
10695
  diffUpdateBranchResolve: "Have Claude resolve the conflicts",
10330
10696
  repositoryChecksOpen: "Show failing checks",
10331
10697
  checksCardTitle: "Failing checks",
@@ -10454,6 +10820,7 @@ window.__ModuleLoader__.load({
10454
10820
  en
10455
10821
  }), "dsh-claude: client copy");
10456
10822
  const t = ctx.locale.bind(namespace);
10823
+ ctx.effect(() => restyleHostChrome(), "dsh-claude: Host chrome restyling");
10457
10824
  const projections = new ClaudeProjectionStore();
10458
10825
  ctx.effect(() => ctx.inputTriggers.registerSource(createClaudeCommandSource(ctx, projections)), "dsh-claude: Claude slash source");
10459
10826
  const sessions = ctx.get("sessions");
@@ -10495,6 +10862,7 @@ window.__ModuleLoader__.load({
10495
10862
  let disposeDiffOverlay;
10496
10863
  let disposeExpandedDetailsResize;
10497
10864
  let detailsSessionId;
10865
+ const diffOpen = new DiffOpenStore();
10498
10866
  const restoreDiff = () => {
10499
10867
  if (disposeDiffOverlay === void 0) return;
10500
10868
  disposeDiffOverlay();
@@ -10511,6 +10879,7 @@ window.__ModuleLoader__.load({
10511
10879
  disposePluginDetails?.();
10512
10880
  disposePluginDetails = void 0;
10513
10881
  detailsSessionId = void 0;
10882
+ diffOpen.close();
10514
10883
  layout?.closeDetails();
10515
10884
  };
10516
10885
  ctx.effect(() => ctx.slots.onEntryError((key, entry) => {
@@ -10620,6 +10989,7 @@ window.__ModuleLoader__.load({
10620
10989
  detailsSessionId = void 0;
10621
10990
  return;
10622
10991
  }
10992
+ diffOpen.open(sessionId);
10623
10993
  disposeExpandedDetailsResize = enableExpandedDetailsResize();
10624
10994
  };
10625
10995
  ctx.effect(() => {
@@ -10651,6 +11021,40 @@ window.__ModuleLoader__.load({
10651
11021
  openTasks: (turn) => openTasksPanel(sessionId, turn)
10652
11022
  })
10653
11023
  }, ClaudeActivityTail));
11024
+ if (connection !== void 0) {
11025
+ const roster = new AgentPresetRoster(connection.api.agentPresets);
11026
+ const hostT = ctx.locale.bind("settings.agentPreset");
11027
+ ctx.slots.inject("conversation.session.header.actions", () => ctx.slots.register({
11028
+ name: "conversation.session.header.actions",
11029
+ id: "agent-preset",
11030
+ order: -10,
11031
+ priority: -10,
11032
+ locale: namespace,
11033
+ inject: () => ({
11034
+ t,
11035
+ hostT: (key) => hostT(key),
11036
+ roster: {
11037
+ subscribe: roster.subscribe,
11038
+ getSnapshot: roster.getSnapshot,
11039
+ load: () => roster.load()
11040
+ }
11041
+ })
11042
+ }, ClaudeAgentPresetLabel));
11043
+ }
11044
+ ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
11045
+ name: "conversation.session.header.utilities",
11046
+ id: "claude-diff",
11047
+ order: 30,
11048
+ locale: namespace,
11049
+ inject: (sessionId) => ({
11050
+ t,
11051
+ toggleDiff: () => {
11052
+ if (diffOpen.isOpen(sessionId)) closePluginDetails();
11053
+ else openDiffPanel(sessionId);
11054
+ },
11055
+ diffOpen: diffOpen.sourceFor(sessionId)
11056
+ })
11057
+ }, ClaudeDiffHeaderAction));
10654
11058
  ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
10655
11059
  name: "conversation.input.dock",
10656
11060
  id: "claude-review-comments",