dsh-ide-git 0.5.4 → 0.5.5
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 +91 -33
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
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.5.
|
|
3
|
+
"version": "0.5.5",
|
|
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
|
@@ -3536,6 +3536,12 @@ window.__ModuleLoader__.load({
|
|
|
3536
3536
|
const present = new Set()
|
|
3537
3537
|
for (const commit of commits) present.add(commit.hash)
|
|
3538
3538
|
const lanes = []
|
|
3539
|
+
/* The current branch keeps the LEFTMOST column whenever it is on the
|
|
3540
|
+
board: pre-seeding its hash makes that commit claim lane 0 when its
|
|
3541
|
+
row arrives, so HEAD's line never drifts to the right of a side
|
|
3542
|
+
branch (the rest of the lanes fill newest-first, see below). */
|
|
3543
|
+
const headCommit = commits.find((commit) => Array.isArray(commit.refs) && commit.refs.some((ref) => String(ref).indexOf('HEAD') >= 0))
|
|
3544
|
+
if (headCommit !== undefined) lanes.push(headCommit.hash)
|
|
3539
3545
|
const rows = []
|
|
3540
3546
|
for (const commit of commits) {
|
|
3541
3547
|
const before = lanes.slice()
|
|
@@ -3593,30 +3599,46 @@ window.__ModuleLoader__.load({
|
|
|
3593
3599
|
centre where the lane genuinely starts (a branch tip) or ends (a root).
|
|
3594
3600
|
v0.1.7 started every non-own lane at 0 and every own lane at height/2, which
|
|
3595
3601
|
left a visible gap between two consecutive commits on the same lane. */
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3602
|
+
/* Vertical lane segments for one row. A lane that this row OPENS through an
|
|
3603
|
+
edge curve (it was empty above, and the curve lands at the row bottom) must
|
|
3604
|
+
NOT also get a vertical segment from the row centre: the two overlapped and
|
|
3605
|
+
left a floating hook where the line visibly started in mid-air — reported
|
|
3606
|
+
twice by the user as "断开的线/无端点的分叉". The curve alone carries the
|
|
3607
|
+
connection, and the next row's segment starts at its top edge. */
|
|
3608
|
+
function laneSegments(row, height) {
|
|
3600
3609
|
const middle = height / 2
|
|
3601
|
-
const children = []
|
|
3602
|
-
/* Lanes beyond the cap share the last column: the geometry stays bounded
|
|
3603
|
-
and the subject column keeps its room. */
|
|
3604
|
-
const x = (lane) => 8 + Math.min(lane, GRAPH_MAX_LANES - 1) * LANE_WIDTH
|
|
3605
|
-
const color = (lane) => LANE_COLORS[lane % LANE_COLORS.length]
|
|
3606
3610
|
const depth = Math.max(row.before.length, row.after.length)
|
|
3611
|
+
const segments = []
|
|
3607
3612
|
for (let lane = 0; lane < depth; lane += 1) {
|
|
3608
3613
|
const above = lane < row.before.length ? row.before[lane] : null
|
|
3609
3614
|
const below = lane < row.after.length ? row.after[lane] : null
|
|
3610
3615
|
const hasTop = above !== null && above !== undefined
|
|
3611
3616
|
const hasBottom = below !== null && below !== undefined
|
|
3612
3617
|
if (hasTop === false && hasBottom === false) continue
|
|
3618
|
+
if (hasTop === false && row.parentLanes.indexOf(lane) >= 0) continue
|
|
3613
3619
|
const y1 = hasTop ? 0 : middle
|
|
3614
3620
|
const y2 = hasBottom ? height : middle
|
|
3615
3621
|
if (y1 === y2) continue
|
|
3622
|
+
segments.push({ lane: lane, y1: y1, y2: y2 })
|
|
3623
|
+
}
|
|
3624
|
+
return segments
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
function GraphCell(props) {
|
|
3628
|
+
const row = props.row
|
|
3629
|
+
const height = props.height
|
|
3630
|
+
const width = Math.min(Math.max(1, row.width), GRAPH_MAX_LANES) * LANE_WIDTH + 8
|
|
3631
|
+
const middle = height / 2
|
|
3632
|
+
const children = []
|
|
3633
|
+
/* Lanes beyond the cap share the last column: the geometry stays bounded
|
|
3634
|
+
and the subject column keeps its room. */
|
|
3635
|
+
const x = (lane) => 8 + Math.min(lane, GRAPH_MAX_LANES - 1) * LANE_WIDTH
|
|
3636
|
+
const color = (lane) => LANE_COLORS[lane % LANE_COLORS.length]
|
|
3637
|
+
for (const segment of laneSegments(row, height)) {
|
|
3616
3638
|
children.push(E('line', {
|
|
3617
|
-
key: 'lane-' + lane,
|
|
3618
|
-
x1: x(lane), y1: y1, x2: x(lane), y2: y2,
|
|
3619
|
-
stroke: color(lane), strokeWidth: 2, strokeLinecap: 'round',
|
|
3639
|
+
key: 'lane-' + segment.lane,
|
|
3640
|
+
x1: x(segment.lane), y1: segment.y1, x2: x(segment.lane), y2: segment.y2,
|
|
3641
|
+
stroke: color(segment.lane), strokeWidth: 2, strokeLinecap: 'round',
|
|
3620
3642
|
}))
|
|
3621
3643
|
}
|
|
3622
3644
|
for (const parentLane of row.parentLanes) {
|
|
@@ -3953,6 +3975,25 @@ window.__ModuleLoader__.load({
|
|
|
3953
3975
|
E(Icon, { name: 'chevron', size: 12 })))
|
|
3954
3976
|
}
|
|
3955
3977
|
|
|
3978
|
+
/* The history filters were the LAST native <select>s: their popup is drawn
|
|
3979
|
+
by the OS, so a dark translucent panel still got a light system list —
|
|
3980
|
+
reported on v0.5.4 ("only the one at the top was replaced"). Same
|
|
3981
|
+
treatment as the header switchers: a button over a panel-internal menu. */
|
|
3982
|
+
function FilterSelect(props) {
|
|
3983
|
+
const options = Array.isArray(props.options) ? props.options : []
|
|
3984
|
+
let current = options[0]
|
|
3985
|
+
for (const option of options) { if (option.value === props.value) { current = option; break } }
|
|
3986
|
+
const label = props.label + ': ' + (current === undefined ? '' : current.label)
|
|
3987
|
+
return E('button', {
|
|
3988
|
+
type: 'button',
|
|
3989
|
+
className: 'dig-filter-select' + (props.value === '' ? '' : ' dig-filter-on'),
|
|
3990
|
+
title: props.hint === undefined ? label : props.hint,
|
|
3991
|
+
onClick: (event) => { if (props.onOpenMenu !== undefined) props.onOpenMenu(event, options) },
|
|
3992
|
+
},
|
|
3993
|
+
E('span', { className: 'dig-filter-label' }, label),
|
|
3994
|
+
E(Icon, { name: 'chevron', size: 11 }))
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3956
3997
|
function RepoPicker(props) {
|
|
3957
3998
|
const t = props.t
|
|
3958
3999
|
const repos = props.repos
|
|
@@ -4375,31 +4416,45 @@ window.__ModuleLoader__.load({
|
|
|
4375
4416
|
applyPath('')
|
|
4376
4417
|
}
|
|
4377
4418
|
|
|
4378
|
-
|
|
4419
|
+
/* Every filter opens the same in-panel menu; a checked row marks the active
|
|
4420
|
+
value, so the button never has to spell the whole list out. */
|
|
4421
|
+
const openFilterMenu = (event, options, currentValue, onPick) => {
|
|
4422
|
+
if (props.openMenuAt === undefined) return
|
|
4423
|
+
props.openMenuAt(event, options.map((option) => ({
|
|
4424
|
+
id: 'filter:' + String(option.value),
|
|
4425
|
+
icon: option.value === '' ? 'filter' : 'check',
|
|
4426
|
+
tone: option.value === '' ? 'secondary' : 'accent',
|
|
4427
|
+
label: option.label,
|
|
4428
|
+
active: option.value === currentValue,
|
|
4429
|
+
run: () => onPick(option.value),
|
|
4430
|
+
})))
|
|
4431
|
+
}
|
|
4432
|
+
const allOption = { value: '', label: t('filter.all') }
|
|
4433
|
+
const filterBar = props.hideSearch === true ? null : E('div', { className: 'dig-filters' },
|
|
4379
4434
|
E('input', {
|
|
4380
4435
|
className: 'dig-input dig-input-compact dig-filter-text', value: text, spellCheck: false,
|
|
4381
4436
|
placeholder: t('history.filter'),
|
|
4382
4437
|
onChange: (event) => setText(event.target.value),
|
|
4383
4438
|
}),
|
|
4384
|
-
E(
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
E(
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
E(
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4439
|
+
E(FilterSelect, {
|
|
4440
|
+
label: t('filter.branch'), value: refName, hint: t('filter.branch'),
|
|
4441
|
+
options: [allOption].concat(refOptions.map((name) => ({ value: name, label: name }))),
|
|
4442
|
+
onOpenMenu: (event, options) => openFilterMenu(event, options, refName, setRefName),
|
|
4443
|
+
}),
|
|
4444
|
+
E(FilterSelect, {
|
|
4445
|
+
label: t('filter.user'), value: author, hint: t('filter.user'),
|
|
4446
|
+
options: [allOption].concat(authors.map((name) => ({ value: name, label: name }))),
|
|
4447
|
+
onOpenMenu: (event, options) => openFilterMenu(event, options, author, setAuthor),
|
|
4448
|
+
}),
|
|
4449
|
+
E(FilterSelect, {
|
|
4450
|
+
label: t('filter.date'), value: since, hint: t('filter.date'),
|
|
4451
|
+
options: [allOption,
|
|
4452
|
+
{ value: 'today', label: t('filter.today') },
|
|
4453
|
+
{ value: 'week', label: t('filter.week') },
|
|
4454
|
+
{ value: 'month', label: t('filter.month') },
|
|
4455
|
+
{ value: 'year', label: t('filter.year') }],
|
|
4456
|
+
onOpenMenu: (event, options) => openFilterMenu(event, options, since, setSince),
|
|
4457
|
+
}),
|
|
4403
4458
|
E('form', {
|
|
4404
4459
|
className: 'dig-filter-path',
|
|
4405
4460
|
onSubmit: (event) => { event.preventDefault(); applyPath(pathDraft.trim()) },
|
|
@@ -4992,7 +5047,7 @@ window.__ModuleLoader__.load({
|
|
|
4992
5047
|
})
|
|
4993
5048
|
: E(HistoryList, {
|
|
4994
5049
|
t: t, commits: commits, hasMore: hasMore, busy: busy, selectedHash: selectedHash,
|
|
4995
|
-
hideSearch: compact, pathFilter: pathFilter,
|
|
5050
|
+
hideSearch: compact, pathFilter: pathFilter, openMenuAt: openMenuAt,
|
|
4996
5051
|
onPathFilter: (value) => setPathFilter(value),
|
|
4997
5052
|
onSelect: (commit) => { void selectCommit(commit) },
|
|
4998
5053
|
onMenu: commitMenu,
|
|
@@ -5497,6 +5552,9 @@ window.__ModuleLoader__.load({
|
|
|
5497
5552
|
'.dig-filter-text{flex:1 1 90px;min-width:80px;width:auto}',
|
|
5498
5553
|
'.dig-filter-select{flex:none;max-width:118px;appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);font:inherit;font-weight:500;height:22px;padding:0 4px;cursor:pointer;text-overflow:ellipsis}',
|
|
5499
5554
|
'.dig-filter-select:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
|
|
5555
|
+
'.dig-filter-select{display:inline-flex;align-items:center;gap:3px}',
|
|
5556
|
+
'.dig-filter-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:96px}',
|
|
5557
|
+
'.dig-filter-on{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}',
|
|
5500
5558
|
'.dig-filter-path{flex:0 1 112px;min-width:70px;display:flex}',
|
|
5501
5559
|
'.dig-load-more{margin:6px auto;display:block;padding:3px 12px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer}',
|
|
5502
5560
|
'.dig-load-more:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
|