dsh-ide-git 0.18.1 → 0.18.3

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.18.1",
3
+ "version": "0.18.3",
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.1",
3
+ "version": "0.18.3",
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
@@ -9829,8 +9829,12 @@ window.__ModuleLoader__.load({
9829
9829
  }
9830
9830
  const handlePick = useCallback((event, entry, selKey) => {
9831
9831
  const ordered = visibleRef.current.map((leaf) => branchSelKeyOf(leaf.entry))
9832
- if (event.shiftKey === true && pickAnchor.current !== null) {
9833
- const from = ordered.indexOf(pickAnchor.current)
9832
+ if (event.shiftKey === true) {
9833
+ /* No anchor yet (fresh tree, nothing clicked): VS Code semantics —
9834
+ the range starts at the FIRST visible row, so a first-click Shift
9835
+ still multi-selects instead of silently degrading to single. */
9836
+ const anchorKey = pickAnchor.current !== null ? pickAnchor.current : ordered[0]
9837
+ const from = ordered.indexOf(anchorKey)
9834
9838
  const to = ordered.indexOf(selKey)
9835
9839
  if (from >= 0 && to >= 0) {
9836
9840
  const low = Math.min(from, to)
@@ -9992,9 +9996,11 @@ window.__ModuleLoader__.load({
9992
9996
  return E('div', {
9993
9997
  className: 'dig-row dig-row-file' + (props.selected === true ? ' dig-row-selected' : ''),
9994
9998
  title: item.origPath === undefined ? item.path : item.origPath + ' → ' + item.path,
9995
- /* The click is handled ONE level up (ChangesPanel): plain click still
9996
- opens the diff, Shift/Ctrl+click join or toggle the multi-select. */
9999
+ /* The click is handled ONE level up (ChangesPanel): plain click only
10000
+ selects, DOUBLE-click opens the diff (v0.18.3 — one interaction model
10001
+ across every list). */
9997
10002
  onClick: (event) => props.onRowClick(event, item, props.group),
10003
+ onDoubleClick: () => props.onDblClick(item, props.group),
9998
10004
  onContextMenu: (event) => { event.preventDefault(); props.onMenu(event, item, props.group) },
9999
10005
  },
10000
10006
  FileTypeGlyph({ path: item.path }),
@@ -10075,9 +10081,12 @@ window.__ModuleLoader__.load({
10075
10081
  of this handler (their memo compares item/group/t/selected, not the
10076
10082
  callback — same stale-callback trade-off as the stage buttons), so
10077
10083
  the selection must never be read from it. */
10078
- if (event.shiftKey === true && anchorRef !== undefined && anchorRef.current !== null) {
10084
+ if (event.shiftKey === true && anchorRef !== undefined) {
10079
10085
  const ordered = orderedRowKeys()
10080
- const from = ordered.indexOf(anchorRef.current)
10086
+ /* Anchor falls back to the FIRST row (VS Code semantics): a Shift
10087
+ click on a fresh list must multi-select, not degrade to single. */
10088
+ const anchorKey = anchorRef.current !== null ? anchorRef.current : ordered[0]
10089
+ const from = ordered.indexOf(anchorKey)
10081
10090
  const to = ordered.indexOf(key)
10082
10091
  if (from >= 0 && to >= 0) {
10083
10092
  const low = Math.min(from, to)
@@ -10101,8 +10110,10 @@ window.__ModuleLoader__.load({
10101
10110
  return
10102
10111
  }
10103
10112
  anchorRef.current = key
10104
- setSelection((previous) => previous.size === 0 ? previous : new Set())
10105
- props.onDiff(item, group)
10113
+ /* Plain click only SELECTS (single-replace, the branch tree's model):
10114
+ the diff moved to DOUBLE-click — a stray click mid-multi-select must
10115
+ not yank the pane away (v0.18.3). */
10116
+ setSelection((previous) => previous.size === 1 && previous.has(key) === true ? previous : new Set([key]))
10106
10117
  }, [setSelection, anchorRef, props.onDiff])
10107
10118
  /* ---- rubber-band selection over the windowed list: the fixed row
10108
10119
  heights + the prefix-sum offsets turn a pixel span back into the
@@ -10244,6 +10255,7 @@ window.__ModuleLoader__.load({
10244
10255
  key: key + ':' + item.path, item: item, group: key, t: t,
10245
10256
  selected: selection.has(key + ':' + item.path) === true,
10246
10257
  onRowClick: handleRowClick,
10258
+ onDblClick: props.onDiff,
10247
10259
  onStage: props.onStage, onUnstage: props.onUnstage, onDiscard: props.onDiscard, onDiff: props.onDiff, onMenu: props.onChangeMenu,
10248
10260
  })
10249
10261
  const folderOf = (item) => {
@@ -10431,7 +10443,6 @@ window.__ModuleLoader__.load({
10431
10443
  return E('div', { className: 'dig-changes' },
10432
10444
  aiConfirmDialog,
10433
10445
  head,
10434
- selectionBar,
10435
10446
  E('div', { className: 'dig-changes-body' },
10436
10447
  E('div', {
10437
10448
  className: 'dig-changes-list', ref: changesScrollRef,
@@ -10456,6 +10467,7 @@ window.__ModuleLoader__.load({
10456
10467
  winLast < changeItems.length ? E('div', { key: 'pad-bottom', style: { height: String(changeOffsets[changeItems.length] - changeOffsets[winLast]) + 'px' } }) : null,
10457
10468
  ]),
10458
10469
  composer),
10470
+ selectionBar,
10459
10471
  marquee === null ? null : E('div', {
10460
10472
  className: 'dig-marquee',
10461
10473
  style: { left: marquee.x + 'px', top: marquee.y + 'px', width: marquee.w + 'px', height: marquee.h + 'px' },
@@ -10584,8 +10596,10 @@ window.__ModuleLoader__.load({
10584
10596
  'data-lane': String(props.row.lane),
10585
10597
  className: 'dig-commit' + (props.selected === true ? ' dig-commit-selected' : ''),
10586
10598
  /* Modifier handling lives in HistoryList (it owns the rendered row
10587
- order the Shift-range walks); a plain click still opens the detail. */
10599
+ order the Shift-range walks); a plain click only selects and the
10600
+ DOUBLE-click opens the detail (v0.18.3, same model as the tree). */
10588
10601
  onClick: (event) => props.onRowClick(event, commit),
10602
+ onDoubleClick: () => props.onOpenDetail(commit),
10589
10603
  onContextMenu: (event) => { event.preventDefault(); props.onMenu(event, commit) },
10590
10604
  },
10591
10605
  E('span', { className: 'dig-commit-date', title: shortDate(commit.date) }, relativeTime(commit.date)),
@@ -10691,10 +10705,12 @@ window.__ModuleLoader__.load({
10691
10705
  const rows = useMemo(() => buildRows(filtered), [filtered])
10692
10706
  /* Multi-select gestures (issue #13): the rendered row order is what the
10693
10707
  Shift-range walks, and the range is computed over the FILTERED rows —
10694
- the same list the eye sees. Plain click keeps opening the detail and
10695
- collapses the multi-select, exactly like the other two lists. The
10696
- stale-callback risk is nil: rows change identity with the filters, and
10697
- every row re-renders whenever its commit reference changes. */
10708
+ the same list the eye sees. A plain click only SELECTS (single-replace,
10709
+ like the branch tree); it must NOT open the detail view — that killed
10710
+ any multi-select mid-flight and is now the DOUBLE-click's job (user
10711
+ request, v0.18.3). The stale-callback risk is nil: rows change identity
10712
+ with the filters, and every row re-renders whenever its commit
10713
+ reference changes. */
10698
10714
  const handleRowClick = (event, commit) => {
10699
10715
  const pickedHashes = props.pickedHashes
10700
10716
  const setPicks = props.onPicksChange
@@ -10704,8 +10720,11 @@ window.__ModuleLoader__.load({
10704
10720
  /* Functional updates only — memoised CommitRows may hold a stale
10705
10721
  closure of this handler (their memo compares commit/row/selected),
10706
10722
  so the picked set is never read from it. */
10707
- if (event.shiftKey === true && pickAnchor.current !== null) {
10708
- const from = ordered.indexOf(pickAnchor.current)
10723
+ if (event.shiftKey === true) {
10724
+ /* Anchor falls back to the FIRST row (VS Code semantics): a Shift
10725
+ click on a fresh list must multi-select, not degrade to single. */
10726
+ const anchorHash = pickAnchor.current !== null ? pickAnchor.current : ordered[0]
10727
+ const from = ordered.indexOf(anchorHash)
10709
10728
  if (from >= 0 && index >= 0) {
10710
10729
  const low = Math.min(from, index)
10711
10730
  const high = Math.max(from, index)
@@ -10847,7 +10866,7 @@ window.__ModuleLoader__.load({
10847
10866
  commit: row.commit, row: row, t: t,
10848
10867
  selected: props.selectedHash === row.commit.hash
10849
10868
  || (props.pickedHashes !== undefined && props.pickedHashes.has(row.commit.hash) === true),
10850
- onRowClick: handleRowClick, onMenu: props.onMenu, openMenuAt: props.openMenuAt,
10869
+ onRowClick: handleRowClick, onOpenDetail: props.onSelect, onMenu: props.onMenu, openMenuAt: props.openMenuAt,
10851
10870
  })),
10852
10871
  winEnd < rows.length ? E('div', { key: 'pad-bottom', style: { height: String((rows.length - winEnd) * HISTORY_ROW_H) + 'px' } }) : null,
10853
10872
  props.hasMore === true
@@ -10874,7 +10893,10 @@ window.__ModuleLoader__.load({
10874
10893
  key: file.path,
10875
10894
  className: 'dig-row dig-row-file' + (props.selectedPath === file.path ? ' dig-row-selected' : ''),
10876
10895
  title: file.path,
10896
+ /* Same click model as everywhere else (v0.18.3): plain click only
10897
+ selects, DOUBLE-click opens the diff. */
10877
10898
  onClick: () => props.onSelectFile(file),
10899
+ onDoubleClick: () => props.onOpenFile(file),
10878
10900
  onContextMenu: props.onFileMenu === undefined ? undefined : (event) => { event.preventDefault(); props.onFileMenu(event, file) },
10879
10901
  },
10880
10902
  FileTypeGlyph({ path: file.path }),
@@ -12231,7 +12253,10 @@ window.__ModuleLoader__.load({
12231
12253
  ? E(CommitDetail, {
12232
12254
  t: t, detail: detail, selectedPath: selectedPath,
12233
12255
  onBack: () => { setView('history'); setSelectedPath(null); setPatch(''); setDiffContext(null) },
12234
- onSelectFile: (file) => {
12256
+ /* Plain click only highlights the file; the DOUBLE-click opens its
12257
+ diff (v0.18.3, one click model across every list). */
12258
+ onSelectFile: (file) => { setSelectedPath(file.path) },
12259
+ onOpenFile: (file) => {
12235
12260
  setSelectedPath(file.path)
12236
12261
  showDiff({
12237
12262
  hash: detail === null || detail === undefined ? undefined : detail.hash,
@@ -13279,10 +13304,12 @@ window.__ModuleLoader__.load({
13279
13304
  '.dig-row-sub{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;max-width:45%;overflow:hidden;text-overflow:ellipsis}',
13280
13305
  '.dig-row-dir{margin-left:auto;opacity:.75}',
13281
13306
  '.dig-badge{font-size:10px;padding:0 5px;border-radius:999px;corner-shape:round;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);flex:none}',
13282
- /* Ahead/behind colours (user request): pending pushes read green, pending
13283
- pulls read blue — the same semantics in the tree rows and the top bar. */
13284
- '.dig-badge-ahead,.dig-track-ahead{color:var(--dsw-alias-state-success-primary)}',
13285
- '.dig-badge-behind,.dig-track-behind{color:var(--dsw-alias-state-business-primary,var(--dsw-alias-brand-primary))}',
13307
+ /* Ahead/behind colours + size (user request): pending pushes read green,
13308
+ pending pulls read blue — the same semantics in the tree rows and the
13309
+ top bar. 12px/600 keeps the counts legible at a glance. */
13310
+ '.dig-badge-ahead,.dig-track-ahead{color:var(--dsw-alias-state-success-primary);font-size:12px;font-weight:600}',
13311
+ '.dig-badge-behind,.dig-track-behind{color:var(--dsw-alias-state-business-primary,var(--dsw-alias-brand-primary));font-size:12px;font-weight:600}',
13312
+ '.dig-badge.dig-badge-ahead,.dig-badge.dig-badge-behind{padding:0 6px}',
13286
13313
  '.dig-badge-muted{opacity:.8}',
13287
13314
  '.dig-badge-worktree{padding:0 3px;display:inline-flex;align-items:center;height:14px}',
13288
13315
  '.dig-history{display:flex;flex-direction:column;min-height:0;flex:1}',
@@ -13319,7 +13346,11 @@ window.__ModuleLoader__.load({
13319
13346
  VIEWPORT-space .dig-changes (invariant 13: position:fixed would be
13320
13347
  captured by the workbench's contain:layout and flung off-panel). */
13321
13348
  '.dig-marquee{position:absolute;border:1px solid var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 14%,transparent);pointer-events:none;z-index:6}',
13322
- '.dig-selection-bar{display:flex;align-items:center;gap:12px;padding:3px 10px;font-size:11px;background:var(--dsw-alias-interactive-bg-hover);border-bottom:1px solid var(--dsw-alias-border-subtle,transparent)}',
13349
+ /* The selection bar FLOATS over the top of the list (absolute, solid
13350
+ surface): as an in-flow bar it shoved the whole list down one row on
13351
+ the first selection — the second click of every double-click then
13352
+ landed on the shifted group head and the dblclick died (v0.18.3). */
13353
+ '.dig-selection-bar{position:absolute;top:0;left:0;right:0;z-index:6;display:flex;align-items:center;gap:12px;padding:3px 10px;font-size:11px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));border-bottom:1px solid var(--dsw-alias-border-subtle,transparent);box-shadow:var(--dsw-elevation-prominent,0 4px 14px rgba(0,0,0,.3))}',
13323
13354
  '.dig-selection-bar .dig-link{margin-left:0}',
13324
13355
  '.dig-selection-bar .dig-link-danger{color:var(--dsw-alias-state-error-primary)}',
13325
13356
  '.dig-selection-count{color:var(--dsw-alias-label-secondary);white-space:nowrap}',