@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 +12 -0
- package/CHANGELOG_EN.md +12 -0
- package/README.md +3 -0
- package/lib/client.js +230 -164
- package/package.json +6 -6
- package/src/client/GitWorkbenchPanel.tsx +38 -1
- package/src/client/worktree-view.ts +30 -0
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
|
-
"
|
|
11602
|
-
"
|
|
11603
|
-
"
|
|
11604
|
-
"
|
|
11638
|
+
"sliderValue": "SD8qLW_sliderValue",
|
|
11639
|
+
"commitHasBody": "SD8qLW_commitHasBody",
|
|
11640
|
+
"elideHead": "SD8qLW_elideHead",
|
|
11641
|
+
"headerView": "SD8qLW_headerView",
|
|
11605
11642
|
"refSearch": "SD8qLW_refSearch",
|
|
11606
|
-
"
|
|
11607
|
-
"
|
|
11608
|
-
"
|
|
11643
|
+
"commitHash": "SD8qLW_commitHash",
|
|
11644
|
+
"cardAdded": "SD8qLW_cardAdded",
|
|
11645
|
+
"opBanner": "SD8qLW_opBanner",
|
|
11646
|
+
"opBannerOk": "SD8qLW_opBannerOk",
|
|
11609
11647
|
"paletteRowActive": "SD8qLW_paletteRowActive",
|
|
11610
|
-
"
|
|
11611
|
-
"
|
|
11612
|
-
"
|
|
11613
|
-
"
|
|
11614
|
-
"
|
|
11615
|
-
"
|
|
11616
|
-
"
|
|
11617
|
-
"
|
|
11618
|
-
"
|
|
11619
|
-
"
|
|
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
|
-
"
|
|
11626
|
-
"
|
|
11627
|
-
"
|
|
11628
|
-
"
|
|
11629
|
-
"
|
|
11630
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
11639
|
-
"
|
|
11640
|
-
"
|
|
11641
|
-
"
|
|
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
|
-
"
|
|
11644
|
-
"
|
|
11645
|
-
"
|
|
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
|
-
"
|
|
11648
|
-
"
|
|
11649
|
-
"
|
|
11650
|
-
"
|
|
11651
|
-
"
|
|
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
|
-
"
|
|
11654
|
-
"
|
|
11655
|
-
"
|
|
11656
|
-
"
|
|
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
|
-
"
|
|
11659
|
-
"
|
|
11660
|
-
"
|
|
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
|
-
"
|
|
11674
|
-
"
|
|
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
|
-
"
|
|
11682
|
-
"
|
|
11683
|
-
"
|
|
11684
|
-
"
|
|
11685
|
-
"
|
|
11686
|
-
"
|
|
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
|
-
"
|
|
11689
|
-
"
|
|
11690
|
-
"
|
|
11691
|
-
"
|
|
11692
|
-
"
|
|
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
|
-
"
|
|
11712
|
-
"
|
|
11713
|
-
"
|
|
11714
|
-
"
|
|
11715
|
-
"
|
|
11716
|
-
"
|
|
11717
|
-
"
|
|
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
|
-
"
|
|
11720
|
-
"
|
|
11721
|
-
"
|
|
11722
|
-
"
|
|
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
|
-
"
|
|
11761
|
+
"refRowActive": "SD8qLW_refRowActive",
|
|
11725
11762
|
"treeWrap": "SD8qLW_treeWrap",
|
|
11726
|
-
"
|
|
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
|
-
"
|
|
11737
|
-
"
|
|
11738
|
-
"
|
|
11739
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
11760
|
-
"
|
|
11761
|
-
"
|
|
11762
|
-
"
|
|
11763
|
-
"
|
|
11764
|
-
"
|
|
11765
|
-
"
|
|
11766
|
-
"
|
|
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
|
-
"
|
|
11769
|
-
"
|
|
11770
|
-
"
|
|
11771
|
-
"
|
|
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
|
-
"
|
|
11782
|
-
"
|
|
11783
|
-
"
|
|
11784
|
-
"
|
|
11785
|
-
"
|
|
11786
|
-
"
|
|
11787
|
-
"
|
|
11788
|
-
"
|
|
11789
|
-
"
|
|
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.
|
|
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
|
*
|