@young1lin/dsh-ui-gitworkbench 0.1.1 → 0.1.2

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,12 @@
2
2
 
3
3
  本文件记录面向使用者的变更。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
4
4
 
5
+ ## [0.1.2] - 2026-08-17
6
+
7
+ ### 修复
8
+
9
+ - **抽屉关着时,会话头状态卡的统计不再冻结在挂载时刻**:此前 agent 整轮改文件期间,芯片上的文件数 / 增删行数 / ↑↓ 计数一直停在 turn 开始前的数字,只能点开抽屉强制刷新。现在芯片跟踪 dsh 会话 store 实时镜像的 `running` 信号——turn 一结束(agent 副作用落定的时刻)自动取一次最新统计:不重置抽屉里的树展开状态、不闪加载占位;turn 进行中不刷(数字在结束时一次到位),空闲会话保持零轮询开销。↑/↓ 计数随同一载荷返回,agent 提交完领先数立即对上。编辑器等会话外的改动仍以打开抽屉为准(维持原有设计)。
10
+
5
11
  ## [0.1.1] - 2026-08-17
6
12
 
7
13
  ### 修复
package/CHANGELOG_EN.md CHANGED
@@ -2,6 +2,12 @@
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.2] - 2026-08-17
6
+
7
+ ### Fixed
8
+
9
+ - **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.
10
+
5
11
  ## [0.1.1] - 2026-08-17
6
12
 
