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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-ide-git",
3
- "version": "0.16.2",
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.2",
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 winLimit = Math.ceil(changesViewport.viewH / ITEM_HEIGHTS.row) + WINDOW_OVERSCAN * 3
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: 16 })))
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: 16 })))
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: 16 })))
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: 16 })))
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
- if (payload !== undefined && payload.repoRoot !== undefined) return requireAbsolute(payload.repoRoot, 'repoRoot')
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
- const result = await runGit(root, ['diff', '--no-color', '--no-ext-diff', '-U3', '--no-index', '--', '/dev/null', absolute])
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(absolute) >= 0 && out.indexOf('\n') !== -1) {
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(absolute) >= 0) return 'diff --git a/' + rel + ' b/' + rel
815
- /* the b/ side of --no-index carries the absolute path verbatim (its
816
- leading slash merged into the separator): match '+++ b' + absolute. */
817
- if (line.startsWith('+++ b' + absolute)) return '+++ b/' + rel
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
  }