@young1lin/dsh-ui-gitworkbench 0.1.1 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  本文件记录面向使用者的变更。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
4
4
 
5
+ ## [0.1.3] - 2026-08-18
6
+
7
+ ### 修复
8
+
9
+ - **空目录裸 `npm i @young1lin/dsh-ui-gitworkbench` 报 E404(`@deepseek-ai/dsh-compact` not found)**:包的 peerDependency 之前全写 `*`,而 npm 7+ 自动安装 peer 时 `*` 按 **`latest` dist-tag** 解析——`@deepseek-ai/*` 全系的 `latest` 至今仍指 8 月 10 日的 `0.0.1-rc.1` 老线,那条线依赖一个从未发布到 npm 的 `dsh-compact`,于是任何不在 dsh profile 工作区里的裸安装必炸(8 月 17 日 dsh 发布 0.1.0-rc.7 后更多人在新环境首次安装撞上)。peer 范围改为显式区间:cordis `^4.0.1-rc.1`、dsh 系 `^0.1.0-rc.2`,不再依赖 dist-tag;`0.1.0-rc` 线的依赖链已实测可独立解析。官方通道 `dsh plugin --profile web add` 不受影响(profile 工作区自带全部 peer)。**0.1.2 的临时绕过**:`npm i @young1lin/dsh-ui-gitworkbench --legacy-peer-deps`。
10
+
11
+ ## [0.1.2] - 2026-08-17
12
+
13
+ ### 修复
14
+
15
+ - **抽屉关着时,会话头状态卡的统计不再冻结在挂载时刻**:此前 agent 整轮改文件期间,芯片上的文件数 / 增删行数 / ↑↓ 计数一直停在 turn 开始前的数字,只能点开抽屉强制刷新。现在芯片跟踪 dsh 会话 store 实时镜像的 `running` 信号——turn 一结束(agent 副作用落定的时刻)自动取一次最新统计:不重置抽屉里的树展开状态、不闪加载占位;turn 进行中不刷(数字在结束时一次到位),空闲会话保持零轮询开销。↑/↓ 计数随同一载荷返回,agent 提交完领先数立即对上。编辑器等会话外的改动仍以打开抽屉为准(维持原有设计)。
16
+
5
17
  ## [0.1.1] - 2026-08-17
6
18
 
7
19
  ### 修复
package/CHANGELOG_EN.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  User-facing changes, newest first. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows SemVer.
4
4
 
