dsh-git-branch-manage 1.0.3 → 1.0.4

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
@@ -391,6 +391,21 @@ window.__ModuleLoader__.load({
391
391
  return post(ROUTE_AI_COMMIT_MESSAGE, body);
392
392
  }
393
393
  //#endregion
394
+ //#region src/client/git-events.ts
395
+ const listeners = /* @__PURE__ */ new Set();
396
+ /** Announce that a surface changed repository state (checkout, rename,
397
+ * delete, create, commit, fetch, update, push). */
398
+ function emitGitMutated() {
399
+ for (const listener of [...listeners]) listener();
400
+ }
401
+ /** Subscribe; returns the unsubscribe function. */
402
+ function onGitMutated(listener) {
403
+ listeners.add(listener);
404
+ return () => {
405
+ listeners.delete(listener);
406
+ };
407
+ }
408
+ //#endregion
394
409
  //#region src/client/branch-tree.ts
395
410
  /** Whether a bare `git push` has a known upstream and local commits ahead. */
396
411
  function hasPushableCommits(rows, currentBranch) {
@@ -2735,6 +2750,12 @@ window.__ModuleLoader__.load({
2735
2750
  document.removeEventListener("visibilitychange", onVisibility);
2736
2751
  };
2737
2752
  }, [refresh]);
2753
+ (0, react.useEffect)(() => {
2754
+ return onGitMutated(() => {
2755
+ if (busyRef.current) return;
2756
+ refresh();
2757
+ });
2758
+ }, [refresh]);
2738
2759
  const showError = (0, react.useCallback)((message) => {
2739
2760
  pushToast(t("errorGeneric", { message }));
2740
2761
  }, [pushToast, t]);
@@ -2756,6 +2777,7 @@ window.__ModuleLoader__.load({
2756
2777
  setConfirm(null);
2757
2778
  if (!keepOpen) setMenuOpen(false);
2758
2779
  onSettled?.();
2780
+ emitGitMutated();
2759
2781
  }, [showError]);
2760
2782
  /** In-place switch flow: POST /switch, then refetch the status. */
2761
2783
  const doSwitch = (0, react.useCallback)((branch, keepOpen = false) => runGuarded(async () => {
@@ -3459,6 +3481,17 @@ window.__ModuleLoader__.load({
3459
3481
  if (needle === "") return [...commits];
3460
3482
  return commits.filter((commit) => commit.subject.toLowerCase().includes(needle) || commit.authorName.toLowerCase().includes(needle));
3461
3483
  }
3484
+ /**
3485
+ * Case-insensitive substring filter over the branch name — the branch card's
3486
+ * head search box. A remote row matches on its full `remote/name`, which
3487
+ * contains the prefix-stripped short name the tree renders, so searching what
3488
+ * the user sees and searching the remote prefix both hit.
3489
+ */
3490
+ function filterBranches(rows, query) {
3491
+ const needle = query.trim().toLowerCase();
3492
+ if (needle === "") return [...rows];
3493
+ return rows.filter((row) => row.name.toLowerCase().includes(needle));
3494
+ }
3462
3495
  /** Merge the next page into the accumulated list, dropping hash duplicates
3463
3496
  * (a ref moved between fetches must not duplicate rows). */
3464
3497
  function appendPage(current, page) {
@@ -3520,9 +3553,19 @@ window.__ModuleLoader__.load({
3520
3553
  branches
3521
3554
  })).sort((left, right) => left.remote.localeCompare(right.remote));
3522
3555
  }
3556
+ /** Every group key the branch tree renders — the collapse-all target set.
3557
+ * The bare `remote` key folds the whole remote tree; the nested
3558
+ * `remote:<name>` keys matter only for the groups it re-opens. */
3559
+ function branchGroupKeys(remotes) {
3560
+ return [
3561
+ "local",
3562
+ "remote",
3563
+ ...groupRemotes(remotes).map((group) => `remote:${group.remote}`)
3564
+ ];
3565
+ }
3523
3566
  //#endregion
3524
3567
  //#region \0git-worktree-css:/home/runner/work/dsh-git-branch/dsh-git-branch/src/client/GitHistoryPanel.module.css?inline
3525
- const css$2 = "/* Git panel (sidebar global panel). Three stacked sections over the main\n * column: uncommitted changes + commit box, branches, commit history. Host\n * tokens only; the few status colors fall back to the same literals the\n * settings card uses. */\n\n.page_427795137 {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.topbar_427795137 {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px 8px;\n}\n\n.back_427795137 {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border: 0;\n background: none;\n cursor: pointer;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n padding: 4px 8px;\n border-radius: 8px;\n}\n\n.back_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.title_427795137 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.repoPill_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--dsw-alias-label-secondary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 999px;\n padding: 3px 10px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.repoName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.branchName_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.track_427795137 {\n font-variant-numeric: tabular-nums;\n}\n\n.topbarActions_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: none;\n}\n\n.refreshBtn_427795137 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 8px;\n background: none;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n.refreshBtn_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.refreshBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.scroll_427795137 {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 16px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* Sections NEVER flex-shrink: the scroll container is a definite-height\n * flex column, and a shrinkable child collapses to a fraction of its\n * content while the overflow paints over the following sections (the\n * branch section went empty and the commit box drifted into the history).\n * Natural heights; the container scrolls as a whole. */\n.section_427795137 {\n flex: none;\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n display: flex;\n flex-direction: column;\n}\n\n.sectionHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.sectionTitle_427795137 {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.countBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 999px;\n padding: 3px 7px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.sectionTools_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.sectionBody_427795137 {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Small action chip used across the section heads and the commit row. */\n.act_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n cursor: pointer;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 4px 10px;\n white-space: nowrap;\n}\n\n.act_427795137:hover:not([disabled]) {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.act_427795137[disabled] {\n opacity: 0.45;\n cursor: default;\n}\n\n.actPrimary_427795137 {\n /* Hardcoded blue by request — the brand token does not resolve reliably\n * inside the better-sidebar panel. */\n background: #4c6bf5;\n border-color: transparent;\n color: #fff;\n}\n\n.actPrimary_427795137:hover:not([disabled]) {\n border-color: transparent;\n filter: brightness(1.06);\n}\n\n/* Uncommitted changes */\n\n.groupTitleRow_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.linkBtn_427795137 {\n margin-left: auto;\n background: none;\n border: 0;\n padding: 2px 4px;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n border-radius: 5px;\n}\n\n.linkBtn_427795137:hover:not([disabled]) {\n text-decoration: underline;\n}\n\n.linkBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.changeRow_427795137 {\n display: grid;\n grid-template-columns: 16px 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.changeRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.changeCheck_427795137 {\n width: 14px;\n height: 14px;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n margin: 0;\n}\n\n.statusBadge_427795137 {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n color: #fff;\n box-sizing: border-box;\n}\n\n.statusA_427795137 { background: var(--dsw-alias-status-success, #218358); }\n.statusM_427795137 { background: var(--dsw-alias-label-warning, #b07000); }\n.statusD_427795137 { background: var(--dsw-alias-status-danger, #d5304f); }\n.statusR_427795137 { background: #8350d8; }\n.statusC_427795137 { background: #8350d8; }\n.statusT_427795137 { background: #5b7a9d; }\n.statusQ_427795137 { background: var(--dsw-alias-label-dimmed, #9aa0aa); }\n.statusU_427795137 { background: #d5502e; }\n\n.changePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.stageHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n flex: none;\n}\n\n.commitBox_427795137 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n}\n\n.commitInput_427795137 {\n width: 100%;\n box-sizing: border-box;\n resize: vertical;\n min-height: 52px;\n font: inherit;\n font-size: 12.5px;\n line-height: 1.5;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.commitInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.commitActions_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.commitHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n min-height: 16px;\n}\n\n.split_427795137 {\n margin-left: auto;\n display: flex;\n position: relative;\n}\n\n.splitMain_427795137 {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 16px;\n font-size: 13px;\n}\n\n.splitCaret_427795137 {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n margin-left: 1px;\n padding: 6px 6px;\n}\n\n.menu_427795137 {\n position: absolute;\n right: 0;\n top: calc(100% + 4px);\n z-index: 40;\n width: max-content;\n min-width: 0;\n background: var(--dsw-alias-surface-raised, var(--dsw-alias-bg-layer-3));\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n box-shadow: var(--dsw-shadow-lv3);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n padding: 3px;\n display: flex;\n flex-direction: column;\n}\n\n.menuItem_427795137 {\n display: block;\n width: 100%;\n text-align: left;\n background: none;\n border: 0;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n padding: 5px 10px;\n border-radius: 5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.menuItem_427795137:hover:not([disabled]) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.menuItem_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.menuSub_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n margin-top: 1px;\n}\n\n/* Branch section — two IDEA-style trees: 本地 → branches, 远程 → per-remote → branches */\n\n.treeHead_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 6px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n\n.treeHead_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.treeChevron_427795137 {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.treeChildren_427795137 {\n display: flex;\n flex-direction: column;\n margin-left: 12px;\n}\n\n.treeHeadNest_427795137 {\n font-weight: 400;\n}\n\n.treeNest_427795137 {\n margin-left: 12px;\n}\n\n.branchRow_427795137 {\n display: grid;\n grid-template-columns: 20px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 5px 8px;\n border-radius: 7px;\n cursor: pointer;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.branchRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.branchRowCurrent_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n cursor: default;\n}\n\n.branchRowCurrent_427795137:hover {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.branchMark_427795137 {\n color: var(--dsw-alias-brand-primary);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.branchName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.branchKind_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n margin-left: 6px;\n}\n\n.branchTrack_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n flex: none;\n}\n\n/* The branch action menu is portaled; fixed positioning by row rect. */\n.floatMenu_427795137 {\n position: fixed;\n z-index: 60;\n min-width: 190px;\n}\n\n.menuHead_427795137 {\n padding: 6px 10px 5px;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n margin-bottom: 3px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.menuHeadStrong_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.menuDanger_427795137 {\n color: var(--dsw-alias-status-danger, #d5304f);\n}\n\n.formField_427795137 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.formLabel_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.formInput_427795137 {\n font: inherit;\n font-size: 13px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.formInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.formError_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-status-danger, #d5304f);\n min-height: 16px;\n}\n\n/* Commit history */\n\n.historyHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.searchBox_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1;\n max-width: 300px;\n min-width: 140px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 0 9px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.searchInput_427795137 {\n flex: 1;\n min-width: 0;\n border: 0;\n background: none;\n outline: none;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.searchInput_427795137::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.commitRow_427795137 {\n position: relative;\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 6px 8px 6px 2px;\n cursor: pointer;\n border-radius: 7px;\n}\n\n.commitRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.commitRowOpen_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitRowOpen_427795137:hover {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitGraph_427795137 {\n position: relative;\n align-self: stretch;\n min-height: 34px;\n}\n\n.commitGraph_427795137::before {\n content: \"\";\n position: absolute;\n left: 10px;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--dsw-alias-border-l2);\n}\n\n.commitRowFirst_427795137 .commitGraph_427795137::before {\n top: 50%;\n}\n\n.commitRowLast_427795137 .commitGraph_427795137::before {\n bottom: 50%;\n}\n\n.commitDot_427795137 {\n position: absolute;\n left: 5px;\n top: 50%;\n transform: translateY(-50%);\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--dsw-alias-brand-primary);\n border: 3px solid var(--dsw-alias-bg-layer-2);\n box-sizing: content-box;\n margin-left: -3px;\n}\n\n.commitDotMerge_427795137 {\n background: #8350d8;\n}\n\n.commitMain_427795137 {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.commitSubject_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.commitSubjectText_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.commitRowOpen_427795137 .commitSubjectText_427795137 {\n font-weight: 600;\n}\n\n.refBadges_427795137 {\n display: inline-flex;\n gap: 4px;\n flex: none;\n}\n\n.refBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 4px;\n padding: 3px 5px;\n font-weight: 500;\n white-space: nowrap;\n max-width: 140px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.refHead_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary-bluish);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refTag_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-warning, #b07000);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refRemote_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: #8350d8;\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refLocal_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-status-success, #218358);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.commitMeta_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n}\n\n.authorDot_427795137 {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--dsw-alias-brand-primary);\n color: #fff;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 9px;\n flex: none;\n}\n\n.commitSide_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n flex: none;\n}\n\n.commitHash_427795137 {\n font-family: ui-monospace, Consolas, monospace;\n}\n\n.commitExpand_427795137 {\n grid-column: 1 / -1;\n padding: 2px 4px 8px 28px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.commitExpandTitle_427795137 {\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n margin-bottom: 3px;\n}\n\n.fileRow_427795137 {\n display: grid;\n grid-template-columns: 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.fileRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.filePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.fileNums_427795137 {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n}\n\n.fileAdd_427795137 { color: var(--dsw-alias-status-success, #218358); }\n.fileDel_427795137 { color: var(--dsw-alias-status-danger, #d5304f); }\n\n.empty_427795137 {\n padding: 28px 16px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12.5px;\n}\n\n.errorText_427795137 {\n padding: 20px 16px;\n text-align: center;\n color: var(--dsw-alias-status-danger, #d5304f);\n font-size: 12.5px;\n}\n\n.moreRow_427795137 {\n display: flex;\n justify-content: center;\n padding: 8px 0 2px;\n}\n";
3568
+ const css$2 = "/* Git panel (sidebar global panel). Three stacked sections over the main\n * column: uncommitted changes + commit box, branches, commit history. Host\n * tokens only; the few status colors fall back to the same literals the\n * settings card uses. */\n\n.page_427795137 {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.topbar_427795137 {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px 8px;\n}\n\n.back_427795137 {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n border: 0;\n background: none;\n cursor: pointer;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n padding: 4px 8px;\n border-radius: 8px;\n}\n\n.back_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.title_427795137 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.repoPill_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12px;\n line-height: 1.4;\n color: var(--dsw-alias-label-secondary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 999px;\n padding: 3px 10px;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.repoName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.branchName_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.track_427795137 {\n font-variant-numeric: tabular-nums;\n}\n\n.topbarActions_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: none;\n}\n\n.refreshBtn_427795137 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 8px;\n background: none;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n.refreshBtn_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.refreshBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.scroll_427795137 {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 16px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* The page column below the topbar is a definite-height flex column and the\n * three cards split it 2:1:2 (uncommitted : branches : history) — five\n * shares, the topbar and gaps taking their natural height first. Each body\n * scrolls INSIDE its share — min-height: 0 is what lets a flex child shrink\n * below its content (the old natural-height model painted overflowing rows\n * over the sections below). A floor keeps a very short panel from\n * collapsing a card to its bare head; past the floor the column itself\n * scrolls. */\n.section_427795137 {\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.sectionFill_427795137 {\n flex: 1 1 0;\n min-height: 84px;\n}\n\n/* The three flex-grow overrides of the 2:1:2 split. */\n.sectionChanges_427795137 {\n flex-grow: 2;\n}\n\n.sectionBranches_427795137 {\n flex-grow: 1;\n}\n\n.sectionHistory_427795137 {\n flex-grow: 2;\n}\n\n.sectionHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.sectionTitle_427795137 {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.countBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 999px;\n padding: 3px 7px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-secondary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n\n.sectionTools_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.sectionBody_427795137 {\n padding: 10px 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/* The branch and history bodies scroll as a whole inside their share. */\n.sectionBodyScroll_427795137 {\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* Inside the uncommitted card only the file lists scroll; the commit box\n * below stays pinned however long the change tree is. */\n.changeList_427795137 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n/* The 全部展开/全部折叠 text chips in the branch card head. */\n.treeToggle_427795137 {\n background: none;\n border: 0;\n padding: 2px 5px;\n font: inherit;\n font-size: 11.5px;\n line-height: 1.4;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n border-radius: 5px;\n white-space: nowrap;\n}\n\n.treeToggle_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-brand-primary);\n}\n\n/* Small action chip used across the section heads and the commit row. */\n.act_427795137 {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n cursor: pointer;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 4px 10px;\n white-space: nowrap;\n}\n\n.act_427795137:hover:not([disabled]) {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.act_427795137[disabled] {\n opacity: 0.45;\n cursor: default;\n}\n\n.actPrimary_427795137 {\n /* Hardcoded blue by request — the brand token does not resolve reliably\n * inside the better-sidebar panel. */\n background: #4c6bf5;\n border-color: transparent;\n color: #fff;\n}\n\n.actPrimary_427795137:hover:not([disabled]) {\n border-color: transparent;\n filter: brightness(1.06);\n}\n\n/* Uncommitted changes */\n\n.groupTitleRow_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.linkBtn_427795137 {\n margin-left: auto;\n background: none;\n border: 0;\n padding: 2px 4px;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n border-radius: 5px;\n}\n\n.linkBtn_427795137:hover:not([disabled]) {\n text-decoration: underline;\n}\n\n.linkBtn_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.changeRow_427795137 {\n display: grid;\n grid-template-columns: 16px 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.changeRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.changeCheck_427795137 {\n width: 14px;\n height: 14px;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n margin: 0;\n}\n\n.statusBadge_427795137 {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n color: #fff;\n box-sizing: border-box;\n}\n\n.statusA_427795137 { background: var(--dsw-alias-status-success, #218358); }\n.statusM_427795137 { background: var(--dsw-alias-label-warning, #b07000); }\n.statusD_427795137 { background: var(--dsw-alias-status-danger, #d5304f); }\n.statusR_427795137 { background: #8350d8; }\n.statusC_427795137 { background: #8350d8; }\n.statusT_427795137 { background: #5b7a9d; }\n.statusQ_427795137 { background: var(--dsw-alias-label-dimmed, #9aa0aa); }\n.statusU_427795137 { background: #d5502e; }\n\n.changePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.stageHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n flex: none;\n}\n\n.commitBox_427795137 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n flex: none;\n}\n\n.commitInput_427795137 {\n width: 100%;\n box-sizing: border-box;\n resize: vertical;\n min-height: 52px;\n font: inherit;\n font-size: 12.5px;\n line-height: 1.5;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.commitInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.commitActions_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.commitHint_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n min-height: 16px;\n}\n\n.split_427795137 {\n margin-left: auto;\n display: flex;\n position: relative;\n}\n\n.splitMain_427795137 {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 16px;\n font-size: 13px;\n}\n\n.splitCaret_427795137 {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n margin-left: 1px;\n padding: 6px 6px;\n}\n\n.menu_427795137 {\n position: absolute;\n right: 0;\n top: calc(100% + 4px);\n z-index: 40;\n width: max-content;\n min-width: 0;\n background: var(--dsw-alias-surface-raised, var(--dsw-alias-bg-layer-3));\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n box-shadow: var(--dsw-shadow-lv3);\n backdrop-filter: var(--dsw-menu-backdrop-filter);\n padding: 3px;\n display: flex;\n flex-direction: column;\n}\n\n.menuItem_427795137 {\n display: block;\n width: 100%;\n text-align: left;\n background: none;\n border: 0;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n line-height: 1.4;\n padding: 5px 10px;\n border-radius: 5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.menuItem_427795137:hover:not([disabled]) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.menuItem_427795137[disabled] {\n opacity: 0.5;\n cursor: default;\n}\n\n.menuSub_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n margin-top: 1px;\n}\n\n/* Branch section — two IDEA-style trees: 本地 → branches, 远程 → per-remote → branches */\n\n.treeHead_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 6px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n\n.treeHead_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n.treeChevron_427795137 {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.treeChildren_427795137 {\n display: flex;\n flex-direction: column;\n margin-left: 12px;\n}\n\n.treeHeadNest_427795137 {\n font-weight: 400;\n}\n\n.treeNest_427795137 {\n margin-left: 12px;\n}\n\n.branchRow_427795137 {\n display: grid;\n grid-template-columns: 20px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 5px 8px;\n border-radius: 7px;\n /* The action menu opens on RIGHT-CLICK (Enter/Space for keyboard) — an\n * arrow, not the pointing hand a left-click affordance would promise. */\n cursor: default;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.branchRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n/* Whole-row highlight for the current branch: the ✓ alone read as too\n * quiet inside the tree. A translucent brand-blue wash plus the inset left\n * accent and a colored name survives both themes; #4c6bf5 is the same\n * hardcoded blue actPrimary uses (the brand token does not resolve\n * reliably inside the better-sidebar panel). */\n.branchRowCurrent_427795137 {\n background: rgba(76, 107, 245, 0.16);\n box-shadow: inset 2px 0 0 #4c6bf5;\n cursor: default;\n}\n\n.branchRowCurrent_427795137:hover {\n background: rgba(76, 107, 245, 0.16);\n}\n\n.branchRowCurrent_427795137 .branchName_427795137 {\n color: #4c6bf5;\n font-weight: 600;\n}\n\n.branchMark_427795137 {\n color: var(--dsw-alias-brand-primary);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.branchName_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.branchKind_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n margin-left: 6px;\n}\n\n.branchTrack_427795137 {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n flex: none;\n}\n\n/* The branch action menu is portaled; fixed positioning by row rect. */\n.floatMenu_427795137 {\n position: fixed;\n z-index: 60;\n min-width: 190px;\n}\n\n.menuHead_427795137 {\n padding: 6px 10px 5px;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n margin-bottom: 3px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.menuHeadStrong_427795137 {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.menuDanger_427795137 {\n color: var(--dsw-alias-status-danger, #d5304f);\n}\n\n.formField_427795137 {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.formLabel_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.formInput_427795137 {\n font: inherit;\n font-size: 13px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n padding: 7px 10px;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n outline: none;\n}\n\n.formInput_427795137:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n\n.formError_427795137 {\n font-size: 12px;\n color: var(--dsw-alias-status-danger, #d5304f);\n min-height: 16px;\n}\n\n/* Commit history */\n\n.historyHead_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n.searchBox_427795137 {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1;\n max-width: 300px;\n min-width: 140px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 7px;\n padding: 0 9px;\n height: 28px;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-bg-layer-3);\n}\n\n/* The branch card's head variant: sits right of the title and grows toward\n * the tools instead of being right-aligned by `margin-left: auto`. The\n * overrides win because both selectors are single classes and this one is\n * declared later. */\n.searchBoxHead_427795137 {\n margin-left: 0;\n flex: 1 1 auto;\n max-width: 320px;\n min-width: 90px;\n}\n\n.searchInput_427795137 {\n flex: 1;\n min-width: 0;\n border: 0;\n background: none;\n outline: none;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.searchInput_427795137::placeholder {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.commitRow_427795137 {\n position: relative;\n display: grid;\n grid-template-columns: 22px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 6px 8px 6px 2px;\n cursor: pointer;\n border-radius: 7px;\n}\n\n.commitRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.commitRowOpen_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitRowOpen_427795137:hover {\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.commitGraph_427795137 {\n position: relative;\n align-self: stretch;\n min-height: 34px;\n}\n\n.commitGraph_427795137::before {\n content: \"\";\n position: absolute;\n left: 10px;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--dsw-alias-border-l2);\n}\n\n.commitRowFirst_427795137 .commitGraph_427795137::before {\n top: 50%;\n}\n\n.commitRowLast_427795137 .commitGraph_427795137::before {\n bottom: 50%;\n}\n\n/* A small plain dot on the rail — the old 10px dot with a fat card-colored\n * ring read as a heavy black blob. #4c6bf5 for the same reason actPrimary\n * hardcodes it (the brand token does not resolve reliably in this panel). */\n.commitDot_427795137 {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #4c6bf5;\n}\n\n.commitDotMerge_427795137 {\n background: #8350d8;\n}\n\n.commitMain_427795137 {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n\n.commitSubject_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n font-size: 12.5px;\n color: var(--dsw-alias-label-primary);\n}\n\n.commitSubjectText_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.commitRowOpen_427795137 .commitSubjectText_427795137 {\n font-weight: 600;\n}\n\n.refBadges_427795137 {\n display: inline-flex;\n gap: 4px;\n flex: none;\n}\n\n.refBadge_427795137 {\n font-size: 10px;\n line-height: 1;\n border-radius: 4px;\n padding: 3px 5px;\n font-weight: 500;\n white-space: nowrap;\n max-width: 140px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.refHead_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary-bluish);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refTag_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-warning, #b07000);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refRemote_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: #8350d8;\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.refLocal_427795137 {\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-status-success, #218358);\n border: 0.5px solid var(--dsw-alias-border-l4);\n}\n\n.commitMeta_427795137 {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n}\n\n.commitSide_427795137 {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11.5px;\n flex: none;\n}\n\n.commitHash_427795137 {\n font-family: ui-monospace, Consolas, monospace;\n}\n\n.commitExpand_427795137 {\n grid-column: 1 / -1;\n padding: 2px 4px 8px 28px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.commitExpandTitle_427795137 {\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n margin-bottom: 3px;\n}\n\n.fileRow_427795137 {\n display: grid;\n grid-template-columns: 18px minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n padding: 3px 6px;\n border-radius: 6px;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.fileRow_427795137:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.filePath_427795137 {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11.5px;\n direction: rtl;\n text-align: left;\n}\n\n.fileNums_427795137 {\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n}\n\n.fileAdd_427795137 { color: var(--dsw-alias-status-success, #218358); }\n.fileDel_427795137 { color: var(--dsw-alias-status-danger, #d5304f); }\n\n.empty_427795137 {\n padding: 28px 16px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12.5px;\n}\n\n.errorText_427795137 {\n padding: 20px 16px;\n text-align: center;\n color: var(--dsw-alias-status-danger, #d5304f);\n font-size: 12.5px;\n}\n\n.moreRow_427795137 {\n display: flex;\n justify-content: center;\n padding: 8px 0 2px;\n}\n";
3526
3569
  const tagId$2 = "dsh-git-branch-manage/GitHistoryPanel.module.css";
3527
3570
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId$2 + "\"]") === null) {
3528
3571
  const tag = document.createElement("style");
@@ -3544,11 +3587,18 @@ window.__ModuleLoader__.load({
3544
3587
  "refreshBtn": "refreshBtn_427795137",
3545
3588
  "scroll": "scroll_427795137",
3546
3589
  "section": "section_427795137",
3590
+ "sectionFill": "sectionFill_427795137",
3591
+ "sectionChanges": "sectionChanges_427795137",
3592
+ "sectionBranches": "sectionBranches_427795137",
3593
+ "sectionHistory": "sectionHistory_427795137",
3547
3594
  "sectionHead": "sectionHead_427795137",
3548
3595
  "sectionTitle": "sectionTitle_427795137",
3549
3596
  "countBadge": "countBadge_427795137",
3550
3597
  "sectionTools": "sectionTools_427795137",
3551
3598
  "sectionBody": "sectionBody_427795137",
3599
+ "sectionBodyScroll": "sectionBodyScroll_427795137",
3600
+ "changeList": "changeList_427795137",
3601
+ "treeToggle": "treeToggle_427795137",
3552
3602
  "act": "act_427795137",
3553
3603
  "actPrimary": "actPrimary_427795137",
3554
3604
  "groupTitleRow": "groupTitleRow_427795137",
@@ -3596,6 +3646,7 @@ window.__ModuleLoader__.load({
3596
3646
  "formError": "formError_427795137",
3597
3647
  "historyHead": "historyHead_427795137",
3598
3648
  "searchBox": "searchBox_427795137",
3649
+ "searchBoxHead": "searchBoxHead_427795137",
3599
3650
  "searchInput": "searchInput_427795137",
3600
3651
  "commitRow": "commitRow_427795137",
3601
3652
  "commitRowOpen": "commitRowOpen_427795137",
@@ -3614,7 +3665,6 @@ window.__ModuleLoader__.load({
3614
3665
  "refRemote": "refRemote_427795137",
3615
3666
  "refLocal": "refLocal_427795137",
3616
3667
  "commitMeta": "commitMeta_427795137",
3617
- "authorDot": "authorDot_427795137",
3618
3668
  "commitSide": "commitSide_427795137",
3619
3669
  "commitHash": "commitHash_427795137",
3620
3670
  "commitExpand": "commitExpand_427795137",
@@ -3632,18 +3682,36 @@ window.__ModuleLoader__.load({
3632
3682
  //#region src/client/panel/BranchSection.tsx
3633
3683
  /**
3634
3684
  * BranchSection: the panel's middle block — the branch list with an IDEA-style
3635
- * action menu. Clicking any row opens a small menu anchored to it (checkout,
3636
- * new branch from here, rename, delete, copy name — remote rows narrow it to
3637
- * the actions a remote branch can take); the CURRENT branch's menu hides only
3638
- * checkout and delete, so rename/new-from/copy stay reachable. Rename and
3639
- * create run through the Modal primitive with the same client-side name
3640
- * validation the composer chip applies; delete confirms first and always uses
3641
- * the safe `-d` form (git refuses unmerged). Presentational: every action
3642
- * arrives as a callback; the menu/dialog state is local and resets when the
3643
- * branch list identity changes.
3685
+ * action menu. RIGHT-CLICKING any row opens a small menu anchored to it
3686
+ * (checkout, new branch from here, rename, delete, copy name — remote rows
3687
+ * narrow it to the actions a remote branch can take); the CURRENT branch's
3688
+ * menu hides only checkout and delete, so rename/new-from/copy stay
3689
+ * reachable. Keyboard rows keep Enter/Space as the menu opener — a context
3690
+ * menu has no keyboard twin. Rename and create run through the Modal
3691
+ * primitive with the same client-side name validation the composer chip
3692
+ * applies; delete confirms first and always uses the safe `-d` form (git
3693
+ * refuses unmerged). Presentational: every action arrives as a callback; the
3694
+ * menu/dialog state is local and resets when the branch list identity
3695
+ * changes.
3696
+ *
3697
+ * The tree opens on 本地 only (the current branch lives there) and the card
3698
+ * takes one share of the panel's fixed 2:1:2 split (its body scrolls inside
3699
+ * it), so a branch-heavy repository can no longer push the other cards off
3700
+ * screen. 全部展开/全部折叠 drive every group at once, and after a checkout
3701
+ * (or expand-all) the card re-centers on the current branch row.
3702
+ *
3703
+ * The head carries a search box: it filters both trees by branch name
3704
+ * (case-insensitive substring), and while a query is active every matching
3705
+ * group renders expanded with its count following the filter — the folded
3706
+ * state the user set is untouched and comes back when the query clears.
3644
3707
  *
3645
3708
  * @module git-worktree/client/panel/BranchSection
3646
3709
  */
3710
+ /** Groups folded when the card mounts: the remote tree only. The current
3711
+ * branch lives at the TOP of the local group (buildBranchRows sorts it
3712
+ * first), so 本地 stays open — the card opens showing what the panel is
3713
+ * about, without the wall of remote branches. */
3714
+ const COLLAPSED_ON_MOUNT = /* @__PURE__ */ new Set(["remote"]);
3647
3715
  /** The action menu as a body portal, fixed-positioned next to the row. */
3648
3716
  function BranchFloatMenu(props) {
3649
3717
  const { state, t, busy, onAction, onClose } = props;
@@ -3739,7 +3807,16 @@ window.__ModuleLoader__.load({
3739
3807
  const [menu, setMenu] = (0, react.useState)(null);
3740
3808
  const [dialog, setDialog] = (0, react.useState)(null);
3741
3809
  const [draft, setDraft] = (0, react.useState)("");
3742
- const [collapsedGroups, setCollapsedGroups] = (0, react.useState)(/* @__PURE__ */ new Set());
3810
+ const [query, setQuery] = (0, react.useState)("");
3811
+ const [collapsedGroups, setCollapsedGroups] = (0, react.useState)(COLLAPSED_ON_MOUNT);
3812
+ /** The card's scroll body — the locate math centers rows inside it. */
3813
+ const bodyRef = (0, react.useRef)(null);
3814
+ /** The current branch this mount last centered on. A refresh re-centers
3815
+ * only when the checkout moved; a same-branch refresh (stage, commit…)
3816
+ * keeps the scroll where the user left it. */
3817
+ const locatedRef = (0, react.useRef)(null);
3818
+ /** Expand-all asks for one more locate — its rows mount on the next commit. */
3819
+ const pendingLocateRef = (0, react.useRef)(false);
3743
3820
  const toggleGroup = (key) => {
3744
3821
  setCollapsedGroups((prev) => {
3745
3822
  const next = new Set(prev);
@@ -3748,7 +3825,41 @@ window.__ModuleLoader__.load({
3748
3825
  return next;
3749
3826
  });
3750
3827
  };
3828
+ const expandAll = () => {
3829
+ pendingLocateRef.current = true;
3830
+ setCollapsedGroups(/* @__PURE__ */ new Set());
3831
+ };
3832
+ const collapseAll = () => {
3833
+ setCollapsedGroups(new Set(branchGroupKeys(remotes)));
3834
+ };
3835
+ /** Center the current branch row in the card body (no scroll when the
3836
+ * body is shorter than its cap). Rect arithmetic, not offsetTop: no
3837
+ * positioned ancestor is guaranteed between the row and the body. */
3838
+ const locateCurrent = () => {
3839
+ const body = bodyRef.current;
3840
+ if (body === null) return;
3841
+ const row = body.querySelector("[data-current=\"true\"]");
3842
+ if (!(row instanceof HTMLElement)) return;
3843
+ const top = row.getBoundingClientRect().top - body.getBoundingClientRect().top + body.scrollTop;
3844
+ body.scrollTop = Math.max(0, top - (body.clientHeight - row.offsetHeight) / 2);
3845
+ };
3846
+ (0, react.useLayoutEffect)(() => {
3847
+ const current = locals.find((row) => row.current)?.name;
3848
+ if (current === void 0) return;
3849
+ if (!pendingLocateRef.current && current === locatedRef.current) return;
3850
+ pendingLocateRef.current = false;
3851
+ locatedRef.current = current;
3852
+ locateCurrent();
3853
+ });
3751
3854
  const total = locals.length + remotes.length;
3855
+ /** The head search: name-substring rows only. A live query opens every
3856
+ * group whatever the folded state is (a hidden match reads as a bug),
3857
+ * without touching that state — clearing the query restores it. */
3858
+ const searching = query.trim() !== "";
3859
+ const visibleLocals = filterBranches(locals, query);
3860
+ const visibleRemotes = filterBranches(remotes, query);
3861
+ const openGroup = (key) => searching || !collapsedGroups.has(key);
3862
+ const matched = visibleLocals.length + visibleRemotes.length;
3752
3863
  const openMenu = (branch, event) => {
3753
3864
  const rect = event.currentTarget.getBoundingClientRect();
3754
3865
  setMenu({
@@ -3803,7 +3914,7 @@ window.__ModuleLoader__.load({
3803
3914
  className: `${GitHistoryPanel_module_default.treeHead} ${nested ? GitHistoryPanel_module_default.treeHeadNest : ""}`,
3804
3915
  role: "button",
3805
3916
  tabIndex: 0,
3806
- "aria-expanded": !collapsedGroups.has(key),
3917
+ "aria-expanded": openGroup(key),
3807
3918
  onClick: () => toggleGroup(key),
3808
3919
  onKeyDown: (event) => {
3809
3920
  if (event.key === "Enter" || event.key === " ") {
@@ -3814,7 +3925,7 @@ window.__ModuleLoader__.load({
3814
3925
  children: [
3815
3926
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3816
3927
  className: GitHistoryPanel_module_default.treeChevron,
3817
- children: collapsedGroups.has(key) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, { size: 12 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { size: 12 })
3928
+ children: openGroup(key) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { size: 12 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, { size: 12 })
3818
3929
  }),
3819
3930
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }),
3820
3931
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -3825,9 +3936,13 @@ window.__ModuleLoader__.load({
3825
3936
  });
3826
3937
  const renderBranchRow = (row, displayName) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3827
3938
  className: `${GitHistoryPanel_module_default.branchRow} ${row.current ? GitHistoryPanel_module_default.branchRowCurrent : ""}`,
3939
+ "data-current": row.current ? "true" : void 0,
3828
3940
  role: "button",
3829
3941
  tabIndex: 0,
3830
- onClick: (event) => openMenu(row, event),
3942
+ onContextMenu: (event) => {
3943
+ event.preventDefault();
3944
+ openMenu(row, event);
3945
+ },
3831
3946
  onKeyDown: (event) => {
3832
3947
  if (event.key === "Enter" || event.key === " ") {
3833
3948
  event.preventDefault();
@@ -3851,39 +3966,75 @@ window.__ModuleLoader__.load({
3851
3966
  ]
3852
3967
  }, row.name);
3853
3968
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3854
- className: GitHistoryPanel_module_default.section,
3969
+ className: `${GitHistoryPanel_module_default.section} ${GitHistoryPanel_module_default.sectionFill} ${GitHistoryPanel_module_default.sectionBranches}`,
3855
3970
  "aria-label": t("panel.branches"),
3856
3971
  children: [
3857
3972
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3858
3973
  className: GitHistoryPanel_module_default.sectionHead,
3859
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3860
- className: GitHistoryPanel_module_default.sectionTitle,
3861
- children: t("panel.branches")
3862
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3863
- className: GitHistoryPanel_module_default.sectionTools,
3864
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
3865
- type: "button",
3866
- className: GitHistoryPanel_module_default.act,
3867
- disabled: busy,
3868
- onClick: () => openDialog("new"),
3869
- children: ["+ ", t("panel.branchNew")]
3974
+ children: [
3975
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
3976
+ className: GitHistoryPanel_module_default.sectionTitle,
3977
+ children: t("panel.branches")
3978
+ }),
3979
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3980
+ className: `${GitHistoryPanel_module_default.searchBox} ${GitHistoryPanel_module_default.searchBoxHead}`,
3981
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3982
+ "aria-hidden": "true",
3983
+ children: "⌕"
3984
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3985
+ type: "text",
3986
+ className: GitHistoryPanel_module_default.searchInput,
3987
+ value: query,
3988
+ placeholder: t("menuSearchPlaceholder"),
3989
+ "aria-label": t("menuSearchPlaceholder"),
3990
+ onChange: (event) => setQuery(event.target.value)
3991
+ })]
3992
+ }),
3993
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3994
+ className: GitHistoryPanel_module_default.sectionTools,
3995
+ children: [
3996
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3997
+ type: "button",
3998
+ className: GitHistoryPanel_module_default.treeToggle,
3999
+ onClick: expandAll,
4000
+ children: t("menuExpandAll")
4001
+ }),
4002
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4003
+ type: "button",
4004
+ className: GitHistoryPanel_module_default.treeToggle,
4005
+ onClick: collapseAll,
4006
+ children: t("menuCollapseAll")
4007
+ }),
4008
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4009
+ type: "button",
4010
+ className: GitHistoryPanel_module_default.act,
4011
+ disabled: busy,
4012
+ onClick: () => openDialog("new"),
4013
+ children: ["+ ", t("panel.branchNew")]
4014
+ })
4015
+ ]
3870
4016
  })
3871
- })]
4017
+ ]
3872
4018
  }),
3873
4019
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3874
- className: GitHistoryPanel_module_default.sectionBody,
4020
+ className: `${GitHistoryPanel_module_default.sectionBody} ${GitHistoryPanel_module_default.sectionBodyScroll}`,
4021
+ ref: bodyRef,
3875
4022
  children: [
3876
4023
  total === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3877
4024
  className: GitHistoryPanel_module_default.empty,
3878
4025
  children: t("menuNoBranches")
3879
4026
  }) : null,
3880
- locals.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [groupHead("local", t("panel.branchLocals"), locals.length, false), !collapsedGroups.has("local") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4027
+ total > 0 && matched === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4028
+ className: GitHistoryPanel_module_default.empty,
4029
+ children: t("menuNoMatches")
4030
+ }) : null,
4031
+ visibleLocals.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [groupHead("local", t("panel.branchLocals"), visibleLocals.length, false), openGroup("local") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3881
4032
  className: GitHistoryPanel_module_default.treeChildren,
3882
- children: locals.map((row) => renderBranchRow(row))
4033
+ children: visibleLocals.map((row) => renderBranchRow(row))
3883
4034
  }) : null] }) : null,
