dsh-git-changes 0.1.2 → 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/README.en.md +5 -0
- package/README.md +5 -0
- package/lib/client.js +43 -1
- package/package.json +1 -1
- package/src/client/10-i18n.js +2 -0
- package/src/client/30-page.js +41 -1
package/README.en.md
CHANGED
|
@@ -30,6 +30,11 @@ DSH's builtin review shows one hunk at a time; this panel gives you the **whole
|
|
|
30
30
|
|
|
31
31
|
## 🆕 What's New
|
|
32
32
|
|
|
33
|
+
### v0.1.3 · Entry & baseline fixes
|
|
34
|
+
|
|
35
|
+
- **Composer pill fixed** — opening the panel from the "this turn" pill now selects the turn baseline and the turn's first changed file instead of an empty Git panel after the work was committed.
|
|
36
|
+
- **Empty-worktree fallback** — when the default Git uncommitted panel has nothing to show but the turn changed files, the panel switches to the turn baseline and says so.
|
|
37
|
+
|
|
33
38
|
### v0.1.1 · Polish
|
|
34
39
|
|
|
35
40
|
- **New Project tab** — a second segment beside Changes that browses the whole workspace tree; opening a file there is plain code viewing.
|
package/README.md
CHANGED
|
@@ -30,6 +30,11 @@ DSH 自带的审查是一个个 hunk 片段;这个插件给的是**整个文
|
|
|
30
30
|
|
|
31
31
|
## 🆕 版本更新
|
|
32
32
|
|
|
33
|
+
### v0.1.3 · 入口与基线修复
|
|
34
|
+
|
|
35
|
+
- **「本轮修改」入口修复**:从聊天输入框上方的胶囊打开时,直接进入「本轮改动」基线并选中该轮的首个改动文件,不再因工作区已提交而显示空面板。
|
|
36
|
+
- **空工作区回落**:默认的 Git 未提交面板若没有改动、而本轮对话有改动,会自动切换到本轮基线并提示。
|
|
37
|
+
|
|
33
38
|
### v0.1.1 · 细节打磨
|
|
34
39
|
|
|
35
40
|
- **新增「项目」标签**:与「更改」并列的第二个分段,浏览整个工作区文件树;从项目树打开文件为纯代码查看。
|
package/lib/client.js
CHANGED
|
@@ -393,6 +393,7 @@ const ZH = {
|
|
|
393
393
|
|
|
394
394
|
'mode.turn': 'Agent 本轮',
|
|
395
395
|
'mode.turn.desc': '本轮对话里 Agent 改了什么(与开始时对比)',
|
|
396
|
+
'mode.git.emptyToTurn': '工作区没有未提交改动,已切换到「本轮改动」',
|
|
396
397
|
'mode.turn.emptyToGit': '本轮没有可审查的改动,已切换到「Git 未提交」',
|
|
397
398
|
'mode.git.desc': '本地还没提交的改动(暂存看 HEAD,未暂存看索引)',
|
|
398
399
|
'mode.commit.desc': '最近一次提交改了什么',
|
|
@@ -666,6 +667,7 @@ const EN = {
|
|
|
666
667
|
|
|
667
668
|
'mode.turn': 'Agent turn',
|
|
668
669
|
'mode.turn.desc': 'What the agent changed this turn (vs the turn start)',
|
|
670
|
+
'mode.git.emptyToTurn': 'The working tree is clean; switched to this turn\'s changes',
|
|
669
671
|
'mode.turn.emptyToGit': 'No conversation changes to review; switched to uncommitted changes',
|
|
670
672
|
'mode.git.desc': 'Uncommitted local changes (staged vs HEAD, unstaged vs index)',
|
|
671
673
|
'mode.commit.desc': 'What the latest commit changed',
|
|
@@ -5838,6 +5840,21 @@ function changesReviewAddressOf(sessionId, seq, turn) {
|
|
|
5838
5840
|
return CHANGES_REVIEW_PREFIX + encodeURIComponent(sessionId) + '/' + String(seq) + '/' + String(turn)
|
|
5839
5841
|
}
|
|
5840
5842
|
|
|
5843
|
+
/**
|
|
5844
|
+
* Parse a builtin hunk-review address back into its turn coordinates.
|
|
5845
|
+
* @param {string|null|undefined} address resource address
|
|
5846
|
+
* @returns {{seq:number,turn:number}|null} parsed coordinates or null
|
|
5847
|
+
*/
|
|
5848
|
+
function turnAddressOf(address) {
|
|
5849
|
+
if (typeof address !== 'string' || address.indexOf(CHANGES_REVIEW_PREFIX) !== 0) return null
|
|
5850
|
+
const parts = address.slice(CHANGES_REVIEW_PREFIX.length).split('/')
|
|
5851
|
+
if (parts.length < 3) return null
|
|
5852
|
+
const seq = Number(parts[1])
|
|
5853
|
+
const turn = Number(parts[2])
|
|
5854
|
+
if (!Number.isFinite(seq) || !Number.isFinite(turn)) return null
|
|
5855
|
+
return { seq, turn }
|
|
5856
|
+
}
|
|
5857
|
+
|
|
5841
5858
|
/**
|
|
5842
5859
|
* Call `openResource` defensively; returns false instead of throwing.
|
|
5843
5860
|
* @param {any} right sidebarRight controller
|
|
@@ -6322,6 +6339,17 @@ function ReviewPage(props) {
|
|
|
6322
6339
|
autoOpenedRef.current = true
|
|
6323
6340
|
return
|
|
6324
6341
|
}
|
|
6342
|
+
// Opened from the chat composer's "this turn" pill: the builtin review
|
|
6343
|
+
// address carries the turn coordinates, so this visit is about that turn
|
|
6344
|
+
// even when a previous visit left another baseline behind.
|
|
6345
|
+
const addressTurn = turnAddressOf(address)
|
|
6346
|
+
if (addressTurn && latest.available) {
|
|
6347
|
+
setMode('turn')
|
|
6348
|
+
setModeTouched(true)
|
|
6349
|
+
autoOpenedRef.current = false
|
|
6350
|
+
restoreLoadRef.current = false
|
|
6351
|
+
return
|
|
6352
|
+
}
|
|
6325
6353
|
const saved = rememberedPageState(sessionId)
|
|
6326
6354
|
if (!saved) {
|
|
6327
6355
|
// First mount in this app run: honour the baseline the user left behind in
|
|
@@ -6372,7 +6400,7 @@ function ReviewPage(props) {
|
|
|
6372
6400
|
setRevision(nextRevision)
|
|
6373
6401
|
setMode(nextMode)
|
|
6374
6402
|
setModeTouched(saved.modeTouched === true)
|
|
6375
|
-
}, [latest, gitStatus, sessionId])
|
|
6403
|
+
}, [latest, gitStatus, sessionId, address])
|
|
6376
6404
|
|
|
6377
6405
|
// Persist the page's workspace state before this tab body unmounts. DSH
|
|
6378
6406
|
// unmounts the previous conversation's sidebar tab bodies on a session switch,
|
|
@@ -6473,6 +6501,20 @@ function ReviewPage(props) {
|
|
|
6473
6501
|
notify(t('mode.turn.emptyToGit'), 'info')
|
|
6474
6502
|
}, [mode, modeTouched, comparison, regions.length, notify, t])
|
|
6475
6503
|
|
|
6504
|
+
// The mirror case: a clean working tree plus a turn that did change files
|
|
6505
|
+
// should not open on an empty Git panel.
|
|
6506
|
+
React.useEffect(() => {
|
|
6507
|
+
if (mode !== 'git' || modeTouched) return
|
|
6508
|
+
if (!latest || latest.available !== true) return
|
|
6509
|
+
const files = latest.summary && Array.isArray(latest.summary.files) ? latest.summary.files : []
|
|
6510
|
+
if (files.length === 0) return
|
|
6511
|
+
const status = gitStatus
|
|
6512
|
+
if (!status || status.available !== true || !Array.isArray(status.files) || status.files.length > 0) return
|
|
6513
|
+
setMode('turn')
|
|
6514
|
+
setModeTouched(true)
|
|
6515
|
+
notify(t('mode.git.emptyToTurn'), 'info')
|
|
6516
|
+
}, [mode, modeTouched, latest, gitStatus, notify, t])
|
|
6517
|
+
|
|
6476
6518
|
/** Load one file's current content, using the per-path cache for instant switch. */
|
|
6477
6519
|
const loadFile = React.useCallback(async (path, options) => {
|
|
6478
6520
|
if (!sessionId || !path) return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-git-changes",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Cursor-style Git changes panel for the DSH right sidebar: staged/unstaged trees, stage/unstage, commit & push, inline or side-by-side diffs, AI commit messages, blame and full-source review.",
|
package/src/client/10-i18n.js
CHANGED
|
@@ -14,6 +14,7 @@ const ZH = {
|
|
|
14
14
|
|
|
15
15
|
'mode.turn': 'Agent 本轮',
|
|
16
16
|
'mode.turn.desc': '本轮对话里 Agent 改了什么(与开始时对比)',
|
|
17
|
+
'mode.git.emptyToTurn': '工作区没有未提交改动,已切换到「本轮改动」',
|
|
17
18
|
'mode.turn.emptyToGit': '本轮没有可审查的改动,已切换到「Git 未提交」',
|
|
18
19
|
'mode.git.desc': '本地还没提交的改动(暂存看 HEAD,未暂存看索引)',
|
|
19
20
|
'mode.commit.desc': '最近一次提交改了什么',
|
|
@@ -287,6 +288,7 @@ const EN = {
|
|
|
287
288
|
|
|
288
289
|
'mode.turn': 'Agent turn',
|
|
289
290
|
'mode.turn.desc': 'What the agent changed this turn (vs the turn start)',
|
|
291
|
+
'mode.git.emptyToTurn': 'The working tree is clean; switched to this turn\'s changes',
|
|
290
292
|
'mode.turn.emptyToGit': 'No conversation changes to review; switched to uncommitted changes',
|
|
291
293
|
'mode.git.desc': 'Uncommitted local changes (staged vs HEAD, unstaged vs index)',
|
|
292
294
|
'mode.commit.desc': 'What the latest commit changed',
|
package/src/client/30-page.js
CHANGED
|
@@ -207,6 +207,21 @@ function changesReviewAddressOf(sessionId, seq, turn) {
|
|
|
207
207
|
return CHANGES_REVIEW_PREFIX + encodeURIComponent(sessionId) + '/' + String(seq) + '/' + String(turn)
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Parse a builtin hunk-review address back into its turn coordinates.
|
|
212
|
+
* @param {string|null|undefined} address resource address
|
|
213
|
+
* @returns {{seq:number,turn:number}|null} parsed coordinates or null
|
|
214
|
+
*/
|
|
215
|
+
function turnAddressOf(address) {
|
|
216
|
+
if (typeof address !== 'string' || address.indexOf(CHANGES_REVIEW_PREFIX) !== 0) return null
|
|
217
|
+
const parts = address.slice(CHANGES_REVIEW_PREFIX.length).split('/')
|
|
218
|
+
if (parts.length < 3) return null
|
|
219
|
+
const seq = Number(parts[1])
|
|
220
|
+
const turn = Number(parts[2])
|
|
221
|
+
if (!Number.isFinite(seq) || !Number.isFinite(turn)) return null
|
|
222
|
+
return { seq, turn }
|
|
223
|
+
}
|
|
224
|
+
|
|
210
225
|
/**
|
|
211
226
|
* Call `openResource` defensively; returns false instead of throwing.
|
|
212
227
|
* @param {any} right sidebarRight controller
|
|
@@ -691,6 +706,17 @@ function ReviewPage(props) {
|
|
|
691
706
|
autoOpenedRef.current = true
|
|
692
707
|
return
|
|
693
708
|
}
|
|
709
|
+
// Opened from the chat composer's "this turn" pill: the builtin review
|
|
710
|
+
// address carries the turn coordinates, so this visit is about that turn
|
|
711
|
+
// even when a previous visit left another baseline behind.
|
|
712
|
+
const addressTurn = turnAddressOf(address)
|
|
713
|
+
if (addressTurn && latest.available) {
|
|
714
|
+
setMode('turn')
|
|
715
|
+
setModeTouched(true)
|
|
716
|
+
autoOpenedRef.current = false
|
|
717
|
+
restoreLoadRef.current = false
|
|
718
|
+
return
|
|
719
|
+
}
|
|
694
720
|
const saved = rememberedPageState(sessionId)
|
|
695
721
|
if (!saved) {
|
|
696
722
|
// First mount in this app run: honour the baseline the user left behind in
|
|
@@ -741,7 +767,7 @@ function ReviewPage(props) {
|
|
|
741
767
|
setRevision(nextRevision)
|
|
742
768
|
setMode(nextMode)
|
|
743
769
|
setModeTouched(saved.modeTouched === true)
|
|
744
|
-
}, [latest, gitStatus, sessionId])
|
|
770
|
+
}, [latest, gitStatus, sessionId, address])
|
|
745
771
|
|
|
746
772
|
// Persist the page's workspace state before this tab body unmounts. DSH
|
|
747
773
|
// unmounts the previous conversation's sidebar tab bodies on a session switch,
|
|
@@ -842,6 +868,20 @@ function ReviewPage(props) {
|
|
|
842
868
|
notify(t('mode.turn.emptyToGit'), 'info')
|
|
843
869
|
}, [mode, modeTouched, comparison, regions.length, notify, t])
|
|
844
870
|
|
|
871
|
+
// The mirror case: a clean working tree plus a turn that did change files
|
|
872
|
+
// should not open on an empty Git panel.
|
|
873
|
+
React.useEffect(() => {
|
|
874
|
+
if (mode !== 'git' || modeTouched) return
|
|
875
|
+
if (!latest || latest.available !== true) return
|
|
876
|
+
const files = latest.summary && Array.isArray(latest.summary.files) ? latest.summary.files : []
|
|
877
|
+
if (files.length === 0) return
|
|
878
|
+
const status = gitStatus
|
|
879
|
+
if (!status || status.available !== true || !Array.isArray(status.files) || status.files.length > 0) return
|
|
880
|
+
setMode('turn')
|
|
881
|
+
setModeTouched(true)
|
|
882
|
+
notify(t('mode.git.emptyToTurn'), 'info')
|
|
883
|
+
}, [mode, modeTouched, latest, gitStatus, notify, t])
|
|
884
|
+
|
|
845
885
|
/** Load one file's current content, using the per-path cache for instant switch. */
|
|
846
886
|
const loadFile = React.useCallback(async (path, options) => {
|
|
847
887
|
if (!sessionId || !path) return
|