5
+ ## [0.1.3] - 2026-08-18
6
+
7
+ ### Fixed
8
+
9
+ - **A bare `npm i @young1lin/dsh-ui-gitworkbench` in an empty directory died with E404 (`@deepseek-ai/dsh-compact` not found).** Every peerDependency was `*`, and when npm 7+ auto-installs a peer, `*` resolves through the **`latest` dist-tag** — which every `@deepseek-ai/*` package still points at the August 10 `0.0.1-rc.1` line, whose dependency on `dsh-compact` was never published to npm. Any install outside a dsh profile workspace therefore 404ed (dsh's 0.1.0-rc.7 release on August 17 sent more people into fresh-environment installs and into the trap). Peer ranges are now explicit — cordis `^4.0.1-rc.1`, the dsh packages `^0.1.0-rc.2` — so resolution no longer consults dist-tags; the `0.1.0-rc` dependency chain is verified to resolve standalone. The official channel, `dsh plugin --profile web add`, was never affected (the profile workspace already carries the peers). **Workaround while on 0.1.2**: `npm i @young1lin/dsh-ui-gitworkbench --legacy-peer-deps`.
10
+
11
+ ## [0.1.2] - 2026-08-17
12
+
13
+ ### Fixed
14
+
15
+ - **The session-header stats card no longer freezes while the drawer is shut.** Through a whole agent turn of edits, the chip's file count, +/− line totals and ahead/behind markers kept showing pre-turn numbers until you opened the drawer and forced a refresh. The chip now tracks the `running` signal dsh mirrors live in the sessions store: the moment a turn ends — when agent side effects have settled — it fetches fresh stats on its own, without resetting the drawer's tree expansion or flashing a loading placeholder. Mid-turn it does not churn (numbers land once, at the end) and idle sessions stay free of polling. Ahead/behind ride in the same payload, so the ↑ count catches up right after the agent commits. Out-of-session edits (your editor, other shells) still wait for the drawer to open, as before.
16
+
5
17
  ## [0.1.1] - 2026-08-17
6
18
 
7
19
  ### Fixed
package/README.md CHANGED
@@ -346,6 +346,9 @@ window.__ModuleLoader__.load({ id: "@young1lin/dsh-ui-gitworkbench", factory: (r
346
346
  ### 6.13 宿主环境可能没有 git(PATH 缺失)
347
347
  宿主 RPC 返回 `git status failed (exit N): <stderr>`(本插件的报错都带 exit code + stderr 尾部)时,先看 stderr——常见是宿主进程环境异常/git 不在 PATH,而不是目录真的不是仓库(2026-08-15 实例:目录明明是仓库却报 not a git worktree,重启 dsh web 换个健康环境即愈)。**报错透出 stderr 是定位这类问题的唯一手段**,新加 git 调用时照抄这个格式。
348
348
 
349
+ ### 6.14 发布的 peer 范围不能写 `*`——`*` 按 `latest` dist-tag 解析
350
+ npm 7+ 自动安装 peer 时,`*` 走 **`latest` dist-tag**,不是「取版本列表最高」。`@deepseek-ai/*` 全系的 `latest` 长期停在 8 月 10 日的 `0.0.1-rc.1` 老线(那条线依赖**从未发布**的 `@deepseek-ai/dsh-compact`,公开安装必 404),能用的 `0.1.0-rc.x` 全挂 `next`。于是 0.1.2 之前任何不在 dsh profile 工作区里的裸 `npm i` 都炸 E404。规则:**peer 写显式区间**(cordis `^4.0.1-rc.1`、dsh 系 `^0.1.0-rc.2`),dsh 发新线时同步抬范围并验证 `npm pack` 出的 tarball 在空目录可装。注意 6.5 的 `auto-install-peers=false` 只管本仓库 pnpm 开发态,管不了用户侧 npm。
351
+
349
352
  ---
350
353
 
351
354
  ## 7. dsh 仓库里的关键参考文件(去哪里抄)
package/lib/client.js CHANGED
@@ -11472,6 +11472,32 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11472
11472
  return (probe.name ?? "") !== (shown?.name ?? "");
11473
11473
  }
11474
11474
  /**
11475
+ * Whether a turn that was in flight has just come to an end.
11476
+ *
11477
+ * The shut chip's stats freeze on mount: the fetch that carries them re-runs
11478
+ * only on source switches and gen bumps, and the 3-15s poll starts at
11479
+ * `if (!open) return`. So an agent that wrote files all turn left the header
11480
+ * counting the tree as it was before the turn — until someone opened the
11481
+ * drawer, which is the one act that already refreshes everything.
11482
+ *
11483
+ * `running` is mirrored live by the sessions store, and a turn boundary is
11484
+ * when agent-caused side effects have stopped accumulating — the one instant
11485
+ * a shut chip should pay for fresh numbers. One fetch per turn, and none for
11486
+ * an idle session, which is the cost rule this panel lives under (it mounts
11487
+ * in every session header).
11488
+ *
11489
+ * The transition is read strictly: `false → true` is a turn STARTING, whose
11490
+ * numbers are seconds away from being rewritten, and a missing `next` counts
11491
+ * as ended because a turn in flight cannot still be in flight once the store
11492
+ * stops saying so.
11493
+ *
11494
+ * @param prevRunning - whether the agent had a turn in flight last render.
11495
+ * @param nextRunning - whether it has one now.
11496
+ */
11497
+ function turnSettled(prevRunning, nextRunning) {
11498
+ return prevRunning === true && nextRunning !== true;
11499
+ }
11500
+ /**
11475
11501
  * Whether a view should say "pending" rather than show what it has.
11476
11502
  *
11477
11503
  * There are two different loads behind one flag. A worktree SWITCH empties the
@@ -11597,196 +11623,196 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
11597
11623
  document.head.appendChild(tag);
11598
11624
  }
11599
11625
  var GitWorkbenchPanel_module_css_default = {
11626
+ "resizerActive": "SD8qLW_resizerActive",
11627
+ "stAdded": "SD8qLW_stAdded",
11628
+ "sliderRow": "SD8qLW_sliderRow",
11629
+ "empty": "SD8qLW_empty",
11630
+ "btnBehind": "SD8qLW_btnBehind",
11631
+ "cardBehind": "SD8qLW_cardBehind",
11632
+ "headerTotalsDel": "SD8qLW_headerTotalsDel",
11633
+ "refValue": "SD8qLW_refValue",
11634
+ "opBannerBad": "SD8qLW_opBannerBad",
11635
+ "refLabel": "SD8qLW_refLabel",
11636
+ "tabs": "SD8qLW_tabs",
11600
11637
  "lineHunk": "SD8qLW_lineHunk",
11601
- "treeRow": "SD8qLW_treeRow",
11602
- "checkMarkPartial": "SD8qLW_checkMarkPartial",
11603
- "refRowName": "SD8qLW_refRowName",
11604
- "elide": "SD8qLW_elide",
11638
+ "sliderValue": "SD8qLW_sliderValue",
11639
+ "commitHasBody": "SD8qLW_commitHasBody",
11640
+ "elideHead": "SD8qLW_elideHead",
11641
+ "headerView": "SD8qLW_headerView",
11605
11642
  "refSearch": "SD8qLW_refSearch",
11606
- "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
11607
- "paneTitle": "SD8qLW_paneTitle",
11608
- "commitBtn": "SD8qLW_commitBtn",
11643
+ "commitHash": "SD8qLW_commitHash",
11644
+ "cardAdded": "SD8qLW_cardAdded",
11645
+ "opBanner": "SD8qLW_opBanner",
11646
+ "opBannerOk": "SD8qLW_opBannerOk",
11609
11647
  "paletteRowActive": "SD8qLW_paletteRowActive",
11610
- "commitPopBody": "SD8qLW_commitPopBody",
11611
- "lineDel": "SD8qLW_lineDel",
11612
- "syncSpacer": "SD8qLW_syncSpacer",
11613
- "tree": "SD8qLW_tree",
11614
- "lineAdd": "SD8qLW_lineAdd",
11615
- "cardDeleted": "SD8qLW_cardDeleted",
11616
- "tab": "SD8qLW_tab",
11617
- "commitLine": "SD8qLW_commitLine",
11618
- "miniBtn": "SD8qLW_miniBtn",
11619
- "cardDetached": "SD8qLW_cardDetached",
11620
- "diffPane": "SD8qLW_diffPane",
11621
- "refFoot": "SD8qLW_refFoot",
11622
- "commitTop": "SD8qLW_commitTop",
11623
- "treeEmpty": "SD8qLW_treeEmpty",
11648
+ "renameLine": "SD8qLW_renameLine",
11649
+ "treeDir": "SD8qLW_treeDir",
11650
+ "cardSep": "SD8qLW_cardSep",
11651
+ "cssArea": "SD8qLW_cssArea",
11652
+ "headerTotalsDim": "SD8qLW_headerTotalsDim",
11653
+ "commitsSentinel": "SD8qLW_commitsSentinel",
11654
+ "stDeleted": "SD8qLW_stDeleted",
11655
+ "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
11656
+ "refButton": "SD8qLW_refButton",
11657
+ "headerTotals": "SD8qLW_headerTotals",
11624
11658
  "chevron": "SD8qLW_chevron",
11625
- "checkBox": "SD8qLW_checkBox",
11626
- "scopeBtnActive": "SD8qLW_scopeBtnActive",
11627
- "commitLead": "SD8qLW_commitLead",
11628
- "treeSub": "SD8qLW_treeSub",
11629
- "stUntracked": "SD8qLW_stUntracked",
11630
- "tabActive": "SD8qLW_tabActive",
11659
+ "stRenamed": "SD8qLW_stRenamed",
11660
+ "gutter": "SD8qLW_gutter",
11661
+ "fileBinary": "SD8qLW_fileBinary",
11662
+ "commitsFoot": "SD8qLW_commitsFoot",
11663
+ "commitSubject": "SD8qLW_commitSubject",
11664
+ "headerBranch": "SD8qLW_headerBranch",
11665
+ "headerPathMain": "SD8qLW_headerPathMain",
11666
+ "commitSubjectRow": "SD8qLW_commitSubjectRow",
11667
+ "chevronOpen": "SD8qLW_chevronOpen",
11668
+ "fileCounts": "SD8qLW_fileCounts",
11631
11669
  "commitStaged": "SD8qLW_commitStaged",
11632
- "headerTotals": "SD8qLW_headerTotals",
11633
- "headerTotalsDim": "SD8qLW_headerTotalsDim",
11634
- "body": "SD8qLW_body",
11635
- "syncBar": "SD8qLW_syncBar",
11636
- "commitBox": "SD8qLW_commitBox",
11670
+ "treeEmpty": "SD8qLW_treeEmpty",
11637
11671
  "btnCount": "SD8qLW_btnCount",
11638
- "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
11639
- "cardAdded": "SD8qLW_cardAdded",
11640
- "swatch": "SD8qLW_swatch",
11641
- "sliderRow": "SD8qLW_sliderRow",
11672
+ "chipSystem": "SD8qLW_chipSystem",
11673
+ "commit": "SD8qLW_commit",
11674
+ "treeDirName": "SD8qLW_treeDirName",
11675
+ "cardFiles": "SD8qLW_cardFiles",
11676
+ "commitBtn": "SD8qLW_commitBtn",
11677
+ "paneTitle": "SD8qLW_paneTitle",
11678
+ "body": "SD8qLW_body",
11679
+ "wordAdd": "SD8qLW_wordAdd",
11680
+ "syncUpstream": "SD8qLW_syncUpstream",
11681
+ "treeLead": "SD8qLW_treeLead",
11682
+ "headerRight": "SD8qLW_headerRight",
11683
+ "scopeRow": "SD8qLW_scopeRow",
11684
+ "elideTail": "SD8qLW_elideTail",
11685
+ "overlayMax": "SD8qLW_overlayMax",
11686
+ "lineAdd": "SD8qLW_lineAdd",
11687
+ "treeDirCount": "SD8qLW_treeDirCount",
11688
+ "treeCol": "SD8qLW_treeCol",
11642
11689
  "header": "SD8qLW_header",
11643
- "sliderValue": "SD8qLW_sliderValue",
11644
- "compareArrow": "SD8qLW_compareArrow",
11645
- "commitsPane": "SD8qLW_commitsPane",
11690
+ "segmentChip": "SD8qLW_segmentChip",
11691
+ "wtCurrent": "SD8qLW_wtCurrent",
11692
+ "scopeHint": "SD8qLW_scopeHint",
11693
+ "cardWt": "SD8qLW_cardWt",
11694
+ "refRowName": "SD8qLW_refRowName",
11695
+ "cardDeleted": "SD8qLW_cardDeleted",
11646
11696
  "chipLight": "SD8qLW_chipLight",
11647
- "headerView": "SD8qLW_headerView",
11648
- "gsSlide": "SD8qLW_gsSlide",
11649
- "paneHead": "SD8qLW_paneHead",
11650
- "commitsSentinel": "SD8qLW_commitsSentinel",
11651
- "checkMarkOn": "SD8qLW_checkMarkOn",
11697
+ "overlay": "SD8qLW_overlay",
11698
+ "swatch": "SD8qLW_swatch",
11699
+ "themeRail": "SD8qLW_themeRail",
11700
+ "lineDel": "SD8qLW_lineDel",
11701
+ "treeIcon": "SD8qLW_treeIcon",
11702
+ "segment": "SD8qLW_segment",
11703
+ "themeNote": "SD8qLW_themeNote",
11704
+ "refRow": "SD8qLW_refRow",
11705
+ "cardAhead": "SD8qLW_cardAhead",
11652
11706
  "commitRow": "SD8qLW_commitRow",
11653
- "headerLeft": "SD8qLW_headerLeft",
11654
- "treeDirLi": "SD8qLW_treeDirLi",
11655
- "fileActive": "SD8qLW_fileActive",
11656
- "cardWt": "SD8qLW_cardWt",
11707
+ "refRowSpacer": "SD8qLW_refRowSpacer",
11708
+ "signDel": "SD8qLW_signDel",
11709
+ "commitLine": "SD8qLW_commitLine",
11710
+ "tree": "SD8qLW_tree",
11711
+ "themeGroup": "SD8qLW_themeGroup",
11712
+ "headerDetached": "SD8qLW_headerDetached",
11657
11713
  "bgEmpty": "SD8qLW_bgEmpty",
11658
- "cardGlyph": "SD8qLW_cardGlyph",
11659
- "elideHead": "SD8qLW_elideHead",
11660
- "commitWhen": "SD8qLW_commitWhen",
11661
- "commitHasBody": "SD8qLW_commitHasBody",
11662
- "fileCountDel": "SD8qLW_fileCountDel",
11663
- "syncLevel": "SD8qLW_syncLevel",
11664
- "treeIcon": "SD8qLW_treeIcon",
11714
+ "commitLead": "SD8qLW_commitLead",
11715
+ "headerViewRef": "SD8qLW_headerViewRef",
11716
+ "fileActive": "SD8qLW_fileActive",
11665
11717
  "scopeBtn": "SD8qLW_scopeBtn",
11666
- "card": "SD8qLW_card",
11667
- "refRowSpacer": "SD8qLW_refRowSpacer",
11668
- "refList": "SD8qLW_refList",
11669
- "treeDirCount": "SD8qLW_treeDirCount",
11670
- "treeLead": "SD8qLW_treeLead",
11671
- "theme": "SD8qLW_theme",
11672
11718
  "commitCopy": "SD8qLW_commitCopy",
11673
- "cardFiles": "SD8qLW_cardFiles",
11674
- "paneDividerActive": "SD8qLW_paneDividerActive",
11675
- "diffPre": "SD8qLW_diffPre",
11676
- "themeNote": "SD8qLW_themeNote",
11677
- "stDeleted": "SD8qLW_stDeleted",
11678
- "commit": "SD8qLW_commit",
11679
- "lnNew": "SD8qLW_lnNew",
11719
+ "syncLevel": "SD8qLW_syncLevel",
11720
+ "bgPreview": "SD8qLW_bgPreview",
11680
11721
  "commitMessage": "SD8qLW_commitMessage",
11681
- "treeLabel": "SD8qLW_treeLabel",
11682
- "commitSubjectRow": "SD8qLW_commitSubjectRow",
11683
- "headerDetached": "SD8qLW_headerDetached",
11684
- "themeLabel": "SD8qLW_themeLabel",
11685
- "commitHash": "SD8qLW_commitHash",
11686
- "overlayMax": "SD8qLW_overlayMax",
11722
+ "commitAmend": "SD8qLW_commitAmend",
11723
+ "graphCell": "SD8qLW_graphCell",
11724
+ "paneHead": "SD8qLW_paneHead",
11725
+ "refFoot": "SD8qLW_refFoot",
11726
+ "treeSub": "SD8qLW_treeSub",
11727
+ "cardBranchName": "SD8qLW_cardBranchName",
11728
+ "cardGlyph": "SD8qLW_cardGlyph",
11729
+ "segmented": "SD8qLW_segmented",
11730
+ "commitPopBody": "SD8qLW_commitPopBody",
11731
+ "gsFade": "SD8qLW_gsFade",
11687
11732
  "treeDirCounts": "SD8qLW_treeDirCounts",
11688
- "commitPopTop": "SD8qLW_commitPopTop",
11689
- "fileCounts": "SD8qLW_fileCounts",
11690
- "headerPathMain": "SD8qLW_headerPathMain",
11691
- "empty": "SD8qLW_empty",
11692
- "treeIconGlyph": "SD8qLW_treeIconGlyph",
11693
- "elideTail": "SD8qLW_elideTail",
11694
- "btnAhead": "SD8qLW_btnAhead",
11733
+ "diffPane": "SD8qLW_diffPane",
11734
+ "syncSpacer": "SD8qLW_syncSpacer",
11735
+ "commitBox": "SD8qLW_commitBox",
11736
+ "compareBar": "SD8qLW_compareBar",
11737
+ "wordDel": "SD8qLW_wordDel",
11695
11738
  "code": "SD8qLW_code",
11696
- "headerRight": "SD8qLW_headerRight",
11697
- "refEmpty": "SD8qLW_refEmpty",
11698
- "refPop": "SD8qLW_refPop",
11699
- "cardSep": "SD8qLW_cardSep",
11700
- "themeGroup": "SD8qLW_themeGroup",
11701
- "file": "SD8qLW_file",
11702
- "fileCountAdd": "SD8qLW_fileCountAdd",
11703
- "lnOld": "SD8qLW_lnOld",
11704
- "headerViewRef": "SD8qLW_headerViewRef",
11705
- "themeDirty": "SD8qLW_themeDirty",
11706
11739
  "treeIconDown": "SD8qLW_treeIconDown",
11707
- "segmentActive": "SD8qLW_segmentActive",
11708
- "pullGroup": "SD8qLW_pullGroup",
11709
- "wordAdd": "SD8qLW_wordAdd",
11710
11740
  "btnClose": "SD8qLW_btnClose",
11711
- "stModified": "SD8qLW_stModified",
11712
- "overlay": "SD8qLW_overlay",
11713
- "line": "SD8qLW_line",
11714
- "bgPreview": "SD8qLW_bgPreview",
11715
- "wordDel": "SD8qLW_wordDel",
11716
- "commitRefMore": "SD8qLW_commitRefMore",
11717
- "headerBranch": "SD8qLW_headerBranch",
11741
+ "treeIconGlyph": "SD8qLW_treeIconGlyph",
11742
+ "treeTools": "SD8qLW_treeTools",
11743
+ "scopeBtnActive": "SD8qLW_scopeBtnActive",
11744
+ "filePath": "SD8qLW_filePath",
11745
+ "commitActive": "SD8qLW_commitActive",
11746
+ "btn": "SD8qLW_btn",
11747
+ "treeActions": "SD8qLW_treeActions",
11748
+ "lnOld": "SD8qLW_lnOld",
11749
+ "btnAhead": "SD8qLW_btnAhead",
11718
11750
  "lineContext": "SD8qLW_lineContext",
11719
- "btnBehind": "SD8qLW_btnBehind",
11720
- "signDel": "SD8qLW_signDel",
11721
- "btnIcon": "SD8qLW_btnIcon",
11722
- "checkMark": "SD8qLW_checkMark",
11751
+ "treeRow": "SD8qLW_treeRow",
11752
+ "fileStatus": "SD8qLW_fileStatus",
11753
+ "commitPopTop": "SD8qLW_commitPopTop",
11754
+ "settingsPop": "SD8qLW_settingsPop",
11755
+ "chipDark": "SD8qLW_chipDark",
11756
+ "refList": "SD8qLW_refList",
11757
+ "commits": "SD8qLW_commits",
11758
+ "cardDetached": "SD8qLW_cardDetached",
11759
+ "treeDirLi": "SD8qLW_treeDirLi",
11723
11760
  "signAdd": "SD8qLW_signAdd",
11724
- "opBannerOk": "SD8qLW_opBannerOk",
11761
+ "refRowActive": "SD8qLW_refRowActive",
11725
11762
  "treeWrap": "SD8qLW_treeWrap",
11726
- "themeRowSplit": "SD8qLW_themeRowSplit",
11727
- "refPicker": "SD8qLW_refPicker",
11728
- "stAdded": "SD8qLW_stAdded",
11729
- "gutter": "SD8qLW_gutter",
11730
- "tabs": "SD8qLW_tabs",
11731
- "treeDir": "SD8qLW_treeDir",
11732
- "renameLine": "SD8qLW_renameLine",
11733
- "wtCurrent": "SD8qLW_wtCurrent",
11734
- "cardBehind": "SD8qLW_cardBehind",
11763
+ "checkBox": "SD8qLW_checkBox",
11735
11764
  "refGroup": "SD8qLW_refGroup",
11736
- "commitActive": "SD8qLW_commitActive",
11737
- "commits": "SD8qLW_commits",
11738
- "stRenamed": "SD8qLW_stRenamed",
11739
- "fileLi": "SD8qLW_fileLi",
11740
- "cssArea": "SD8qLW_cssArea",
11741
- "treeDirActive": "SD8qLW_treeDirActive",
11742
- "commitRef": "SD8qLW_commitRef",
11743
- "chipDark": "SD8qLW_chipDark",
11744
- "treeCol": "SD8qLW_treeCol",
11745
- "compareBar": "SD8qLW_compareBar",
11765
+ "stUntracked": "SD8qLW_stUntracked",
11766
+ "themeLabel": "SD8qLW_themeLabel",
11767
+ "commitWhen": "SD8qLW_commitWhen",
11768
+ "diffPre": "SD8qLW_diffPre",
11746
11769
  "btnPrimary": "SD8qLW_btnPrimary",
11747
- "refCaret": "SD8qLW_refCaret",
11748
- "refRow": "SD8qLW_refRow",
11749
- "headerPicker": "SD8qLW_headerPicker",
11750
- "chipSystem": "SD8qLW_chipSystem",
11751
- "refRowActive": "SD8qLW_refRowActive",
11752
- "syncUpstream": "SD8qLW_syncUpstream",
11753
- "graphCell": "SD8qLW_graphCell",
11754
- "scopeHint": "SD8qLW_scopeHint",
11755
- "refButton": "SD8qLW_refButton",
11756
- "segmented": "SD8qLW_segmented",
11757
- "paneDivider": "SD8qLW_paneDivider",
11770
+ "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
11758
11771
  "paletteRow": "SD8qLW_paletteRow",
11759
- "segment": "SD8qLW_segment",
11760
- "scopeRow": "SD8qLW_scopeRow",
11761
- "treeDirName": "SD8qLW_treeDirName",
11762
- "fileStatus": "SD8qLW_fileStatus",
11763
- "commitsFoot": "SD8qLW_commitsFoot",
11764
- "fileBinary": "SD8qLW_fileBinary",
11765
- "segmentChip": "SD8qLW_segmentChip",
11766
- "commitAmend": "SD8qLW_commitAmend",
11772
+ "themeDirty": "SD8qLW_themeDirty",
11773
+ "commitPop": "SD8qLW_commitPop",
11774
+ "lnNew": "SD8qLW_lnNew",
11775
+ "segmentActive": "SD8qLW_segmentActive",
11776
+ "theme": "SD8qLW_theme",
11777
+ "checkMark": "SD8qLW_checkMark",
11778
+ "stModified": "SD8qLW_stModified",
11779
+ "btnIcon": "SD8qLW_btnIcon",
11780
+ "gsSlide": "SD8qLW_gsSlide",
11781
+ "paneDividerActive": "SD8qLW_paneDividerActive",
11782
+ "tab": "SD8qLW_tab",
11783
+ "commitsPane": "SD8qLW_commitsPane",
11784
+ "checkMarkOn": "SD8qLW_checkMarkOn",
11785
+ "tabActive": "SD8qLW_tabActive",
11786
+ "card": "SD8qLW_card",
11787
+ "treeLabel": "SD8qLW_treeLabel",
11788
+ "headerPicker": "SD8qLW_headerPicker",
11789
+ "elide": "SD8qLW_elide",
11790
+ "refPicker": "SD8qLW_refPicker",
11791
+ "fileCountAdd": "SD8qLW_fileCountAdd",
11792
+ "fileCountDel": "SD8qLW_fileCountDel",
11793
+ "cardBranch": "SD8qLW_cardBranch",
11794
+ "syncBar": "SD8qLW_syncBar",
11795
+ "commitTop": "SD8qLW_commitTop",
11796
+ "fileLi": "SD8qLW_fileLi",
11797
+ "commitRef": "SD8qLW_commitRef",
11798
+ "refEmpty": "SD8qLW_refEmpty",
11799
+ "resizer": "SD8qLW_resizer",
11767
11800
  "drawer": "SD8qLW_drawer",
11768
- "gsFade": "SD8qLW_gsFade",
11769
- "commitSubject": "SD8qLW_commitSubject",
11770
- "treeTools": "SD8qLW_treeTools",
11771
- "treeActions": "SD8qLW_treeActions",
11801
+ "themeRowSplit": "SD8qLW_themeRowSplit",
11802
+ "compareArrow": "SD8qLW_compareArrow",
11803
+ "paneDivider": "SD8qLW_paneDivider",
11804
+ "headerLeft": "SD8qLW_headerLeft",
11772
11805
  "commitPopSubject": "SD8qLW_commitPopSubject",
11773
- "cardAhead": "SD8qLW_cardAhead",
11774
- "refLabel": "SD8qLW_refLabel",
11775
- "opBanner": "SD8qLW_opBanner",
11776
- "themeRail": "SD8qLW_themeRail",
11777
- "headerTotalsDel": "SD8qLW_headerTotalsDel",
11778
- "cardBranch": "SD8qLW_cardBranch",
11779
- "cardBranchName": "SD8qLW_cardBranchName",
11780
11806
  "menuPop": "SD8qLW_menuPop",
11781
- "refValue": "SD8qLW_refValue",
11782
- "resizerActive": "SD8qLW_resizerActive",
11783
- "resizer": "SD8qLW_resizer",
11784
- "chevronOpen": "SD8qLW_chevronOpen",
11785
- "filePath": "SD8qLW_filePath",
11786
- "btn": "SD8qLW_btn",
11787
- "commitPop": "SD8qLW_commitPop",
11788
- "settingsPop": "SD8qLW_settingsPop",
11789
- "opBannerBad": "SD8qLW_opBannerBad"
11807
+ "treeDirActive": "SD8qLW_treeDirActive",
11808
+ "line": "SD8qLW_line",
11809
+ "miniBtn": "SD8qLW_miniBtn",
11810
+ "refCaret": "SD8qLW_refCaret",
11811
+ "checkMarkPartial": "SD8qLW_checkMarkPartial",
11812
+ "pullGroup": "SD8qLW_pullGroup",
11813
+ "commitRefMore": "SD8qLW_commitRefMore",
11814
+ "refPop": "SD8qLW_refPop",
11815
+ "file": "SD8qLW_file"
11790
11816
  };
11791
11817
  //#endregion
11792
11818
  //#region src/client/GitWorkbenchPanel.tsx
@@ -12219,6 +12245,46 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
12219
12245
  fetchSessionBinding,
12220
12246
  fetchWorktreeStatus
12221
12247
  ]);
12248
+ /** The agent's `running` on the previous render. The flag itself says
12249
+ * whether a turn is in flight; only the EDGE of it says the turn has
12250
+ * ended, and the edge is what the effect below keys on. */
12251
+ const wasRunningRef = (0, react.useRef)(void 0);
12252
+ /**
12253
+ * Refresh the SHUT chip's stats when a turn ends.
12254
+ *
12255
+ * The keyed fetch below runs on mount, on source switches and on gen bumps,
12256
+ * and the 3-15s poll starts at `if (!open) return` — so while the drawer was
12257
+ * shut, an agent that wrote files all turn left the header counting the tree
12258
+ * as it stood before the turn. Opening the drawer was the only thing that
12259
+ * refreshed it, and an indicator you must open to read is not an indicator.
12260
+ *
12261
+ * `running` is mirrored live by the sessions store, and a turn boundary is
12262
+ * when agent-caused side effects have settled ({@link turnSettled}), so one
12263
+ * fetch per turn buys the chip the numbers the turn just made true — ahead
12264
+ * counts included, which ride along in the same payload. The write follows
12265
+ * the poll's discipline exactly: guarded on the source so a retired worktree
12266
+ * cannot repaint the tree, touching neither `gen` (which would reset tree
12267
+ * expansion) nor `statsLoading` (which would swap the header totals for a
12268
+ * `—` while a good answer is still on screen).
12269
+ *
12270
+ * An open drawer skips it — the poll is running there and the open itself
12271
+ * bumped gen. Like the probe's full refetch above, the fetch is left to land
12272
+ * guarded rather than aborted: a cleanup fired for an unrelated dep (the
12273
+ * drawer opening mid-flight) must not cancel the only fetch this turn gets.
12274
+ */
12275
+ (0, react.useEffect)(() => {
12276
+ const settled = turnSettled(wasRunningRef.current, agentRunning);
12277
+ wasRunningRef.current = agentRunning;
12278
+ if (!settled || open) return;
12279
+ fetchStats(statsPath, new AbortController().signal).then((value) => {
12280
+ if (value !== null && statsPathRef.current === statsPath) setStats(value);
12281
+ }).catch(() => {});
12282
+ }, [
12283
+ agentRunning,
12284
+ open,
12285
+ statsPath,
12286
+ fetchStats
12287
+ ]);
12222
12288
  (0, react.useEffect)(() => {
12223
12289
  const ctrl = new AbortController();
12224
12290
  let alive = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@young1lin/dsh-ui-gitworkbench",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Out-of-tree dsh web UI plugin: a session-header git workbench chip opening a drawer with the file tree, per-file diff, history, compare, staging, commit, and sync (fetch/pull/push).",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -65,11 +65,11 @@
65
65
  },
66
66
  "packageManager": "pnpm@10.20.0",
67
67
  "peerDependencies": {
68
- "@deepseek-ai/cordis": "*",
69
- "@deepseek-ai/dsh-client-runtime": "*",
70
- "@deepseek-ai/dsh-client-ui-slots": "*",
71
- "@deepseek-ai/dsh-tools": "*",
72
- "@deepseek-ai/dsh-typert-protocol": "*",
68
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
69
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.2",
70
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.2",
71
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.2",
72
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.2",
73
73
  "react": "^18.2.0"
74
74
  },
75
75
  "devDependencies": {
@@ -62,7 +62,7 @@ import {
62
62
  type CheckState, type Tick, type TickAction,
63
63
  } from './stage-tree.ts'
64
64
  import { grammarLoadCount, highlightForRows, shikiLangOf, shikiThemeOf, subscribeGrammarLoaded, type HighlightRun } from './highlight.ts'
65
- import { badgeRepeatsBranch, bindingChanged, branchOfWorktree, probesClosedBinding, samePath, showsPending, splitPath, viewedPath } from './worktree-view.ts'
65
+ import { badgeRepeatsBranch, bindingChanged, branchOfWorktree, probesClosedBinding, samePath, showsPending, splitPath, turnSettled, viewedPath } from './worktree-view.ts'
66
66
  import { BUSY_DELAY_MS, BUSY_HOLD_MS, holdRemaining, quietlyDisabled } from './op-feedback.ts'
67
67
  import type { WorkbenchKey } from './locales.ts'
68
68
  import css from './GitWorkbenchPanel.module.css'
@@ -630,6 +630,43 @@ export function GitWorkbenchPanel({ sessionId, useSessions, t, fetchStats, fetch
630
630
  return () => { alive = false; clearInterval(id) }
631
631
  }, [open, agentRunning, sessionId, worktreePath, fetchSessionBinding, fetchWorktreeStatus])
632
632
 
633
+ /** The agent's `running` on the previous render. The flag itself says
634
+ * whether a turn is in flight; only the EDGE of it says the turn has
635
+ * ended, and the edge is what the effect below keys on. */
636
+ const wasRunningRef = useRef<boolean | undefined>(undefined)
637
+
638
+ /**
639
+ * Refresh the SHUT chip's stats when a turn ends.
640
+ *
641
+ * The keyed fetch below runs on mount, on source switches and on gen bumps,
642
+ * and the 3-15s poll starts at `if (!open) return` — so while the drawer was
643
+ * shut, an agent that wrote files all turn left the header counting the tree
644
+ * as it stood before the turn. Opening the drawer was the only thing that
645
+ * refreshed it, and an indicator you must open to read is not an indicator.
646
+ *
647
+ * `running` is mirrored live by the sessions store, and a turn boundary is
648
+ * when agent-caused side effects have settled ({@link turnSettled}), so one
649
+ * fetch per turn buys the chip the numbers the turn just made true — ahead
650
+ * counts included, which ride along in the same payload. The write follows
651
+ * the poll's discipline exactly: guarded on the source so a retired worktree
652
+ * cannot repaint the tree, touching neither `gen` (which would reset tree
653
+ * expansion) nor `statsLoading` (which would swap the header totals for a
654
+ * `—` while a good answer is still on screen).
655
+ *
656
+ * An open drawer skips it — the poll is running there and the open itself
657
+ * bumped gen. Like the probe's full refetch above, the fetch is left to land
658
+ * guarded rather than aborted: a cleanup fired for an unrelated dep (the
659
+ * drawer opening mid-flight) must not cancel the only fetch this turn gets.
660
+ */
661
+ useEffect(() => {
662
+ const settled = turnSettled(wasRunningRef.current, agentRunning)
663
+ wasRunningRef.current = agentRunning
664
+ if (!settled || open) return
665
+ fetchStats(statsPath, new AbortController().signal)
666
+ .then(value => { if (value !== null && statsPathRef.current === statsPath) setStats(value) })
667
+ .catch(() => {})
668
+ }, [agentRunning, open, statsPath, fetchStats])
669
+
633
670
  // Stats for the active source: on mount, on source change and on gen bumps
634
671
  // (manual refresh / source switch). Cleanup aborts a superseded in-flight fetch.
635
672
  useEffect(() => {
@@ -109,6 +109,36 @@ export function bindingChanged(
109
109
  return (probe.name ?? '') !== (shown?.name ?? '')
110
110
  }
111
111
 
112
+ /**
113
+ * Whether a turn that was in flight has just come to an end.
114
+ *
115
+ * The shut chip's stats freeze on mount: the fetch that carries them re-runs
116
+ * only on source switches and gen bumps, and the 3-15s poll starts at
117
+ * `if (!open) return`. So an agent that wrote files all turn left the header
118
+ * counting the tree as it was before the turn — until someone opened the
119
+ * drawer, which is the one act that already refreshes everything.
120
+ *
121
+ * `running` is mirrored live by the sessions store, and a turn boundary is
122
+ * when agent-caused side effects have stopped accumulating — the one instant
123
+ * a shut chip should pay for fresh numbers. One fetch per turn, and none for
124
+ * an idle session, which is the cost rule this panel lives under (it mounts
125
+ * in every session header).
126
+ *
127
+ * The transition is read strictly: `false → true` is a turn STARTING, whose
128
+ * numbers are seconds away from being rewritten, and a missing `next` counts
129
+ * as ended because a turn in flight cannot still be in flight once the store
130
+ * stops saying so.
131
+ *
132
+ * @param prevRunning - whether the agent had a turn in flight last render.
133
+ * @param nextRunning - whether it has one now.
134
+ */
135
+ export function turnSettled(
136
+ prevRunning: boolean | undefined,
137
+ nextRunning: boolean | undefined,
138
+ ): boolean {
139
+ return prevRunning === true && nextRunning !== true
140
+ }
141
+
112
142
  /**
113
143
  * Whether a view should say "pending" rather than show what it has.
114
144
  *