3884
- remotes.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [groupHead("remote", t("panel.branchRemotes"), remotes.length, false), !collapsedGroups.has("remote") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4035
+ visibleRemotes.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [groupHead("remote", t("panel.branchRemotes"), visibleRemotes.length, false), openGroup("remote") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3885
4036
  className: GitHistoryPanel_module_default.treeNest,
3886
- children: groupRemotes(remotes).map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [groupHead(`remote:${group.remote}`, group.remote, group.branches.length, true), !collapsedGroups.has(`remote:${group.remote}`) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4037
+ children: groupRemotes(visibleRemotes).map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [groupHead(`remote:${group.remote}`, group.remote, group.branches.length, true), openGroup(`remote:${group.remote}`) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3887
4038
  className: GitHistoryPanel_module_default.treeChildren,
3888
4039
  children: group.branches.map((row) => renderBranchRow(row, row.short))
3889
4040
  }) : null] }, group.remote))
@@ -4024,7 +4175,6 @@ window.__ModuleLoader__.load({
4024
4175
  const badges = classifyRefs(commit.refs);
4025
4176
  const when = new Date(commit.date);
4026
4177
  const absolute = Number.isFinite(commit.date) && commit.date > 0 ? `${when.toLocaleDateString()} ${when.toLocaleTimeString()}` : "";
4027
- const authorInitial = commit.authorName.slice(0, 1);
4028
4178
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4029
4179
  ref,
4030
4180
  className: [
@@ -4069,15 +4219,7 @@ window.__ModuleLoader__.load({
4069
4219
  })]
4070
4220
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4071
4221
  className: GitHistoryPanel_module_default.commitMeta,
4072
- children: [
4073
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4074
- className: GitHistoryPanel_module_default.authorDot,
4075
- "aria-hidden": "true",
4076
- children: authorInitial
4077
- }),
4078
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: commit.authorName }),
4079
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: timeLabelFor(commit.date) })
4080
- ]
4222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: commit.authorName }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: timeLabelFor(commit.date) })]
4081
4223
  })]
