dsh-ide-git 0.19.8 → 0.19.9
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 +63 -8
- package/src/index.js +8 -1
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.9",
|
|
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.9",
|
|
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
|
@@ -7068,6 +7068,20 @@ window.__ModuleLoader__.load({
|
|
|
7068
7068
|
return kind + ':' + entry.name
|
|
7069
7069
|
}
|
|
7070
7070
|
|
|
7071
|
+
/* v0.19.9: the REMOTE section folds by path exactly like the local one. In
|
|
7072
|
+
the common single-remote repo every row starts with the same "<remote>/"
|
|
7073
|
+
prefix, which would nest every tree under one folder named after the
|
|
7074
|
+
remote — noise, not grouping — so the strip applies only when EVERY name
|
|
7075
|
+
shares that first segment AND carries it (a bare "main" beside
|
|
7076
|
+
"origin/main" means mixed shapes and keeps full names). */
|
|
7077
|
+
function remoteTreeStripOf(names) {
|
|
7078
|
+
if (names.length === 0) return ''
|
|
7079
|
+
const first = names.map((name) => String(name).split('/')[0])
|
|
7080
|
+
if (new Set(first).size !== 1) return ''
|
|
7081
|
+
const prefix = first[0] + '/'
|
|
7082
|
+
return names.every((name) => String(name).startsWith(prefix) === true) ? prefix : ''
|
|
7083
|
+
}
|
|
7084
|
+
|
|
7071
7085
|
/* Join selected values for the clipboard — one per line, the shape a
|
|
7072
7086
|
terminal or an editor expects when pasting a list. */
|
|
7073
7087
|
function clipboardLines(values) {
|
|
@@ -10249,6 +10263,22 @@ window.__ModuleLoader__.load({
|
|
|
10249
10263
|
return props.vertical === true ? rect.width : rect.height
|
|
10250
10264
|
}
|
|
10251
10265
|
const sign = props.side === 'next' ? -1 : 1
|
|
10266
|
+
/* Pointermove arrives faster than frames can paint, and every dragPane
|
|
10267
|
+
* does a forced reflow (measurePanes) plus two setStates — handling each
|
|
10268
|
+
* event is layout thrashing, and the report (#16 follow-up) is exactly
|
|
10269
|
+
* that: a drag starts smooth, then feels glued to the cursor. The latest
|
|
10270
|
+
* px parks in a ref and ONE rAF per frame hands it to the clamp; pointerup
|
|
10271
|
+
* flushes the parked value first so a gesture never ends on a stale size. */
|
|
10272
|
+
const frameRef = useRef(0)
|
|
10273
|
+
const pendingRef = useRef(null)
|
|
10274
|
+
const flushRef = useRef(null)
|
|
10275
|
+
useEffect(() => () => { if (frameRef.current !== 0) cancelAnimationFrame(frameRef.current) }, [])
|
|
10276
|
+
flushRef.current = () => {
|
|
10277
|
+
if (frameRef.current !== 0) { cancelAnimationFrame(frameRef.current); frameRef.current = 0 }
|
|
10278
|
+
const value = pendingRef.current
|
|
10279
|
+
pendingRef.current = null
|
|
10280
|
+
if (value !== null) props.onDrag(value)
|
|
10281
|
+
}
|
|
10252
10282
|
const onPointerDown = (event) => {
|
|
10253
10283
|
if (event.button !== undefined && event.button !== 0) return
|
|
10254
10284
|
dragRef.current = {
|
|
@@ -10264,11 +10294,13 @@ window.__ModuleLoader__.load({
|
|
|
10264
10294
|
const drag = dragRef.current
|
|
10265
10295
|
if (drag === null || drag.id !== event.pointerId) return
|
|
10266
10296
|
const at = props.vertical === true ? event.clientX : event.clientY
|
|
10267
|
-
|
|
10297
|
+
pendingRef.current = drag.size + sign * (at - drag.at)
|
|
10298
|
+
if (frameRef.current === 0) frameRef.current = requestAnimationFrame(() => flushRef.current())
|
|
10268
10299
|
}
|
|
10269
10300
|
const onPointerUp = () => {
|
|
10270
10301
|
if (dragRef.current === null) return
|
|
10271
10302
|
dragRef.current = null
|
|
10303
|
+
if (frameRef.current !== 0) flushRef.current()
|
|
10272
10304
|
props.onEnd()
|
|
10273
10305
|
}
|
|
10274
10306
|
const onKeyDown = (event) => {
|
|
@@ -10478,6 +10510,13 @@ window.__ModuleLoader__.load({
|
|
|
10478
10510
|
double-click browses the current branch's history, right-click opens
|
|
10479
10511
|
the same row menu (quick jump back to HEAD lives there). */
|
|
10480
10512
|
const headEntry = locals.find((entry) => entry.head === true) || null
|
|
10513
|
+
/* v0.19.9: remote rows group by their path BELOW the "<remote>/" prefix;
|
|
10514
|
+
the full name still drives every selKey/menu/checkout path. */
|
|
10515
|
+
const remoteStrip = remoteTreeStripOf(remotes.map((entry) => entry.name))
|
|
10516
|
+
const remoteNameOf = (entry) => {
|
|
10517
|
+
const name = String(entry.name)
|
|
10518
|
+
return remoteStrip !== '' && name.startsWith(remoteStrip) === true ? name.slice(remoteStrip.length) : name
|
|
10519
|
+
}
|
|
10481
10520
|
/* A refresh that makes a picked ref disappear must not keep it selected. */
|
|
10482
10521
|
useEffect(() => {
|
|
10483
10522
|
reportGestureRef.current = false
|
|
@@ -10507,10 +10546,14 @@ window.__ModuleLoader__.load({
|
|
|
10507
10546
|
* row only SELECTS it (checking out rewrites the working tree), so the row
|
|
10508
10547
|
* carries the selection and a double-click does the checkout.
|
|
10509
10548
|
*/
|
|
10510
|
-
|
|
10549
|
+
/* nameOf (v0.19.9) lets the remote section group by its name WITHOUT the
|
|
10550
|
+
"<remote>/" prefix (see remoteTreeStripOf) while every row keeps its
|
|
10551
|
+
full entry.name for selection, menus and checkout. */
|
|
10552
|
+
const forestOf = (entries, nameOf) => {
|
|
10553
|
+
const labelOf = nameOf === undefined ? (entry) => entry.name : nameOf
|
|
10511
10554
|
const root = { folders: new Map(), leaves: [] }
|
|
10512
10555
|
for (const entry of entries) {
|
|
10513
|
-
const parts = String(entry
|
|
10556
|
+
const parts = String(labelOf(entry)).split('/')
|
|
10514
10557
|
let node = root
|
|
10515
10558
|
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
10516
10559
|
const key = parts[index]
|
|
@@ -10591,7 +10634,9 @@ window.__ModuleLoader__.load({
|
|
|
10591
10634
|
for (const leaf of node.leaves) out.push(branchRow(leaf.entry, leaf.label, depth))
|
|
10592
10635
|
return out
|
|
10593
10636
|
}
|
|
10594
|
-
|
|
10637
|
+
/* ns (v0.19.9) namespaces the fold ids of a grouped section ("remote/…")
|
|
10638
|
+
so its folders remember their state independently of the local ones. */
|
|
10639
|
+
const section = (key, title, entries, decorate, grouped, ns, nameOf) => E('div', { className: 'dig-section', key: key },
|
|
10595
10640
|
E('button', {
|
|
10596
10641
|
type: 'button', className: 'dig-section-head',
|
|
10597
10642
|
onClick: () => setCollapsed((previous) => Object.assign({}, previous, { [key]: !previous[key] })),
|
|
@@ -10602,14 +10647,16 @@ window.__ModuleLoader__.load({
|
|
|
10602
10647
|
E('span', null, title)),
|
|
10603
10648
|
collapsed[key] === true ? null : E('div', { className: 'dig-section-body' },
|
|
10604
10649
|
grouped === true
|
|
10605
|
-
? rowsOf(forestOf(entries.map((entry) => decorate(entry))), '', 0)
|
|
10650
|
+
? rowsOf(forestOf(entries.map((entry) => decorate(entry)), nameOf), ns || '', 0)
|
|
10606
10651
|
: entries.map((entry) => branchRow(decorate(entry), undefined, 0))))
|
|
10607
10652
|
/* Render-order mirror for Shift-ranges and Ctrl+A, plus the selKey→entry
|
|
10608
10653
|
lookup the right-click menu and the Delete key resolve through. */
|
|
10609
10654
|
const visibleLeaves = []
|
|
10610
10655
|
if (favorites.length > 0) flatLeaves(forestOf(locals.filter((entry) => favorites.indexOf(entry.name) >= 0)), '', 0, visibleLeaves)
|
|
10611
10656
|
flatLeaves(forestOf(locals), '', 0, visibleLeaves)
|
|
10612
|
-
|
|
10657
|
+
/* Same tree, same prefix namespace the section renders with — Shift-ranges
|
|
10658
|
+
and Ctrl+A must walk the EXACT order rows are shown in. */
|
|
10659
|
+
flatLeaves(forestOf(remotes.map((entry) => Object.assign({}, entry, { remote: true })), remoteNameOf), 'remote', 0, visibleLeaves)
|
|
10613
10660
|
for (const entry of tags) visibleLeaves.push({ entry: entry, label: undefined, depth: 0 })
|
|
10614
10661
|
visibleRef.current = visibleLeaves
|
|
10615
10662
|
const entryBySelKey = new Map()
|
|
@@ -10726,7 +10773,7 @@ window.__ModuleLoader__.load({
|
|
|
10726
10773
|
}, t('branches.head')),
|
|
10727
10774
|
favorites.length === 0 ? null : section('favorites', t('branches.favorites'), locals.filter((entry) => favorites.indexOf(entry.name) >= 0), (entry) => entry),
|
|
10728
10775
|
section('local', t('branches.local'), locals, (entry) => entry, true),
|
|
10729
|
-
section('remote', t('branches.remote'), remotes, (entry) => Object.assign({}, entry, { remote: true })),
|
|
10776
|
+
section('remote', t('branches.remote'), remotes, (entry) => Object.assign({}, entry, { remote: true }), true, 'remote', remoteNameOf),
|
|
10730
10777
|
section('tags', t('branches.tags'), tags, (entry) => Object.assign({}, entry, { tag: true }))),
|
|
10731
10778
|
E(MarqueeBox, { box: treeMarquee.box }))
|
|
10732
10779
|
}
|
|
@@ -12089,7 +12136,15 @@ window.__ModuleLoader__.load({
|
|
|
12089
12136
|
re-clamps a stored size has to see the same numbers the drag did — a drag
|
|
12090
12137
|
allowed further than the render allows snaps back under the cursor. The
|
|
12091
12138
|
bail-out comparison is what keeps this from looping. */
|
|
12092
|
-
|
|
12139
|
+
/* A LAYOUT effect: on mount this runs before the first paint, so the
|
|
12140
|
+
fallback layout (paneGeometry's stylesheet/literal defaults) is
|
|
12141
|
+
replaced by the live one inside the same frame instead of being
|
|
12142
|
+
painted and then jumped a frame later. Painting both is the "height
|
|
12143
|
+
animation" on tab switches from issue #16 — two visible layouts ~80px
|
|
12144
|
+
apart read as a smooth resize in a low-fps screen recording. Later
|
|
12145
|
+
runs still track content arrival; the layout-phase cost is one
|
|
12146
|
+
querySelectorAll over the 3-4 panes the post-paint run already paid. */
|
|
12147
|
+
useMeasureLayoutEffect(() => {
|
|
12093
12148
|
const found = measurePanes()
|
|
12094
12149
|
setPaneSizes((current) => (samePaneSizes(current, found) ? current : found))
|
|
12095
12150
|
})
|
package/src/index.js
CHANGED
|
@@ -755,7 +755,14 @@ async function branches(payload) {
|
|
|
755
755
|
behind: 0,
|
|
756
756
|
}
|
|
757
757
|
if (parts[0].startsWith('refs/heads/')) local.push(entry)
|
|
758
|
-
else if (parts[0].startsWith('refs/remotes/'))
|
|
758
|
+
else if (parts[0].startsWith('refs/remotes/')) {
|
|
759
|
+
/* "<remote>/HEAD" is the symbolic pointer at a remote's default branch —
|
|
760
|
+
its %(refname:short) collapses to the BARE remote name, so it is not a
|
|
761
|
+
branch anyone can check out. It never becomes a row (the stray
|
|
762
|
+
"<remote>" entry used to pollute the remote tree and, grouped, it also
|
|
763
|
+
defeated the tree's single-remote prefix strip). */
|
|
764
|
+
if (/^refs\/remotes\/[^/]+\/HEAD$/.test(parts[0]) === false) remote.push(entry)
|
|
765
|
+
}
|
|
759
766
|
else if (parts[0].startsWith('refs/tags/')) tags.push({ name: entry.name, hash: entry.hash, shortHash: entry.shortHash, date: entry.date, subject: entry.subject })
|
|
760
767
|
}
|
|
761
768
|
for (const entry of local) {
|