7
13
  ### Fixed
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 = {
11600
- "lineHunk": "SD8qLW_lineHunk",
11601
- "treeRow": "SD8qLW_treeRow",
11602
- "checkMarkPartial": "SD8qLW_checkMarkPartial",
11603
- "refRowName": "SD8qLW_refRowName",
11604
- "elide": "SD8qLW_elide",
11605
- "refSearch": "SD8qLW_refSearch",
11606
- "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
11607
- "paneTitle": "SD8qLW_paneTitle",
11608
- "commitBtn": "SD8qLW_commitBtn",
11609
- "paletteRowActive": "SD8qLW_paletteRowActive",
11610
- "commitPopBody": "SD8qLW_commitPopBody",
11626
+ "paneHead": "SD8qLW_paneHead",
11627
+ "commitSubject": "SD8qLW_commitSubject",
11628
+ "treeDirCount": "SD8qLW_treeDirCount",
11629
+ "elideTail": "SD8qLW_elideTail",
11630
+ "opBannerBad": "SD8qLW_opBannerBad",
11631
+ "treeIconGlyph": "SD8qLW_treeIconGlyph",
11632
+ "segmentActive": "SD8qLW_segmentActive",
11633
+ "headerViewRef": "SD8qLW_headerViewRef",
11634
+ "themeGroup": "SD8qLW_themeGroup",
11635
+ "refGroup": "SD8qLW_refGroup",
11636
+ "bgPreview": "SD8qLW_bgPreview",
11637
+ "themeLabel": "SD8qLW_themeLabel",
11638
+ "refList": "SD8qLW_refList",
11639
+ "gutter": "SD8qLW_gutter",
11640
+ "headerLeft": "SD8qLW_headerLeft",
11611
11641
  "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",
11624
- "chevron": "SD8qLW_chevron",
11625
11642
  "checkBox": "SD8qLW_checkBox",
11626
- "scopeBtnActive": "SD8qLW_scopeBtnActive",
11627
- "commitLead": "SD8qLW_commitLead",
11628
- "treeSub": "SD8qLW_treeSub",
11629
- "stUntracked": "SD8qLW_stUntracked",
11630
- "tabActive": "SD8qLW_tabActive",
11631
- "commitStaged": "SD8qLW_commitStaged",
11643
+ "btnPrimary": "SD8qLW_btnPrimary",
11644
+ "refCaret": "SD8qLW_refCaret",
11645
+ "lineAdd": "SD8qLW_lineAdd",
11646
+ "themeRail": "SD8qLW_themeRail",
11632
11647
  "headerTotals": "SD8qLW_headerTotals",
11633
- "headerTotalsDim": "SD8qLW_headerTotalsDim",
11634
- "body": "SD8qLW_body",
11635
- "syncBar": "SD8qLW_syncBar",
11636
- "commitBox": "SD8qLW_commitBox",
11648
+ "btn": "SD8qLW_btn",
11637
11649
  "btnCount": "SD8qLW_btnCount",
11638
- "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
11639
- "cardAdded": "SD8qLW_cardAdded",
11640
- "swatch": "SD8qLW_swatch",
11641
- "sliderRow": "SD8qLW_sliderRow",
11642
- "header": "SD8qLW_header",
11643
- "sliderValue": "SD8qLW_sliderValue",
11644
11650
  "compareArrow": "SD8qLW_compareArrow",
11645
- "commitsPane": "SD8qLW_commitsPane",
11646
- "chipLight": "SD8qLW_chipLight",
11647
- "headerView": "SD8qLW_headerView",
11648
- "gsSlide": "SD8qLW_gsSlide",
11649
- "paneHead": "SD8qLW_paneHead",
11650
- "commitsSentinel": "SD8qLW_commitsSentinel",
11651
- "checkMarkOn": "SD8qLW_checkMarkOn",
11651
+ "headerTotalsDel": "SD8qLW_headerTotalsDel",
11652
+ "overlay": "SD8qLW_overlay",
11653
+ "empty": "SD8qLW_empty",
11652
11654
  "commitRow": "SD8qLW_commitRow",
11653
- "headerLeft": "SD8qLW_headerLeft",
11655
+ "cardAdded": "SD8qLW_cardAdded",
11656
+ "headerBranch": "SD8qLW_headerBranch",
11657
+ "treeIcon": "SD8qLW_treeIcon",
11658
+ "scopeBtnActive": "SD8qLW_scopeBtnActive",
11654
11659
  "treeDirLi": "SD8qLW_treeDirLi",
11660
+ "elide": "SD8qLW_elide",
11661
+ "settingsPop": "SD8qLW_settingsPop",
11662
+ "opBannerOk": "SD8qLW_opBannerOk",
11655
11663
  "fileActive": "SD8qLW_fileActive",
11656
- "cardWt": "SD8qLW_cardWt",
11657
- "bgEmpty": "SD8qLW_bgEmpty",
11658
11664
  "cardGlyph": "SD8qLW_cardGlyph",
11665
+ "fileCountAdd": "SD8qLW_fileCountAdd",
11666
+ "syncUpstream": "SD8qLW_syncUpstream",
11667
+ "commit": "SD8qLW_commit",
11668
+ "treeActions": "SD8qLW_treeActions",
11669
+ "btnBehind": "SD8qLW_btnBehind",
11670
+ "refRow": "SD8qLW_refRow",
11671
+ "miniBtn": "SD8qLW_miniBtn",
11672
+ "commitBox": "SD8qLW_commitBox",
11673
+ "tabActive": "SD8qLW_tabActive",
11674
+ "card": "SD8qLW_card",
11675
+ "menuPop": "SD8qLW_menuPop",
11676
+ "drawer": "SD8qLW_drawer",
11677
+ "treeCol": "SD8qLW_treeCol",
11678
+ "treeIconDown": "SD8qLW_treeIconDown",
11679
+ "segmented": "SD8qLW_segmented",
11680
+ "commitPopSubject": "SD8qLW_commitPopSubject",
11681
+ "lnNew": "SD8qLW_lnNew",
11682
+ "chevronOpen": "SD8qLW_chevronOpen",
11683
+ "stUntracked": "SD8qLW_stUntracked",
11684
+ "commitPopBody": "SD8qLW_commitPopBody",
11685
+ "signDel": "SD8qLW_signDel",
11686
+ "swatch": "SD8qLW_swatch",
11687
+ "commitsFoot": "SD8qLW_commitsFoot",
11688
+ "wordAdd": "SD8qLW_wordAdd",
11689
+ "lnOld": "SD8qLW_lnOld",
11690
+ "cardBranch": "SD8qLW_cardBranch",
11659
11691
  "elideHead": "SD8qLW_elideHead",
11660
11692
  "commitWhen": "SD8qLW_commitWhen",
11661
- "commitHasBody": "SD8qLW_commitHasBody",
11662
- "fileCountDel": "SD8qLW_fileCountDel",
11663
- "syncLevel": "SD8qLW_syncLevel",
11664
- "treeIcon": "SD8qLW_treeIcon",
11665
- "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
- "commitCopy": "SD8qLW_commitCopy",
11673
- "cardFiles": "SD8qLW_cardFiles",
11674
- "paneDividerActive": "SD8qLW_paneDividerActive",
11675
- "diffPre": "SD8qLW_diffPre",
11676
- "themeNote": "SD8qLW_themeNote",
11693
+ "miniBtnPrimary": "SD8qLW_miniBtnPrimary",
11694
+ "lineHunk": "SD8qLW_lineHunk",
11695
+ "cardDeleted": "SD8qLW_cardDeleted",
11696
+ "headerView": "SD8qLW_headerView",
11697
+ "treeDirName": "SD8qLW_treeDirName",
11698
+ "resizer": "SD8qLW_resizer",
11699
+ "commitPop": "SD8qLW_commitPop",
11700
+ "fileStatus": "SD8qLW_fileStatus",
11701
+ "chipDark": "SD8qLW_chipDark",
11702
+ "compareBar": "SD8qLW_compareBar",
11703
+ "stAdded": "SD8qLW_stAdded",
11677
11704
  "stDeleted": "SD8qLW_stDeleted",
11678
- "commit": "SD8qLW_commit",
11679
- "lnNew": "SD8qLW_lnNew",
11680
11705
  "commitMessage": "SD8qLW_commitMessage",
11706
+ "commits": "SD8qLW_commits",
11707
+ "scopeRow": "SD8qLW_scopeRow",
11708
+ "stRenamed": "SD8qLW_stRenamed",
11709
+ "btnClose": "SD8qLW_btnClose",
11710
+ "paletteRow": "SD8qLW_paletteRow",
11711
+ "commitLine": "SD8qLW_commitLine",
11712
+ "headerTotalsAdd": "SD8qLW_headerTotalsAdd",
11713
+ "resizerActive": "SD8qLW_resizerActive",
11714
+ "themeDirty": "SD8qLW_themeDirty",
11715
+ "refSearch": "SD8qLW_refSearch",
11716
+ "fileCountDel": "SD8qLW_fileCountDel",
11717
+ "commitStaged": "SD8qLW_commitStaged",
11681
11718
  "treeLabel": "SD8qLW_treeLabel",
11719
+ "sliderRow": "SD8qLW_sliderRow",
11720
+ "code": "SD8qLW_code",
11721
+ "gsFade": "SD8qLW_gsFade",
11722
+ "tabs": "SD8qLW_tabs",
11723
+ "treeRow": "SD8qLW_treeRow",
11724
+ "refValue": "SD8qLW_refValue",
11725
+ "chipLight": "SD8qLW_chipLight",
11726
+ "commitActive": "SD8qLW_commitActive",
11682
11727
  "commitSubjectRow": "SD8qLW_commitSubjectRow",
11683
- "headerDetached": "SD8qLW_headerDetached",
11684
- "themeLabel": "SD8qLW_themeLabel",
11728
+ "treeWrap": "SD8qLW_treeWrap",
11729
+ "checkMark": "SD8qLW_checkMark",
11730
+ "btnIcon": "SD8qLW_btnIcon",
11731
+ "btnAhead": "SD8qLW_btnAhead",
11732
+ "segmentChip": "SD8qLW_segmentChip",
11685
11733
  "commitHash": "SD8qLW_commitHash",
11686
- "overlayMax": "SD8qLW_overlayMax",
11734
+ "themeRowSplit": "SD8qLW_themeRowSplit",
11735
+ "cardAhead": "SD8qLW_cardAhead",
11736
+ "treeTools": "SD8qLW_treeTools",
11737
+ "cardBehind": "SD8qLW_cardBehind",
11738
+ "paneDividerActive": "SD8qLW_paneDividerActive",
11739
+ "refRowName": "SD8qLW_refRowName",
11740
+ "commitTop": "SD8qLW_commitTop",
11741
+ "refLabel": "SD8qLW_refLabel",
11687
11742
  "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",
11695
- "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
- "treeIconDown": "SD8qLW_treeIconDown",
11707
- "segmentActive": "SD8qLW_segmentActive",
11743
+ "checkMarkPartial": "SD8qLW_checkMarkPartial",
11708
11744
  "pullGroup": "SD8qLW_pullGroup",
11709
- "wordAdd": "SD8qLW_wordAdd",
11710
- "btnClose": "SD8qLW_btnClose",
11745
+ "cssArea": "SD8qLW_cssArea",
11746
+ "treeLead": "SD8qLW_treeLead",
11747
+ "headerTotalsDim": "SD8qLW_headerTotalsDim",
11748
+ "commitCopy": "SD8qLW_commitCopy",
11749
+ "renameLine": "SD8qLW_renameLine",
11750
+ "header": "SD8qLW_header",
11751
+ "paneDivider": "SD8qLW_paneDivider",
11752
+ "body": "SD8qLW_body",
11753
+ "headerPathMain": "SD8qLW_headerPathMain",
11754
+ "treeDirActive": "SD8qLW_treeDirActive",
11755
+ "cardFiles": "SD8qLW_cardFiles",
11756
+ "tree": "SD8qLW_tree",
11757
+ "syncSpacer": "SD8qLW_syncSpacer",
11758
+ "refPicker": "SD8qLW_refPicker",
11759
+ "paletteRowActive": "SD8qLW_paletteRowActive",
11760
+ "fileLi": "SD8qLW_fileLi",
11761
+ "refFoot": "SD8qLW_refFoot",
11762
+ "lineContext": "SD8qLW_lineContext",
11763
+ "headerDetached": "SD8qLW_headerDetached",
11764
+ "commitBtn": "SD8qLW_commitBtn",
11711
11765
  "stModified": "SD8qLW_stModified",
11712
- "overlay": "SD8qLW_overlay",
11766
+ "paneTitle": "SD8qLW_paneTitle",
11767
+ "scopeBtn": "SD8qLW_scopeBtn",
11768
+ "sliderValue": "SD8qLW_sliderValue",
11713
11769
  "line": "SD8qLW_line",
11714
- "bgPreview": "SD8qLW_bgPreview",
11715
- "wordDel": "SD8qLW_wordDel",
11716
- "commitRefMore": "SD8qLW_commitRefMore",
11717
- "headerBranch": "SD8qLW_headerBranch",
11718
- "lineContext": "SD8qLW_lineContext",
11719
- "btnBehind": "SD8qLW_btnBehind",
11720
- "signDel": "SD8qLW_signDel",
11721
- "btnIcon": "SD8qLW_btnIcon",
11722
- "checkMark": "SD8qLW_checkMark",
11723
- "signAdd": "SD8qLW_signAdd",
11724
- "opBannerOk": "SD8qLW_opBannerOk",
11725
- "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",
11770
+ "commitAmend": "SD8qLW_commitAmend",
11771
+ "cardWt": "SD8qLW_cardWt",
11772
+ "cardSep": "SD8qLW_cardSep",
11773
+ "commitsSentinel": "SD8qLW_commitsSentinel",
11774
+ "gsSlide": "SD8qLW_gsSlide",
11775
+ "refRowSpacer": "SD8qLW_refRowSpacer",
11776
+ "overlayMax": "SD8qLW_overlayMax",
11777
+ "filePath": "SD8qLW_filePath",
11778
+ "diffPane": "SD8qLW_diffPane",
11779
+ "syncLevel": "SD8qLW_syncLevel",
11780
+ "syncBar": "SD8qLW_syncBar",
11781
+ "refPop": "SD8qLW_refPop",
11782
+ "segment": "SD8qLW_segment",
11783
+ "theme": "SD8qLW_theme",
11784
+ "chipSystem": "SD8qLW_chipSystem",
11733
11785
  "wtCurrent": "SD8qLW_wtCurrent",
11734
- "cardBehind": "SD8qLW_cardBehind",
11735
- "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
11786
  "commitRef": "SD8qLW_commitRef",
11743
- "chipDark": "SD8qLW_chipDark",
11744
- "treeCol": "SD8qLW_treeCol",
11745
- "compareBar": "SD8qLW_compareBar",
11746
- "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
11787
  "scopeHint": "SD8qLW_scopeHint",
11755
- "refButton": "SD8qLW_refButton",
11756
- "segmented": "SD8qLW_segmented",
11757
- "paneDivider": "SD8qLW_paneDivider",
11758
- "paletteRow": "SD8qLW_paletteRow",
11759
- "segment": "SD8qLW_segment",
11760
- "scopeRow": "SD8qLW_scopeRow",
11761
- "treeDirName": "SD8qLW_treeDirName",
11762
- "fileStatus": "SD8qLW_fileStatus",
11763
- "commitsFoot": "SD8qLW_commitsFoot",
11788
+ "bgEmpty": "SD8qLW_bgEmpty",
11789
+ "cardDetached": "SD8qLW_cardDetached",
11790
+ "refEmpty": "SD8qLW_refEmpty",
11791
+ "treeEmpty": "SD8qLW_treeEmpty",
11792
+ "headerRight": "SD8qLW_headerRight",
11793
+ "cardBranchName": "SD8qLW_cardBranchName",
11764
11794
  "fileBinary": "SD8qLW_fileBinary",
11765
- "segmentChip": "SD8qLW_segmentChip",
11766
- "commitAmend": "SD8qLW_commitAmend",
11767
- "drawer": "SD8qLW_drawer",
11768
- "gsFade": "SD8qLW_gsFade",
11769
- "commitSubject": "SD8qLW_commitSubject",
11770
- "treeTools": "SD8qLW_treeTools",
11771
- "treeActions": "SD8qLW_treeActions",
11772
- "commitPopSubject": "SD8qLW_commitPopSubject",
11773
- "cardAhead": "SD8qLW_cardAhead",
11774
- "refLabel": "SD8qLW_refLabel",
11795
+ "fileCounts": "SD8qLW_fileCounts",
11796
+ "headerPicker": "SD8qLW_headerPicker",
11797
+ "signAdd": "SD8qLW_signAdd",
11798
+ "commitLead": "SD8qLW_commitLead",
11799
+ "commitPopTop": "SD8qLW_commitPopTop",
11800
+ "tab": "SD8qLW_tab",
11801
+ "themeNote": "SD8qLW_themeNote",
11802
+ "file": "SD8qLW_file",
11803
+ "diffPre": "SD8qLW_diffPre",
11804
+ "refButton": "SD8qLW_refButton",
11775
11805
  "opBanner": "SD8qLW_opBanner",
11776
- "themeRail": "SD8qLW_themeRail",
11777
- "headerTotalsDel": "SD8qLW_headerTotalsDel",
11778
- "cardBranch": "SD8qLW_cardBranch",
11779
- "cardBranchName": "SD8qLW_cardBranchName",
11780
- "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"
11806
+ "commitRefMore": "SD8qLW_commitRefMore",
11807
+ "graphCell": "SD8qLW_graphCell",
11808
+ "treeDir": "SD8qLW_treeDir",
11809
+ "checkMarkOn": "SD8qLW_checkMarkOn",
11810
+ "commitsPane": "SD8qLW_commitsPane",
11811
+ "wordDel": "SD8qLW_wordDel",
11812
+ "commitHasBody": "SD8qLW_commitHasBody",
11813
+ "chevron": "SD8qLW_chevron",
11814
+ "treeSub": "SD8qLW_treeSub",
11815
+ "refRowActive": "SD8qLW_refRowActive"
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.2",
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",
@@ -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
  *