4082
4224
  }),
4083
4225
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -4122,7 +4264,7 @@ window.__ModuleLoader__.load({
4122
4264
  const needle = query.trim().toLowerCase();
4123
4265
  const visible = filterCommits(commits, query);
4124
4266
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4125
- className: GitHistoryPanel_module_default.section,
4267
+ className: `${GitHistoryPanel_module_default.section} ${GitHistoryPanel_module_default.sectionFill} ${GitHistoryPanel_module_default.sectionHistory}`,
4126
4268
  "aria-label": t("panel.history"),
4127
4269
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4128
4270
  className: GitHistoryPanel_module_default.historyHead,
@@ -4144,7 +4286,7 @@ window.__ModuleLoader__.load({
4144
4286
  })]
4145
4287
  })]
4146
4288
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4147
- className: GitHistoryPanel_module_default.sectionBody,
4289
+ className: `${GitHistoryPanel_module_default.sectionBody} ${GitHistoryPanel_module_default.sectionBodyScroll}`,
4148
4290
  children: [loadError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4149
4291
  className: GitHistoryPanel_module_default.errorText,
4150
4292
  children: loadError
@@ -4191,6 +4333,11 @@ window.__ModuleLoader__.load({
4191
4333
  * the draft, and every action arrive as props from the panel state owner, so
4192
4334
  * this file renders and never talks to git itself.
4193
4335
  *
4336
+ * The card takes two shares of the panel's fixed 2:1:2 split: the file
4337
+ * lists scroll inside their own region while the commit box stays pinned
4338
+ * below — a 500-change tree must not bury the commit affordance, and a
4339
+ * clean tree must not float it up either.
4340
+ *
4194
4341
  * The commit affordance is a split button: the main face commits, the caret
4195
4342
  * opens the two op variants (commit; commit and push). The AI button fills
4196
4343
  * the draft from the host's one-shot llm route — it renders only when the
@@ -4263,7 +4410,7 @@ window.__ModuleLoader__.load({
4263
4410
  const hint = staged.length === 0 ? t("panel.commitNeedsStaged") : commitDraft.trim() === "" ? t("panel.commitNeedsMessage") : "";
4264
4411
  const aiReady = aiAvailable && total > 0;
4265
4412
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4266
- className: GitHistoryPanel_module_default.section,
4413
+ className: `${GitHistoryPanel_module_default.section} ${GitHistoryPanel_module_default.sectionFill} ${GitHistoryPanel_module_default.sectionChanges}`,
4267
4414
  "aria-label": t("panel.uncommitted"),
4268
4415
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4269
4416
  className: GitHistoryPanel_module_default.sectionHead,
@@ -4308,129 +4455,131 @@ window.__ModuleLoader__.load({
4308
4455
  ]
4309
4456
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4310
4457
  className: GitHistoryPanel_module_default.sectionBody,
4311
- children: [
4312
- total === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4313
- className: GitHistoryPanel_module_default.empty,
4314
- children: t("panel.clean")
4315
- }) : null,
4316
- truncated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4317
- className: GitHistoryPanel_module_default.empty,
4318
- children: t("panel.changesTruncated")
4319
- }) : null,
4320
- staged.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4321
- className: GitHistoryPanel_module_default.groupTitleRow,
4322
- children: [
4323
- t("panel.staged"),
4324
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4325
- className: GitHistoryPanel_module_default.countBadge,
4326
- children: String(staged.length)
4327
- }),
4328
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4329
- type: "button",
4330
- className: GitHistoryPanel_module_default.linkBtn,
4331
- disabled: busy,
4332
- onClick: onUnstageAll,
4333
- children: t("panel.unstageAll")
4334
- })
4335
- ]
4336
- }), staged.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChangeRowView, {
4337
- row,
4338
- disabled: busy,
4339
- onToggle: onToggleRow,
4340
- t
4341
- }, `${row.staged}:${row.path}:${row.oldPath ?? ""}`))] }) : null,
4342
- unstaged.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4343
- className: GitHistoryPanel_module_default.groupTitleRow,
4344
- children: [
4345
- t("panel.unstaged"),
4346
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4347
- className: GitHistoryPanel_module_default.countBadge,
4348
- children: String(unstaged.length)
4349
- }),
4350
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4351
- type: "button",
4352
- className: GitHistoryPanel_module_default.linkBtn,
4353
- disabled: busy,
4354
- onClick: onStageAll,
4355
- children: t("panel.stageAll")
4356
- })
4357
- ]
4358
- }), unstaged.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChangeRowView, {
4359
- row,
4360
- disabled: busy,
4361
- onToggle: onToggleRow,
4362
- t
4363
- }, `u:${row.path}:${row.oldPath ?? ""}`))] }) : null,
4364
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4365
- className: GitHistoryPanel_module_default.commitBox,
4366
- children: [
4367
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
4368
- className: GitHistoryPanel_module_default.commitInput,
4369
- value: commitDraft,
4370
- placeholder: t("panel.commitPlaceholder"),
4371
- "aria-label": t("panel.commitPlaceholder"),
4372
- rows: 2,
4373
- maxLength: COMMIT_MESSAGE_LIMIT,
4374
- onChange: (event) => onCommitDraft(event.target.value)
4375
- }),
4376
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4377
- className: GitHistoryPanel_module_default.commitActions,
4378
- children: [aiAvailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4458
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4459
+ className: GitHistoryPanel_module_default.changeList,
4460
+ children: [
4461
+ total === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4462
+ className: GitHistoryPanel_module_default.empty,
4463
+ children: t("panel.clean")
4464
+ }) : null,
4465
+ truncated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4466
+ className: GitHistoryPanel_module_default.empty,
4467
+ children: t("panel.changesTruncated")
4468
+ }) : null,
4469
+ staged.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4470
+ className: GitHistoryPanel_module_default.groupTitleRow,
4471
+ children: [
4472
+ t("panel.staged"),
4473
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4474
+ className: GitHistoryPanel_module_default.countBadge,
4475
+ children: String(staged.length)
4476
+ }),
4477
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4379
4478
  type: "button",
