dsh-git-changes 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/README.en.md CHANGED
@@ -24,13 +24,17 @@ DSH's builtin review shows one hunk at a time; this panel gives you the **whole
24
24
  - **Project tab** — a second segment next to Changes that browses the whole workspace tree; opening a file there is plain code viewing, not a diff
25
25
  - **AI commit messages**: the ✨ button in the commit box asks the Host default model to draft a message from the staged (or unstaged) diff; edit it before committing
26
26
  - **Line blame**: pause the cursor on a line for about a second to see `author · time · subject` at the line end; hover the annotation for a commit card (author, time, full message, sha)
27
- - **Commit graph**: a collapsible lane graph at the bottom of the changes panel lists the latest 80 commits; clicking one opens its diff as the baseline
28
27
  - **Inline / side-by-side diffs** — one click to switch; `auto` mode picks a layout from the panel width (900px breakpoint)
29
28
  - **Controlled git writes** — only touches the git index and commits: never rewrites files, never runs arbitrary commands, never force pushes; every write goes through a session-authenticated local route
30
29
  - **Follows the DSH light/dark theme automatically**
31
30
 
32
31
  ## 🆕 What's New
33
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
+
34
38
  ### v0.1.1 · Polish
35
39
 
36
40
  - **New Project tab** — a second segment beside Changes that browses the whole workspace tree; opening a file there is plain code viewing.
@@ -39,7 +43,6 @@ DSH's builtin review shows one hunk at a time; this panel gives you the **whole
39
43
  - **Tree / list toggle**, file-type icons on every change row.
40
44
  - **Blame on cursor dwell** — the toolbar switch is gone: pause on a line for about a second to see the line-end annotation; hover it for a commit card. Works on both sides of a side-by-side diff.
41
45
  - **AI commit messages** — the ✨ button drafts a Chinese message following Conventional Commits, retrying when needed.
42
- - **Commit graph** — a lane graph at the bottom of the changes panel; click a commit to diff against it.
43
46
  - **Simpler baselines** — fixed to Git uncommitted by default; the baseline dropdown, the changed-only filter and the official diff-review button were removed; other baselines live in the command palette.
44
47
 
45
48
  ### v0.1.0 · Git changes panel (first release of this fork)
package/README.md CHANGED
@@ -24,13 +24,17 @@ DSH 自带的审查是一个个 hunk 片段;这个插件给的是**整个文
24
24
  - **「项目」标签**:右侧栏另一个分段,浏览整个工作区的文件树;点击文件是纯代码查看,不会进入 diff 模式
25
25
  - **AI 生成提交信息**:提交框右侧 ✨ 按钮调用 Host 的默认模型,根据已暂存(无暂存时用未暂存)改动生成提交信息,填入后仍可修改
26
26
  - **行级 Blame**:光标在某行停留约 1 秒后,行尾显示「作者 · 时间 · 提交说明」;鼠标移到标注上弹出提交详情卡片(作者、时间、完整 commit message、提交号)
27
- - **提交图谱**:更改面板底部「提交图谱」折叠区,用 lane 图展示最近 80 条提交,点击任意提交即以该版本为对比基线打开 diff
28
27
  - **内联 / 左右并排 diff**:一键切换,`auto` 模式按面板宽度自动选择(900px 阈值)
29
28
  - **可控的 Git 写操作**:只碰 Git 索引与提交,不写文件、不执行任意命令、不 force push;所有写操作都走 DSH 会话鉴权的本地接口
30
29
  - **自动跟随 DSH 明暗主题**
31
30
 
32
31
  ## 🆕 版本更新
33
32
 
33
+ ### v0.1.3 · 入口与基线修复
34
+
35
+ - **「本轮修改」入口修复**:从聊天输入框上方的胶囊打开时,直接进入「本轮改动」基线并选中该轮的首个改动文件,不再因工作区已提交而显示空面板。
36
+ - **空工作区回落**:默认的 Git 未提交面板若没有改动、而本轮对话有改动,会自动切换到本轮基线并提示。
37
+
34
38
  ### v0.1.1 · 细节打磨
35
39
 
36
40
  - **新增「项目」标签**:与「更改」并列的第二个分段,浏览整个工作区文件树;从项目树打开文件为纯代码查看。
@@ -40,7 +44,6 @@ DSH 自带的审查是一个个 hunk 片段;这个插件给的是**整个文
40
44
  - **文件类型图标**:更改面板的每个文件行显示与项目树一致的图标。
41
45
  - **Blame 改为光标停留触发**:移除工具栏开关,光标在某行停留约 1 秒显示行尾注释,悬停弹出提交详情卡片;并排 diff 两侧都支持。
42
46
  - **AI 提交信息**:提交框 ✨ 按钮生成中文提交信息(遵循 Conventional Commits,必要时自动重试)。
43
- - **提交图谱**:更改面板底部 lane 图,点击提交直接以该版本为基线打开 diff。
44
47
  - **更简单的基线**:默认固定「Git 未提交」,移除比较基线下拉与「仅看改动」「官方 Diff 审查」按钮;其他基线从命令面板切换。
45
48
 
46
49
  ### v0.1.0 · Git 更改面板(本 fork 首个版本)
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,9 +1,9 @@
1
1
  {
2
2
  "name": "dsh-git-changes",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
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, commit graph and full-source review.",
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.",
7
7
  "keywords": [
8
8
  "dsh",
9
9
  "dsh-plugin",
@@ -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',
@@ -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