dsh-ide-git 0.18.5 → 0.18.6
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 +97 -26
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.6",
|
|
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.18.
|
|
3
|
+
"version": "0.18.6",
|
|
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
|
@@ -7762,6 +7762,55 @@ window.__ModuleLoader__.load({
|
|
|
7762
7762
|
return index < 0 ? '' : filePath.slice(0, index)
|
|
7763
7763
|
}
|
|
7764
7764
|
|
|
7765
|
+
/* A REAL directory tree for the "by directory" grouping (v0.18.6): files
|
|
7766
|
+
are inserted by path segments, then a folder that has no direct files
|
|
7767
|
+
and exactly one child is COMPRESSED into that child ("a/b/c" as one
|
|
7768
|
+
node, VS Code source-control style) — the flat per-directory heads the
|
|
7769
|
+
user rejected rendered every folder of a chain side by side, so
|
|
7770
|
+
`pkg/service` and `pkg/service/impl` looked like unrelated siblings.
|
|
7771
|
+
Returns the compressed roots (sorted), every foldable node id (for
|
|
7772
|
+
fold-all), the files sitting at the group root, and the two recursive
|
|
7773
|
+
helpers the renderer needs. */
|
|
7774
|
+
function buildDirForest(items, groupKey) {
|
|
7775
|
+
const root = { dirs: new Map(), files: [], path: [] }
|
|
7776
|
+
for (const item of items) {
|
|
7777
|
+
const dir = dirName(item.path)
|
|
7778
|
+
const parts = dir === '' ? [] : dir.split('/')
|
|
7779
|
+
let node = root
|
|
7780
|
+
for (const part of parts) {
|
|
7781
|
+
let child = node.dirs.get(part)
|
|
7782
|
+
if (child === undefined) {
|
|
7783
|
+
child = { dirs: new Map(), files: [], path: node.path.concat([part]) }
|
|
7784
|
+
node.dirs.set(part, child)
|
|
7785
|
+
}
|
|
7786
|
+
node = child
|
|
7787
|
+
}
|
|
7788
|
+
node.files.push(item)
|
|
7789
|
+
}
|
|
7790
|
+
const compress = (node) => {
|
|
7791
|
+
let merged = node
|
|
7792
|
+
while (merged.files.length === 0 && merged.dirs.size === 1) {
|
|
7793
|
+
const only = merged.dirs.values().next().value
|
|
7794
|
+
merged = { dirs: only.dirs, files: only.files, path: only.path }
|
|
7795
|
+
}
|
|
7796
|
+
return merged
|
|
7797
|
+
}
|
|
7798
|
+
const countOf = (node) => {
|
|
7799
|
+
let total = node.files.length
|
|
7800
|
+
for (const child of node.dirs.values()) total += countOf(child)
|
|
7801
|
+
return total
|
|
7802
|
+
}
|
|
7803
|
+
const sortedChildren = (node) => Array.from(node.dirs.values()).sort((a, b) => a.path[a.path.length - 1].localeCompare(b.path[b.path.length - 1]))
|
|
7804
|
+
const ids = []
|
|
7805
|
+
const collect = (node) => {
|
|
7806
|
+
ids.push(groupKey + '|' + node.path.join('/'))
|
|
7807
|
+
for (const child of sortedChildren(node)) collect(child)
|
|
7808
|
+
}
|
|
7809
|
+
const roots = sortedChildren(root).map((child) => compress(child))
|
|
7810
|
+
for (const node of roots) collect(node)
|
|
7811
|
+
return { roots: roots, rootFiles: root.files, ids: ids, countOf: countOf, sortedChildren: sortedChildren }
|
|
7812
|
+
}
|
|
7813
|
+
|
|
7765
7814
|
/* ============================== commit graph ============================== */
|
|
7766
7815
|
|
|
7767
7816
|
function buildRows(commits) {
|
|
@@ -10155,8 +10204,12 @@ window.__ModuleLoader__.load({
|
|
|
10155
10204
|
// stage/discard buttons below would silently do nothing (git needs -f).
|
|
10156
10205
|
const readOnly = props.group === 'ignored'
|
|
10157
10206
|
const status = readOnly ? 'I' : (raw === '' ? 'M' : raw.charAt(0))
|
|
10207
|
+
/* Tree depth in dir-grouped mode (-1 = flat): every level under a
|
|
10208
|
+
folder head indents one 12px step past the base 26px. */
|
|
10209
|
+
const depth = typeof props.depth === 'number' ? props.depth : -1
|
|
10158
10210
|
return E('div', {
|
|
10159
|
-
className: 'dig-row dig-row-file' + (props.selected === true ? ' dig-row-selected' : '') + (
|
|
10211
|
+
className: 'dig-row dig-row-file' + (props.selected === true ? ' dig-row-selected' : '') + (depth >= 0 ? ' dig-row-nested' : ''),
|
|
10212
|
+
style: depth >= 0 ? { paddingLeft: (26 + depth * 12) + 'px' } : undefined,
|
|
10160
10213
|
title: item.origPath === undefined ? item.path : item.origPath + ' → ' + item.path,
|
|
10161
10214
|
/* The click is handled ONE level up (ChangesPanel): plain click only
|
|
10162
10215
|
selects, DOUBLE-click opens the diff (v0.18.3 — one interaction model
|
|
@@ -10173,14 +10226,14 @@ window.__ModuleLoader__.load({
|
|
|
10173
10226
|
// In dir-grouped mode the folder head already names the directory —
|
|
10174
10227
|
// repeating the full path at the end of every nested row is what made
|
|
10175
10228
|
// the tree read as a flat list (v0.18.5). Flat modes keep it.
|
|
10176
|
-
|
|
10229
|
+
depth >= 0 ? null : E('span', { className: 'dig-row-sub dig-row-dir' }, item.path.slice(-1) === '/' ? '' : dirName(item.path)),
|
|
10177
10230
|
item.additions > 0 ? E('span', { className: 'dig-stat-add' }, '+' + item.additions) : null,
|
|
10178
10231
|
item.deletions > 0 ? E('span', { className: 'dig-stat-del' }, '-' + item.deletions) : null,
|
|
10179
10232
|
readOnly ? null : (props.group === 'staged'
|
|
10180
10233
|
? E('button', { type: 'button', className: 'dig-mini', title: t('action.unstage'), onClick: (event) => { event.stopPropagation(); props.onUnstage(item) } }, E(Icon, { name: 'minus', size: 12 }))
|
|
10181
10234
|
: E('button', { type: 'button', className: 'dig-mini', title: t('action.stage'), onClick: (event) => { event.stopPropagation(); props.onStage(item) } }, E(Icon, { name: 'plus', size: 12 }))),
|
|
10182
10235
|
readOnly ? null : E('button', { type: 'button', className: 'dig-mini', title: t('action.discard'), onClick: (event) => { event.stopPropagation(); props.onDiscard(item, props.group) } }, E(Icon, { name: 'undo', size: 12 })))
|
|
10183
|
-
}, (prev, next) => prev.item === next.item && prev.group === next.group && prev.t === next.t && prev.selected === next.selected && prev.
|
|
10236
|
+
}, (prev, next) => prev.item === next.item && prev.group === next.group && prev.t === next.t && prev.selected === next.selected && prev.depth === next.depth)
|
|
10184
10237
|
|
|
10185
10238
|
function ChangesPanel(props) {
|
|
10186
10239
|
const t = props.t
|
|
@@ -10416,18 +10469,14 @@ window.__ModuleLoader__.load({
|
|
|
10416
10469
|
setAmend(false)
|
|
10417
10470
|
return true
|
|
10418
10471
|
}
|
|
10419
|
-
const entryRow = (key, item,
|
|
10472
|
+
const entryRow = (key, item, depth) => E(ChangeRow, {
|
|
10420
10473
|
key: key + ':' + item.path, item: item, group: key, t: t,
|
|
10421
|
-
|
|
10474
|
+
depth: typeof depth === 'number' ? depth : -1,
|
|
10422
10475
|
selected: selection.has(key + ':' + item.path) === true,
|
|
10423
10476
|
onRowClick: handleRowClick,
|
|
10424
10477
|
onDblClick: props.onDiff,
|
|
10425
10478
|
onStage: props.onStage, onUnstage: props.onUnstage, onDiscard: props.onDiscard, onDiff: props.onDiff, onMenu: props.onChangeMenu,
|
|
10426
10479
|
})
|
|
10427
|
-
const folderOf = (item) => {
|
|
10428
|
-
const dir = dirName(item.path)
|
|
10429
|
-
return dir === '' ? t('changes.rootDir') : dir
|
|
10430
|
-
}
|
|
10431
10480
|
/* The "..." menu's sort orders (v0.15). 'git' keeps git's own status
|
|
10432
10481
|
order; the others are pure client-side sorts of already-loaded
|
|
10433
10482
|
entries — no extra git calls, no re-fetch. */
|
|
@@ -10455,29 +10504,48 @@ window.__ModuleLoader__.load({
|
|
|
10455
10504
|
for (const item of sorted) out.push({ kind: 'row', key: key + ':' + item.path, h: ITEM_HEIGHTS.row, node: entryRow(key, item) })
|
|
10456
10505
|
return
|
|
10457
10506
|
}
|
|
10458
|
-
const
|
|
10459
|
-
|
|
10460
|
-
const
|
|
10461
|
-
if (folders.has(name) === false) folders.set(name, [])
|
|
10462
|
-
folders.get(name).push(item)
|
|
10463
|
-
}
|
|
10464
|
-
for (const name of Array.from(folders.keys()).sort()) {
|
|
10465
|
-
const id = key + '|' + name
|
|
10507
|
+
const forest = buildDirForest(sorted, key)
|
|
10508
|
+
const renderFolder = (node, depth) => {
|
|
10509
|
+
const id = key + '|' + node.path.join('/')
|
|
10466
10510
|
const open = foldedDirs[id] !== true
|
|
10467
|
-
const list = folders.get(name)
|
|
10468
10511
|
out.push({
|
|
10469
10512
|
kind: 'folder', key: 'folder:' + id, h: ITEM_HEIGHTS.folder,
|
|
10470
10513
|
node: E('button', {
|
|
10471
10514
|
type: 'button', className: 'dig-folder-head',
|
|
10515
|
+
style: { paddingLeft: (4 + depth * 12) + 'px' },
|
|
10472
10516
|
onClick: () => setFoldedDirs((previous) => Object.assign({}, previous, { [id]: previous[id] !== true })),
|
|
10473
10517
|
},
|
|
10474
10518
|
E('span', { className: 'dig-chevron' + (open ? ' dig-chevron-open' : '') }, E(Icon, { name: 'chevron', size: 10 })),
|
|
10475
10519
|
E('span', { className: 'dig-row-icon dig-tone-accent' }, E(Icon, { name: 'folder', size: 10 })),
|
|
10476
|
-
E('span', { className: 'dig-folder-name' },
|
|
10477
|
-
E('span', { className: 'dig-count' }, String(
|
|
10520
|
+
E('span', { className: 'dig-folder-name' }, node.path.join('/')),
|
|
10521
|
+
E('span', { className: 'dig-count' }, String(forest.countOf(node)))),
|
|
10522
|
+
})
|
|
10523
|
+
if (open === true) {
|
|
10524
|
+
/* VS Code order inside a folder: subfolders first (recursively,
|
|
10525
|
+
each indented one more level), then this folder's own files. */
|
|
10526
|
+
for (const child of forest.sortedChildren(node)) renderFolder(child, depth + 1)
|
|
10527
|
+
for (const item of sortedOf(node.files)) out.push({ kind: 'row', key: key + ':' + item.path, h: ITEM_HEIGHTS.row, node: entryRow(key, item, depth) })
|
|
10528
|
+
}
|
|
10529
|
+
}
|
|
10530
|
+
/* Files sitting at the group ROOT get their own (root) head, sorted
|
|
10531
|
+
first — the flat grouping used to give them a localized head too. */
|
|
10532
|
+
if (forest.rootFiles.length > 0) {
|
|
10533
|
+
const rootId = key + '|/'
|
|
10534
|
+
const rootOpen = foldedDirs[rootId] !== true
|
|
10535
|
+
out.push({
|
|
10536
|
+
kind: 'folder', key: 'folder:' + rootId, h: ITEM_HEIGHTS.folder,
|
|
10537
|
+
node: E('button', {
|
|
10538
|
+
type: 'button', className: 'dig-folder-head',
|
|
10539
|
+
onClick: () => setFoldedDirs((previous) => Object.assign({}, previous, { [rootId]: previous[rootId] !== true })),
|
|
10540
|
+
},
|
|
10541
|
+
E('span', { className: 'dig-chevron' + (rootOpen ? ' dig-chevron-open' : '') }, E(Icon, { name: 'chevron', size: 10 })),
|
|
10542
|
+
E('span', { className: 'dig-row-icon dig-tone-accent' }, E(Icon, { name: 'folder', size: 10 })),
|
|
10543
|
+
E('span', { className: 'dig-folder-name' }, t('changes.rootDir')),
|
|
10544
|
+
E('span', { className: 'dig-count' }, String(forest.rootFiles.length))),
|
|
10478
10545
|
})
|
|
10479
|
-
if (
|
|
10546
|
+
if (rootOpen === true) for (const item of sortedOf(forest.rootFiles)) out.push({ kind: 'row', key: key + ':' + item.path, h: ITEM_HEIGHTS.row, node: entryRow(key, item, 0) })
|
|
10480
10547
|
}
|
|
10548
|
+
for (const node of forest.roots) renderFolder(node, 0)
|
|
10481
10549
|
}
|
|
10482
10550
|
const emitGroup = (key, title, entries) => {
|
|
10483
10551
|
if (entries.length === 0) return
|
|
@@ -12600,10 +12668,12 @@ window.__ModuleLoader__.load({
|
|
|
12600
12668
|
if (changes !== null) {
|
|
12601
12669
|
const groups = [['conflicted', changes.conflicted], ['staged', changes.staged], ['unstaged', changes.unstaged], ['untracked', changes.untracked], ['ignored', changes.ignored === undefined ? [] : changes.ignored]]
|
|
12602
12670
|
for (const pair of groups) {
|
|
12603
|
-
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12671
|
+
/* Fold every node of the compressed tree — the ids come from
|
|
12672
|
+
the same builder the renderer uses, so single-chain nodes
|
|
12673
|
+
("a/b/c") fold exactly where they render (v0.18.6). */
|
|
12674
|
+
const forest = buildDirForest(pair[1], pair[0])
|
|
12675
|
+
if (forest.rootFiles.length > 0) next[pair[0] + '|/'] = true
|
|
12676
|
+
for (const id of forest.ids) next[id] = true
|
|
12607
12677
|
}
|
|
12608
12678
|
}
|
|
12609
12679
|
setFoldedDirs(next)
|
|
@@ -13598,7 +13668,8 @@ window.__ModuleLoader__.load({
|
|
|
13598
13668
|
'.dig-star-filled svg path{fill:currentColor}',
|
|
13599
13669
|
// Rows nested under a dir-group folder head are indented and drop the
|
|
13600
13670
|
// trailing directory repeat, so the grouping reads as a tree.
|
|
13601
|
-
|
|
13671
|
+
/* Nested rows indent inline (26px base + 12px per tree depth) — the CSS
|
|
13672
|
+
class only marks them for tests/probes. */
|
|
13602
13673
|
'.dig-graph{flex:none}',
|
|
13603
13674
|
'.dig-commit-date{color:var(--dsw-alias-label-tertiary);flex:none;width:54px;font-variant-numeric:tabular-nums;font-weight:500}',
|
|
13604
13675
|
'.dig-commit-author{color:var(--dsw-alias-label-secondary);flex:none;width:96px;overflow:hidden;text-overflow:ellipsis;font-weight:500}',
|