4380
- className: GitHistoryPanel_module_default.act,
4381
- disabled: busy || aiBusy || !aiReady,
4382
- title: !aiReady ? t("panel.aiNeedsChanges") : void 0,
4383
- onClick: onAiGenerate,
4384
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkleRegular, { size: 13 }), aiBusy ? t("panel.aiBusy") : t("panel.aiGenerate")]
4385
- }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4386
- className: GitHistoryPanel_module_default.split,
4387
- ref: menuRef,
4388
- children: [
4389
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4479
+ className: GitHistoryPanel_module_default.linkBtn,
4480
+ disabled: busy,
4481
+ onClick: onUnstageAll,
4482
+ children: t("panel.unstageAll")
4483
+ })
4484
+ ]
4485
+ }), staged.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChangeRowView, {
4486
+ row,
4487
+ disabled: busy,
4488
+ onToggle: onToggleRow,
4489
+ t
4490
+ }, `${row.staged}:${row.path}:${row.oldPath ?? ""}`))] }) : null,
4491
+ unstaged.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4492
+ className: GitHistoryPanel_module_default.groupTitleRow,
4493
+ children: [
4494
+ t("panel.unstaged"),
4495
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4496
+ className: GitHistoryPanel_module_default.countBadge,
4497
+ children: String(unstaged.length)
4498
+ }),
4499
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4500
+ type: "button",
4501
+ className: GitHistoryPanel_module_default.linkBtn,
4502
+ disabled: busy,
4503
+ onClick: onStageAll,
4504
+ children: t("panel.stageAll")
4505
+ })
4506
+ ]
4507
+ }), unstaged.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChangeRowView, {
4508
+ row,
4509
+ disabled: busy,
4510
+ onToggle: onToggleRow,
4511
+ t
4512
+ }, `u:${row.path}:${row.oldPath ?? ""}`))] }) : null
4513
+ ]
4514
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4515
+ className: GitHistoryPanel_module_default.commitBox,
4516
+ children: [
4517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
4518
+ className: GitHistoryPanel_module_default.commitInput,
4519
+ value: commitDraft,
4520
+ placeholder: t("panel.commitPlaceholder"),
4521
+ "aria-label": t("panel.commitPlaceholder"),
4522
+ rows: 2,
4523
+ maxLength: COMMIT_MESSAGE_LIMIT,
4524
+ onChange: (event) => onCommitDraft(event.target.value)
4525
+ }),
4526
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4527
+ className: GitHistoryPanel_module_default.commitActions,
4528
+ children: [aiAvailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4529
+ type: "button",
4530
+ className: GitHistoryPanel_module_default.act,
4531
+ disabled: busy || aiBusy || !aiReady,
4532
+ title: !aiReady ? t("panel.aiNeedsChanges") : void 0,
4533
+ onClick: onAiGenerate,
4534
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkleRegular, { size: 13 }), aiBusy ? t("panel.aiBusy") : t("panel.aiGenerate")]
4535
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4536
+ className: GitHistoryPanel_module_default.split,
4537
+ ref: menuRef,
4538
+ children: [
4539
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4540
+ type: "button",
4541
+ className: `${GitHistoryPanel_module_default.act} ${GitHistoryPanel_module_default.actPrimary} ${GitHistoryPanel_module_default.splitMain}`,
4542
+ disabled: !canCommit || busy,
4543
+ onClick: () => onCommit(false),
4544
+ children: busy ? t("panel.commitBusy") : t("panel.commit")
4545
+ }),
4546
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4547
+ type: "button",
4548
+ className: `${GitHistoryPanel_module_default.act} ${GitHistoryPanel_module_default.actPrimary} ${GitHistoryPanel_module_default.splitCaret}`,
4549
+ "aria-label": t("panel.commitMoreAria"),
4550
+ "aria-expanded": commitMenuOpen,
4551
+ disabled: !canCommit || busy,
4552
+ onClick: onToggleCommitMenu,
4553
+ children: "▾"
4554
+ }),
4555
+ commitMenuOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4556
+ className: GitHistoryPanel_module_default.menu,
4557
+ role: "menu",
4558
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4390
4559
  type: "button",
