dsh-ide-git 0.16.2 → 0.16.4
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/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +9 -8
- package/src/index.js +17 -8
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ide-git",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4",
|
|
4
4
|
"description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/client.js
CHANGED
|
@@ -9578,11 +9578,12 @@ window.__ModuleLoader__.load({
|
|
|
9578
9578
|
for (const item of changeItems) sums.push(sums[sums.length - 1] + item.h)
|
|
9579
9579
|
return sums
|
|
9580
9580
|
}, [changeItems])
|
|
9581
|
+
/* Virtual window, VSCode-style (ListView.getVisibleRange): both ends are
|
|
9582
|
+
pixel→index conversions over the same prefix-sum array, so no "row
|
|
9583
|
+
count" ever enters the math (issue #12 — the old winLimit computed a
|
|
9584
|
+
row count while the loop accumulated pixels, stalling at 2 entries). */
|
|
9581
9585
|
const winFirst = Math.max(0, windowStart(changeOffsets, changesViewport.scrollTop) - WINDOW_OVERSCAN)
|
|
9582
|
-
const
|
|
9583
|
-
let winLast = winFirst
|
|
9584
|
-
let used = 0
|
|
9585
|
-
while (winLast < changeItems.length && used < winLimit) { used += changeItems[winLast].h; winLast += 1 }
|
|
9586
|
+
const winLast = Math.min(changeItems.length, windowStart(changeOffsets, changesViewport.scrollTop + changesViewport.viewH) + 1 + WINDOW_OVERSCAN)
|
|
9586
9587
|
const visibleItems = changeItems.slice(winFirst, winLast)
|
|
9587
9588
|
const aiConfirmDialog = aiAsk !== true ? null : E(ConfirmDialog, {
|
|
9588
9589
|
t: t,
|
|
@@ -9673,7 +9674,7 @@ window.__ModuleLoader__.load({
|
|
|
9673
9674
|
title: t('toolbar.more'),
|
|
9674
9675
|
'data-action': 'more',
|
|
9675
9676
|
onClick: (event) => props.onMoreMenu(event),
|
|
9676
|
-
}, E(Icon, { name: 'more', size:
|
|
9677
|
+
}, E(Icon, { name: 'more', size: 18 })))
|
|
9677
9678
|
return E('div', { className: 'dig-changes' },
|
|
9678
9679
|
aiConfirmDialog,
|
|
9679
9680
|
head,
|
|
@@ -11579,7 +11580,7 @@ window.__ModuleLoader__.load({
|
|
|
11579
11580
|
title: action.disabled === true ? action.label + ' · ' + reason : action.label,
|
|
11580
11581
|
disabled: action.disabled === true,
|
|
11581
11582
|
onClick: action.run,
|
|
11582
|
-
}, E(Icon, { name: action.icon, size:
|
|
11583
|
+
}, E(Icon, { name: action.icon, size: 18 })))
|
|
11583
11584
|
if (overflow === true) {
|
|
11584
11585
|
children.push(E('button', {
|
|
11585
11586
|
key: 'more',
|
|
@@ -11596,7 +11597,7 @@ window.__ModuleLoader__.load({
|
|
|
11596
11597
|
reason: reason,
|
|
11597
11598
|
run: (inner) => action.run(inner === undefined ? event : inner),
|
|
11598
11599
|
}))),
|
|
11599
|
-
}, E(Icon, { name: 'more', size:
|
|
11600
|
+
}, E(Icon, { name: 'more', size: 18 })))
|
|
11600
11601
|
}
|
|
11601
11602
|
children.push(E('span', { key: 'gap', className: vertical ? 'dig-rail-gap' : 'dig-rail-gap-x' }))
|
|
11602
11603
|
children.push(E('button', {
|
|
@@ -11613,7 +11614,7 @@ window.__ModuleLoader__.load({
|
|
|
11613
11614
|
if (target === 'page') { openSettingsPage(); return }
|
|
11614
11615
|
setRailSettings(true)
|
|
11615
11616
|
},
|
|
11616
|
-
}, E(Icon, { name: 'settings', size:
|
|
11617
|
+
}, E(Icon, { name: 'settings', size: 18 })))
|
|
11617
11618
|
return E('div', { className: vertical ? 'dig-rail' : 'dig-rail-row', ref: railHostRef }, children)
|
|
11618
11619
|
}
|
|
11619
11620
|
|
package/src/index.js
CHANGED
|
@@ -239,7 +239,11 @@ function cwdOf(payload) {
|
|
|
239
239
|
/* ============================== git reads ============================== */
|
|
240
240
|
|
|
241
241
|
async function repoRootOf(cwd, payload) {
|
|
242
|
-
|
|
242
|
+
/* Normalize the separator: `rev-parse --show-toplevel` spells drive paths
|
|
243
|
+
with forward slashes on Windows while path.join produces backslashes, so
|
|
244
|
+
the untracked-diff fence (root + path.sep prefix check) rejected every
|
|
245
|
+
in-repo path there. macOS/Linux are byte-identical no-ops. */
|
|
246
|
+
if (payload !== undefined && payload.repoRoot !== undefined) return path.normalize(requireAbsolute(payload.repoRoot, 'repoRoot'))
|
|
243
247
|
const result = await runGit(cwd, ['rev-parse', '--show-toplevel'])
|
|
244
248
|
if (result.code !== 0) {
|
|
245
249
|
// A workspace that is not a repository is a normal state, not a failure:
|
|
@@ -247,7 +251,7 @@ async function repoRootOf(cwd, payload) {
|
|
|
247
251
|
throw new PanelError('not-a-repo', 'not a git repository: ' + cwd, 409)
|
|
248
252
|
}
|
|
249
253
|
const top = result.stdout.trim()
|
|
250
|
-
return top === '' ? cwd : top
|
|
254
|
+
return path.normalize(top === '' ? cwd : top)
|
|
251
255
|
}
|
|
252
256
|
|
|
253
257
|
async function currentBranchOf(root) {
|
|
@@ -800,7 +804,12 @@ async function diff(payload) {
|
|
|
800
804
|
/* --no-index reads the file for real, so unlike the plain `git diff --`
|
|
801
805
|
form an absolute path must NOT be allowed to escape the repository. */
|
|
802
806
|
if (absolute !== root && !absolute.startsWith(root + path.sep)) throw badRequest('path must stay inside the repository')
|
|
803
|
-
|
|
807
|
+
/* git quotes and C-escapes backslash paths inside the diff header (and
|
|
808
|
+
git ≥2.55 spells the a/ side with the real path instead of /dev/null);
|
|
809
|
+
passing forward slashes keeps the header unquoted, so the rewrite below
|
|
810
|
+
can match it verbatim. POSIX is a no-op. */
|
|
811
|
+
const headerPath = absolute.split(path.sep).join('/')
|
|
812
|
+
const result = await runGit(root, ['diff', '--no-color', '--no-ext-diff', '-U3', '--no-index', '--', '/dev/null', headerPath])
|
|
804
813
|
if (result.code !== 0 && result.code !== 1) {
|
|
805
814
|
const detail = result.stderr.trim() === '' ? result.stdout.trim() : result.stderr.trim()
|
|
806
815
|
throw new PanelError('git-failed', 'git diff --no-index failed: ' + (detail === '' ? 'exit ' + result.code : detail), 409)
|
|
@@ -809,12 +818,12 @@ async function diff(payload) {
|
|
|
809
818
|
the header lines back to the repository-relative spelling so the preview
|
|
810
819
|
reads like every other patch (body lines are left untouched). */
|
|
811
820
|
let out = result.stdout
|
|
812
|
-
if (out.indexOf(
|
|
821
|
+
if (out.indexOf(headerPath) >= 0 && out.indexOf('\n') !== -1) {
|
|
813
822
|
out = out.split('\n').map((line) => {
|
|
814
|
-
if (line.startsWith('diff --git ') && line.indexOf(
|
|
815
|
-
/*
|
|
816
|
-
|
|
817
|
-
if (line.startsWith('+++
|
|
823
|
+
if (line.startsWith('diff --git ') && line.indexOf(headerPath) >= 0) return 'diff --git a/' + rel + ' b/' + rel
|
|
824
|
+
/* git ≥2.55 quotes the header and spells the a/ side with the real
|
|
825
|
+
path instead of /dev/null, so match on the path, not the prefix. */
|
|
826
|
+
if (line.startsWith('+++ ') && line.indexOf(headerPath) >= 0) return '+++ b/' + rel
|
|
818
827
|
return line
|
|
819
828
|
}).join('\n')
|
|
820
829
|
}
|