@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 +6 -0
- package/CHANGELOG_EN.md +6 -0
- package/lib/client.js +231 -165
- package/package.json +1 -1
- package/src/client/GitWorkbenchPanel.tsx +38 -1
- package/src/client/worktree-view.ts +30 -0
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
|
-
"
|
|
11601
|
-
"
|
|
11602
|
-
"
|
|
11603
|
-
"
|
|
11604
|
-
"
|
|
11605
|
-
"
|
|
11606
|
-
"
|
|
11607
|
-
"
|
|
11608
|
-
"
|
|
11609
|
-
"
|
|
11610
|
-
"
|
|
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
|
-
"
|
|
11627
|
-
"
|
|
11628
|
-
"
|
|
11629
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
11646
|
-
"
|
|
11647
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
11662
|
-
"
|
|
11663
|
-
"
|
|
11664
|
-
"
|
|
11665
|
-
"
|
|
11666
|
-
"
|
|
11667
|
-
"
|
|
11668
|
-
"
|
|
11669
|
-
"
|
|
11670
|
-
"
|
|
11671
|
-
"
|
|
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
|
-
"
|
|
11684
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
11710
|
-
"
|
|
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
|
-
"
|
|
11766
|
+
"paneTitle": "SD8qLW_paneTitle",
|
|
11767
|
+
"scopeBtn": "SD8qLW_scopeBtn",
|
|
11768
|
+
"sliderValue": "SD8qLW_sliderValue",
|
|
11713
11769
|
"line": "SD8qLW_line",
|
|
11714
|
-
"
|
|
11715
|
-
"
|
|
11716
|
-
"
|
|
11717
|
-
"
|
|
11718
|
-
"
|
|
11719
|
-
"
|
|
11720
|
-
"
|
|
11721
|
-
"
|
|
11722
|
-
"
|
|
11723
|
-
"
|
|
11724
|
-
"
|
|
11725
|
-
"
|
|
11726
|
-
"
|
|
11727
|
-
"
|
|
11728
|
-
"
|
|
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
|
-
"
|
|
11756
|
-
"
|
|
11757
|
-
"
|
|
11758
|
-
"
|
|
11759
|
-
"
|
|
11760
|
-
"
|
|
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
|
-
"
|
|
11766
|
-
"
|
|
11767
|
-
"
|
|
11768
|
-
"
|
|
11769
|
-
"
|
|
11770
|
-
"
|
|
11771
|
-
"
|
|
11772
|
-
"
|
|
11773
|
-
"
|
|
11774
|
-
"
|
|
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
|
-
"
|
|
11777
|
-
"
|
|
11778
|
-
"
|
|
11779
|
-
"
|
|
11780
|
-
"
|
|
11781
|
-
"
|
|
11782
|
-
"
|
|
11783
|
-
"
|
|
11784
|
-
"
|
|
11785
|
-
"
|
|
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.
|
|
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
|
*
|