4391
- className: `${GitHistoryPanel_module_default.act} ${GitHistoryPanel_module_default.actPrimary} ${GitHistoryPanel_module_default.splitMain}`,
4392
- disabled: !canCommit || busy,
4560
+ role: "menuitem",
4561
+ className: GitHistoryPanel_module_default.menuItem,
4562
+ disabled: !canCommit,
4393
4563
  onClick: () => onCommit(false),
4394
- children: busy ? t("panel.commitBusy") : t("panel.commit")
4395
- }),
4396
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4564
+ children: t("panel.commit")
4565
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4397
4566
  type: "button",
4398
- className: `${GitHistoryPanel_module_default.act} ${GitHistoryPanel_module_default.actPrimary} ${GitHistoryPanel_module_default.splitCaret}`,
4399
- "aria-label": t("panel.commitMoreAria"),
4400
- "aria-expanded": commitMenuOpen,
4401
- disabled: !canCommit || busy,
4402
- onClick: onToggleCommitMenu,
4403
- children: "▾"
4404
- }),
4405
- commitMenuOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4406
- className: GitHistoryPanel_module_default.menu,
4407
- role: "menu",
4408
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4409
- type: "button",
4410
- role: "menuitem",
4411
- className: GitHistoryPanel_module_default.menuItem,
4412
- disabled: !canCommit,
4413
- onClick: () => onCommit(false),
4414
- children: t("panel.commit")
4415
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4416
- type: "button",
4417
- role: "menuitem",
4418
- className: GitHistoryPanel_module_default.menuItem,
4419
- disabled: !canCommit,
4420
- onClick: () => onCommit(true),
4421
- children: t("panel.commitPush")
4422
- })]
4423
- }) : null
4424
- ]
4425
- })]
4426
- }),
4427
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4428
- className: GitHistoryPanel_module_default.commitHint,
4429
- children: hint
4430
- })
4431
- ]
4432
- })
4433
- ]
4567
+ role: "menuitem",
4568
+ className: GitHistoryPanel_module_default.menuItem,
4569
+ disabled: !canCommit,
4570
+ onClick: () => onCommit(true),
4571
+ children: t("panel.commitPush")
4572
+ })]
4573
+ }) : null
4574
+ ]
4575
+ })]
4576
+ }),
4577
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4578
+ className: GitHistoryPanel_module_default.commitHint,
4579
+ children: hint
4580
+ })
4581
+ ]
4582
+ })]
4434
4583
  })]
