dsh-ide-git 0.19.3 → 0.19.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 +82 -12
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.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.19.
|
|
3
|
+
"version": "0.19.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
|
@@ -10375,6 +10375,24 @@ window.__ModuleLoader__.load({
|
|
|
10375
10375
|
selKeys (`local:name` / `remote:name` / `tag:name` — the prefix keeps a
|
|
10376
10376
|
branch and a tag that share a name from colliding). */
|
|
10377
10377
|
const [picks, setPicks] = useState(() => new Set())
|
|
10378
|
+
/* v0.19.4: the Panel bumps clearSignal when ANOTHER list gains the
|
|
10379
|
+
selection (or a blank click clears everywhere) — drop ours. Monotonic
|
|
10380
|
+
epochs start at 0, so the mount pass never clears. */
|
|
10381
|
+
const treeClearSignal = props.clearSignal === undefined ? 0 : props.clearSignal
|
|
10382
|
+
const seenTreeClear = useRef(treeClearSignal)
|
|
10383
|
+
/* v0.19.4 echo guard: only USER gestures may flow up to the Panel — the
|
|
10384
|
+
mirror effect below reports picks exactly once per gesture. Reactive
|
|
10385
|
+
setPicks calls (the clear-signal effect above, the survival filter)
|
|
10386
|
+
must NOT re-enter the forwarder, or the epoch it bumps clears the
|
|
10387
|
+
selection the OTHER list just made (the T1b echo bug). */
|
|
10388
|
+
const reportGestureRef = useRef(false)
|
|
10389
|
+
useEffect(() => {
|
|
10390
|
+
if (seenTreeClear.current !== treeClearSignal) {
|
|
10391
|
+
seenTreeClear.current = treeClearSignal
|
|
10392
|
+
reportGestureRef.current = false
|
|
10393
|
+
setPicks(new Set())
|
|
10394
|
+
}
|
|
10395
|
+
}, [treeClearSignal])
|
|
10378
10396
|
const pickAnchor = useRef(null)
|
|
10379
10397
|
const visibleRef = useRef([])
|
|
10380
10398
|
const [foldedFolders, setFoldedFolders] = useState(() => {
|
|
@@ -10409,6 +10427,7 @@ window.__ModuleLoader__.load({
|
|
|
10409
10427
|
const headEntry = locals.find((entry) => entry.head === true) || null
|
|
10410
10428
|
/* A refresh that makes a picked ref disappear must not keep it selected. */
|
|
10411
10429
|
useEffect(() => {
|
|
10430
|
+
reportGestureRef.current = false
|
|
10412
10431
|
setPicks((previous) => {
|
|
10413
10432
|
if (previous.size === 0) return previous
|
|
10414
10433
|
const alive = new Set()
|
|
@@ -10426,6 +10445,8 @@ window.__ModuleLoader__.load({
|
|
|
10426
10445
|
render-time call would trip the hooks rules): the rail reads them so
|
|
10427
10446
|
its buttons can act on the selection instead of only on HEAD. */
|
|
10428
10447
|
useEffect(() => {
|
|
10448
|
+
if (reportGestureRef.current !== true) return
|
|
10449
|
+
reportGestureRef.current = false
|
|
10429
10450
|
if (props.onPicksChange !== undefined) props.onPicksChange(picks)
|
|
10430
10451
|
}, [picks])
|
|
10431
10452
|
/**
|
|
@@ -10460,6 +10481,7 @@ window.__ModuleLoader__.load({
|
|
|
10460
10481
|
return out
|
|
10461
10482
|
}
|
|
10462
10483
|
const handlePick = useCallback((event, entry, selKey) => {
|
|
10484
|
+
reportGestureRef.current = true
|
|
10463
10485
|
const ordered = visibleRef.current.map((leaf) => branchSelKeyOf(leaf.entry))
|
|
10464
10486
|
if (event.shiftKey === true) {
|
|
10465
10487
|
/* No anchor yet (fresh tree, nothing clicked): VS Code semantics —
|
|
@@ -10581,7 +10603,7 @@ window.__ModuleLoader__.load({
|
|
|
10581
10603
|
})
|
|
10582
10604
|
return rects
|
|
10583
10605
|
},
|
|
10584
|
-
onPick: (keys) => setPicks(new Set(keys)),
|
|
10606
|
+
onPick: (keys) => { reportGestureRef.current = true; setPicks(new Set(keys)) },
|
|
10585
10607
|
})
|
|
10586
10608
|
/* List-scope keyboard: Ctrl/Cmd+C copies the picked names, Delete asks
|
|
10587
10609
|
Panel for a grouped delete (head branches are skipped — git refuses
|
|
@@ -10630,7 +10652,16 @@ window.__ModuleLoader__.load({
|
|
|
10630
10652
|
ref: treeScrollRef,
|
|
10631
10653
|
onPointerDown: treeMarquee.onPointerDown,
|
|
10632
10654
|
onClickCapture: (event) => {
|
|
10633
|
-
if (treeMarquee.swallowRef.current === true) { event.preventDefault(); event.stopPropagation() }
|
|
10655
|
+
if (treeMarquee.swallowRef.current === true) { event.preventDefault(); event.stopPropagation(); return }
|
|
10656
|
+
/* v0.19.4: a blank click (not on a branch row or a control) clears
|
|
10657
|
+
the picks here AND everywhere else — "selection follows the last
|
|
10658
|
+
gesture". The Set goes up through the same forwarder a row click
|
|
10659
|
+
uses, so the other two lists reset as well. */
|
|
10660
|
+
const target = event.target
|
|
10661
|
+
if (target instanceof Element && target.closest('[data-mkey],button,input,textarea,select,a') === null) {
|
|
10662
|
+
reportGestureRef.current = true
|
|
10663
|
+
setPicks(new Set())
|
|
10664
|
+
}
|
|
10634
10665
|
},
|
|
10635
10666
|
},
|
|
10636
10667
|
headEntry === null ? E('div', { className: 'dig-section-head dig-section-head-static' }, t('branches.head')) : E('div', {
|
|
@@ -11082,7 +11113,11 @@ window.__ModuleLoader__.load({
|
|
|
11082
11113
|
},
|
|
11083
11114
|
onPointerDown: changeMarquee.onPointerDown,
|
|
11084
11115
|
onClickCapture: (event) => {
|
|
11085
|
-
if (changeMarquee.swallowRef.current === true) { event.preventDefault(); event.stopPropagation() }
|
|
11116
|
+
if (changeMarquee.swallowRef.current === true) { event.preventDefault(); event.stopPropagation(); return }
|
|
11117
|
+
/* v0.19.4: blank click clears — the wrapped setter also bumps the
|
|
11118
|
+
other lists' epochs, so this is a clear-everywhere gesture. */
|
|
11119
|
+
const target = event.target
|
|
11120
|
+
if (target instanceof Element && target.closest('.dig-row,.dig-group-head,button,input,textarea,select,a') === null) setSelection(new Set())
|
|
11086
11121
|
},
|
|
11087
11122
|
},
|
|
11088
11123
|
collapsed ? null : [
|
|
@@ -11723,7 +11758,12 @@ window.__ModuleLoader__.load({
|
|
|
11723
11758
|
onKeyDown: onListKeyDown,
|
|
11724
11759
|
onPointerDown: historyMarquee.onPointerDown,
|
|
11725
11760
|
onClickCapture: (event) => {
|
|
11726
|
-
if (historyMarquee.swallowRef.current === true) { event.preventDefault(); event.stopPropagation() }
|
|
11761
|
+
if (historyMarquee.swallowRef.current === true) { event.preventDefault(); event.stopPropagation(); return }
|
|
11762
|
+
/* v0.19.4: blank click clears — see the changes/tree twins. */
|
|
11763
|
+
const target = event.target
|
|
11764
|
+
if (target instanceof Element && target.closest('.dig-commit,button,input,textarea,select,a') === null) {
|
|
11765
|
+
if (props.onPicksChange !== undefined) props.onPicksChange(new Set())
|
|
11766
|
+
}
|
|
11727
11767
|
},
|
|
11728
11768
|
onPointerDownCapture: (event) => {
|
|
11729
11769
|
if (isTypingTarget(event.target) === false) event.currentTarget.focus({ preventScroll: true })
|
|
@@ -11845,6 +11885,28 @@ window.__ModuleLoader__.load({
|
|
|
11845
11885
|
/* The branch tree reports its picks up (onPicksChange) so the rail can act
|
|
11846
11886
|
on the selection: "select main, press pull" must pull main, not HEAD. */
|
|
11847
11887
|
const [branchPicks, setBranchPicks] = useState(() => new Set())
|
|
11888
|
+
/* v0.19.4: the three lists' selections are mutually exclusive — a pick in
|
|
11889
|
+
one list (row click or rubber band) clears the other two, and a blank
|
|
11890
|
+
click inside a list clears everything too. Each interaction bumps the
|
|
11891
|
+
OTHER lists' epoch counters; the effects below (and a clearSignal prop
|
|
11892
|
+
into BranchTree, whose picks live inside it) reset on change. Epochs
|
|
11893
|
+
are monotonic and start at 0, so the mount pass clears nothing. */
|
|
11894
|
+
const [pickEpochs, setPickEpochs] = useState(() => ({ changes: 0, history: 0, tree: 0 }))
|
|
11895
|
+
const clearPicksExcept = useCallback((owner) => {
|
|
11896
|
+
setPickEpochs((previous) => ({
|
|
11897
|
+
changes: owner === 'changes' ? previous.changes : previous.changes + 1,
|
|
11898
|
+
history: owner === 'history' ? previous.history : previous.history + 1,
|
|
11899
|
+
tree: owner === 'tree' ? previous.tree : previous.tree + 1,
|
|
11900
|
+
}))
|
|
11901
|
+
}, [])
|
|
11902
|
+
useEffect(() => { if (pickEpochs.changes > 0) setChangeSelection(new Set()) }, [pickEpochs.changes])
|
|
11903
|
+
useEffect(() => { if (pickEpochs.history > 0) setPickedCommits(new Set()) }, [pickEpochs.history])
|
|
11904
|
+
/* Forwarders: the raw setters wrapped so EVERY selection gesture reports
|
|
11905
|
+
an owner. The updates arrive either as a Set or as a functional
|
|
11906
|
+
updater (the memoised rows hold stale closures) — forwarded as-is. */
|
|
11907
|
+
const onBranchPicks = useCallback((update) => { setBranchPicks(update); clearPicksExcept('tree') }, [clearPicksExcept])
|
|
11908
|
+
const onChangeSelection = useCallback((update) => { setChangeSelection(update); clearPicksExcept('changes') }, [clearPicksExcept])
|
|
11909
|
+
const onCommitPicks = useCallback((update) => { setPickedCommits(update); clearPicksExcept('history') }, [clearPicksExcept])
|
|
11848
11910
|
const [menu, setMenu] = useState(null)
|
|
11849
11911
|
const [dialog, setDialog] = useState(null)
|
|
11850
11912
|
const [tick, setTick] = useState(0)
|
|
@@ -13269,8 +13331,8 @@ window.__ModuleLoader__.load({
|
|
|
13269
13331
|
onLoadMore: () => { void guard(() => loadCommits(commits.length)) },
|
|
13270
13332
|
/* ---- multi-select (issue #13) ---- */
|
|
13271
13333
|
pickedHashes: pickedCommits,
|
|
13272
|
-
onPicksChange:
|
|
13273
|
-
onSelectAll: (hashes) =>
|
|
13334
|
+
onPicksChange: onCommitPicks,
|
|
13335
|
+
onSelectAll: (hashes) => onCommitPicks(new Set(hashes)),
|
|
13274
13336
|
onKeyDown: onHistoryKeyDown,
|
|
13275
13337
|
mergeAnchor: mergeAnchor,
|
|
13276
13338
|
base: base,
|
|
@@ -13543,7 +13605,7 @@ window.__ModuleLoader__.load({
|
|
|
13543
13605
|
onChangeMenu: changeMenu,
|
|
13544
13606
|
/* ---- multi-select (issue #13) ---- */
|
|
13545
13607
|
selection: changeSelection,
|
|
13546
|
-
onSelectionChange:
|
|
13608
|
+
onSelectionChange: onChangeSelection,
|
|
13547
13609
|
anchorRef: changeAnchor,
|
|
13548
13610
|
onBulkStage: bulkStageChanges,
|
|
13549
13611
|
onBulkUnstage: bulkUnstageChanges,
|
|
@@ -13824,19 +13886,19 @@ window.__ModuleLoader__.load({
|
|
|
13824
13886
|
title: t('toolbar.tree'),
|
|
13825
13887
|
onClick: () => foldTree(!treeOpen),
|
|
13826
13888
|
}, E(Icon, { name: treeOpen ? 'eye' : 'eyeOff' }))),
|
|
13827
|
-
treeOpen ? E('div', { className: 'dig-compact-tree', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBrowse: browseBranch, onBranchMenu: branchMenu, onDeleteEntries: deleteBranchEntries, onCopyValues: copyValues, onPicksChange:
|
|
13889
|
+
treeOpen ? E('div', { className: 'dig-compact-tree', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBrowse: browseBranch, onBranchMenu: branchMenu, onDeleteEntries: deleteBranchEntries, onCopyValues: copyValues, onPicksChange: onBranchPicks, clearSignal: pickEpochs.tree })) : null,
|
|
13828
13890
|
treeOpen ? gutter('tree', 'prev') : null,
|
|
13829
13891
|
view === 'changes' ? changesPane : E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffGutter, diffPane))
|
|
13830
13892
|
} else if (columns) {
|
|
13831
13893
|
body = E('div', { className: 'dig-body dig-body-columns', ref: bodyRef },
|
|
13832
|
-
treeOpen ? E('div', { className: 'dig-pane dig-pane-tree', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBrowse: browseBranch, onBranchMenu: branchMenu, onDeleteEntries: deleteBranchEntries, onCopyValues: copyValues, onPicksChange:
|
|
13894
|
+
treeOpen ? E('div', { className: 'dig-pane dig-pane-tree', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBrowse: browseBranch, onBranchMenu: branchMenu, onDeleteEntries: deleteBranchEntries, onCopyValues: copyValues, onPicksChange: onBranchPicks, clearSignal: pickEpochs.tree })) : null,
|
|
13833
13895
|
treeOpen ? gutter('tree', 'prev') : null,
|
|
13834
13896
|
E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffGutter, diffPane),
|
|
13835
13897
|
gutter('changes', 'next'),
|
|
13836
13898
|
E('div', { className: 'dig-pane dig-pane-changes', 'data-pane': 'changes', style: paneStyle('changes') }, changesPane))
|
|
13837
13899
|
} else {
|
|
13838
13900
|
body = E('div', { className: 'dig-body dig-body-stack', ref: bodyRef },
|
|
13839
|
-
treeOpen ? E('div', { className: 'dig-pane dig-pane-tree-stack', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBrowse: browseBranch, onBranchMenu: branchMenu, onDeleteEntries: deleteBranchEntries, onCopyValues: copyValues, onPicksChange:
|
|
13901
|
+
treeOpen ? E('div', { className: 'dig-pane dig-pane-tree-stack', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBrowse: browseBranch, onBranchMenu: branchMenu, onDeleteEntries: deleteBranchEntries, onCopyValues: copyValues, onPicksChange: onBranchPicks, clearSignal: pickEpochs.tree })) : null,
|
|
13840
13902
|
treeOpen ? gutter('tree', 'prev') : null,
|
|
13841
13903
|
E('div', { className: 'dig-pane dig-pane-changes-stack', 'data-pane': 'changes', style: paneStyle('changes') }, changesPane),
|
|
13842
13904
|
gutter('changes', 'prev'),
|
|
@@ -14392,7 +14454,14 @@ window.__ModuleLoader__.load({
|
|
|
14392
14454
|
'.dig-head-branch{cursor:pointer;transition:background .12s}',
|
|
14393
14455
|
'.dig-head-branch:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
14394
14456
|
'.dig-row-head{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary))}',
|
|
14395
|
-
|
|
14457
|
+
/* v0.19.4: the flat alias background read as "barely selected" over the
|
|
14458
|
+
translucency/frost surfaces the any-background plugin produces. Mixing
|
|
14459
|
+
the token with the row's own TEXT color keeps the theme hues (light
|
|
14460
|
+
theme darkens, dark theme lightens — both gain contrast), and because
|
|
14461
|
+
the text color is near-opaque the mix carries an alpha FLOOR (~42%):
|
|
14462
|
+
the token's own translucency still dominates the look (58%), so the
|
|
14463
|
+
selection follows the plugin's transparency while staying readable. */
|
|
14464
|
+
'.dig-row-selected{background:color-mix(in srgb,var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1)))) 58%,currentColor 42%);color:var(--dsw-alias-label-primary);box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary)}',
|
|
14396
14465
|
'.dig-row-remote .dig-row-label{color:var(--dsw-alias-label-secondary);font-weight:500}',
|
|
14397
14466
|
'.dig-row-tag .dig-row-label{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary));font-weight:500}',
|
|
14398
14467
|
'.dig-row-head .dig-row-label{font-weight:600}',
|
|
@@ -14416,7 +14485,8 @@ window.__ModuleLoader__.load({
|
|
|
14416
14485
|
'.dig-history-scroll{flex:1;overflow:auto}',
|
|
14417
14486
|
'.dig-commit{display:flex;align-items:center;gap:6px;padding:1px 8px 1px 0;cursor:pointer;height:26px;overflow:hidden}',
|
|
14418
14487
|
'.dig-commit:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
14419
|
-
|
|
14488
|
+
/* Same floor + tint mix as .dig-row-selected (see it). */
|
|
14489
|
+
'.dig-commit-selected{background:color-mix(in srgb,var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1)))) 58%,currentColor 42%);color:var(--dsw-alias-label-primary);box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary)}',
|
|
14420
14490
|
// A favorited branch renders its star SOLID (BranchRow adds this class
|
|
14421
14491
|
// and flips the tone to warn) — the hollow green HEAD star read as
|
|
14422
14492
|
// "not favorited" (v0.18.5).
|