4435
4584
  });
4436
4585
  }
@@ -4572,16 +4721,20 @@ window.__ModuleLoader__.load({
4572
4721
  const refresh = (0, react.useCallback)(() => {
4573
4722
  loadAll();
4574
4723
  }, [loadAll]);
4724
+ (0, react.useEffect)(() => onGitMutated(refresh), [refresh]);
4575
4725
  (0, react.useImperativeHandle)(ref, () => ({ refresh }), [refresh]);
4576
4726
  const repo = status !== null && status.repo ? status : null;
4577
4727
  /** Run one mutating verb with the section-busy guard and error toast.
4578
4728
  * Mutations refresh through `loadAll` so their re-read bumps the request
4579
4729
  * sequence — an older in-flight snapshot can never land over the fresh
4580
- * post-mutation state. */
4730
+ * post-mutation state. A SUCCESS also emits the cross-surface notice so
4731
+ * the composer chip drops the branch git just left (stage toggles ride
4732
+ * along — one extra idempotent /status beats per-verb wiring). */
4581
4733
  const runGuarded = (0, react.useCallback)(async (verb) => {
4582
4734
  setSectionBusy(true);
4583
4735
  try {
4584
4736
  await verb();
4737
+ emitGitMutated();
4585
4738
  } catch (error) {
4586
4739
  pushToast(t("errorGeneric", { message: failureText(error) }), "error");
4587
4740
  } finally {
@@ -4703,7 +4856,10 @@ window.__ModuleLoader__.load({
4703
4856
  try {
4704
4857
  const result = await requestFetch(cwd);
4705
4858
  if (!result.ok) pushToast(t("errorGeneric", { message: result.error }), "error");
4706
- else pushToast(t("fetchDone"), "success");
4859
+ else {
4860
+ pushToast(t("fetchDone"), "success");
4861
+ emitGitMutated();
4862
+ }
4707
4863
  await loadAll();
4708
4864
  } finally {
4709
4865
  setSyncBusy(false);
@@ -4721,7 +4877,10 @@ window.__ModuleLoader__.load({
4721
4877
  try {
4722
4878
  const result = await requestUpdate(cwd);
4723
4879
  if (!result.ok) pushToast(t("errorGeneric", { message: result.error }), "error");
4724
- else pushToast(result.updated ? t("updateDone", { branch: result.branch }) : t("updateUpToDate"), "success");
4880
+ else {
4881
+ pushToast(result.updated ? t("updateDone", { branch: result.branch }) : t("updateUpToDate"), "success");
4882
+ emitGitMutated();
4883
+ }
4725
4884
  await loadAll();
4726
4885
  } finally {
4727
4886
  setSyncBusy(false);
@@ -4739,7 +4898,10 @@ window.__ModuleLoader__.load({
4739
4898
  try {
4740
4899
  const result = await requestPush(cwd);
4741
4900
  if (!result.ok) pushToast(t("errorGeneric", { message: result.error }), "error");
4742
- else pushToast(result.pushed ? t("pushDone", { branch: result.branch }) : t("pushUpToDate"), "success");
4901
+ else {
4902
+ pushToast(result.pushed ? t("pushDone", { branch: result.branch }) : t("pushUpToDate"), "success");
4903
+ emitGitMutated();
4904
+ }
4743
4905
  await loadAll();
4744
4906
  } finally {
4745
4907
  setSyncBusy(false);
package/lib/git.d.ts CHANGED
@@ -148,15 +148,17 @@ export declare function cutoutBranchName(exec: Exec, repoRoot: string, base: str
148
148
  */
149
149
  export declare function addWorktreeCutout(exec: Exec, repoRoot: string, base: string, newBranch: string, targetPath: string): Promise<void>;
150
150
  /**
151
- * In-place branch switch of the main checkout. A remote display name relies
151
+ * In-place branch switch of one checkout. A remote display name relies
152
152
  * on git's dwim: `git switch <local>` creates the tracking branch when
153
153
  * exactly one remote has it.
154
154
  * @param exec - executor seam.
155
- * @param repoRoot - main worktree directory.
155
+ * @param checkoutDir - the directory whose OWN checkout moves: the queried
156
+ * session's toplevel, so a linked worktree switches itself rather than the
157
+ * main checkout. `worktree prune` still runs here (it is repo-wide).
156
158
  * @param branch - branch display name.
157
159
  * @returns the local branch name now checked out.
158
160
  */
159
- export declare function switchBranch(exec: Exec, repoRoot: string, branch: string): Promise<string>;
161
+ export declare function switchBranch(exec: Exec, checkoutDir: string, branch: string): Promise<string>;
160
162
  /**
161
163
  * Create a NEW branch. Three shapes:
162
164
  *
package/lib/git.js CHANGED
@@ -374,20 +374,22 @@ export async function addWorktreeCutout(exec, repoRoot, base, newBranch, targetP
374
374
  await git(exec, repoRoot, ['worktree', 'add', targetPath, '-b', newBranch, base]);
375
375
  }
376
376
  /**
377
- * In-place branch switch of the main checkout. A remote display name relies
377
+ * In-place branch switch of one checkout. A remote display name relies
378
378
  * on git's dwim: `git switch <local>` creates the tracking branch when
379
379
  * exactly one remote has it.
380
380
  * @param exec - executor seam.
381
- * @param repoRoot - main worktree directory.
381
+ * @param checkoutDir - the directory whose OWN checkout moves: the queried
382
+ * session's toplevel, so a linked worktree switches itself rather than the
383
+ * main checkout. `worktree prune` still runs here (it is repo-wide).
382
384
  * @param branch - branch display name.
383
385
  * @returns the local branch name now checked out.
384
386
  */
385
- export async function switchBranch(exec, repoRoot, branch) {
386
- const resolved = await resolveBranch(exec, repoRoot, branch);
387
+ export async function switchBranch(exec, checkoutDir, branch) {
388
+ const resolved = await resolveBranch(exec, checkoutDir, branch);
387
389
  if (resolved === undefined)
388
390
  throw new GitError(['switch'], 1, `branch "${branch}" not found`);
389
391
  try {
390
- await git(exec, repoRoot, ['switch', resolved.local]);
392
+ await git(exec, checkoutDir, ['switch', resolved.local]);
391
393
  }
392
394
  catch (error) {
393
395
  // A stale worktree registration claims the branch; prune (no-op on a
@@ -395,8 +397,8 @@ export async function switchBranch(exec, repoRoot, branch) {
395
397
  const stale = error instanceof GitError && error.stderr.includes('already used by worktree');
396
398
  if (!stale)
397
399
  throw error;
398
- await git(exec, repoRoot, ['worktree', 'prune']);
399
- await git(exec, repoRoot, ['switch', resolved.local]);
400
+ await git(exec, checkoutDir, ['worktree', 'prune']);
401
+ await git(exec, checkoutDir, ['switch', resolved.local]);
400
402
  }
401
403
  return resolved.local;
402
404
  }
package/lib/routes.d.ts CHANGED
@@ -120,7 +120,12 @@ export declare function handleStatus(deps: RouteDeps, path: string | undefined):
120
120
  */
121
121
  export declare function handleCreateWorktree(deps: RouteDeps, body: unknown): Promise<RouteOutcome>;
122
122
  /**
123
- * POST /switch 鈥?in-place branch switch of the main checkout.
123
+ * POST /switch — in-place branch switch of the QUERIED directory's own
124
+ * checkout. A main-checkout session switches the main checkout; a linked
125
+ * worktree session switches ITS worktree. (It used to always run in the
126
+ * main checkout: a worktree session's checkout appeared to succeed while its
127
+ * own branch never moved. git itself refuses a branch another worktree
128
+ * holds — an honest error beats moving a checkout nobody was looking at.)
124
129
  * @param deps - host dependencies.
125
130
  * @param body - parsed request body.
126
131
  */
package/lib/routes.js CHANGED
@@ -471,7 +471,12 @@ export async function handleCreateWorktree(deps, body) {
471
471
  }
472
472
  }
473
473
  /**
474
- * POST /switch 鈥?in-place branch switch of the main checkout.
474
+ * POST /switch — in-place branch switch of the QUERIED directory's own
475
+ * checkout. A main-checkout session switches the main checkout; a linked
476
+ * worktree session switches ITS worktree. (It used to always run in the
477
+ * main checkout: a worktree session's checkout appeared to succeed while its
478
+ * own branch never moved. git itself refuses a branch another worktree
479
+ * holds — an honest error beats moving a checkout nobody was looking at.)
475
480
  * @param deps - host dependencies.
476
481
  * @param body - parsed request body.
477
482
  */
@@ -488,7 +493,7 @@ export async function handleSwitch(deps, body) {
488
493
  const facts = await probeRepo(deps.exec, repoPath, deps.dirExists);
489
494
  if (facts === undefined)
490
495
  return fail(400, `"${repoPath}" is not inside a git repository`);
491
- const switched = await switchBranch(deps.exec, facts.repoRoot, branch);
496
+ const switched = await switchBranch(deps.exec, facts.toplevel, branch);
492
497
  const result = { branch: switched };
493
498
  return { status: 200, body: result };
494
499
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-git-branch-manage",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "提供 git 分支与 worktree 管理:输入框工具栏显示当前分支、可原地切换;支持 IDEA 风格分支选择器、远程分支检出、右键操作菜单(新建/重命名/删除/复制)、推送与更新当前分支,以及把分支隔离到独立 worktree 工作区,并管理其创建与删除。",
5
5
  "keywords": [
6
6
  "dsh",