dsh-ide-git 0.16.0 → 0.16.2

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.16.0",
3
+ "version": "0.16.2",
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.16.0",
3
+ "version": "0.16.2",
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
@@ -8243,7 +8243,7 @@ window.__ModuleLoader__.load({
8243
8243
  fetch — a BROKEN stem: the references come down, nothing is merged;
8244
8244
  pull — a solid stem pointing down: the remote lands here;
8245
8245
  push — a solid stem pointing up: the local goes out. */
8246
- fetch: ['M8 2v2', 'M8 5.6v2.2', 'M4.6 6.4 8 9.8l3.4-3.4', 'M3 13h10'],
8246
+ fetch: [{ d: 'M8 1.5v8.3', dash: '2.4 2.6' }, { d: 'M4.6 6.4 8 9.8l3.4-3.4' }, { d: 'M3 13h10', w: 2 }],
8247
8247
  pull: ['M8 2v7.8', 'M4.6 6.4 8 9.8l3.4-3.4', 'M3 13h10'],
8248
8248
  push: ['M8 9.8V2.2', 'M4.6 5.6 8 2.2l3.4 3.4', 'M3 13h10'],
8249
8249
  plus: ['M8 3v10', 'M3 8h10'],
@@ -8295,11 +8295,22 @@ window.__ModuleLoader__.load({
8295
8295
  const size = props.size === undefined ? 14 : props.size
8296
8296
  const shape = ICONS[props.name] === undefined ? ICONS.commit : ICONS[props.name]
8297
8297
  const weight = props.name === 'more' ? 2.6 : props.name === 'grip' ? 1.6 : 1.4
8298
+ /* A shape entry is either a plain `d` string or { d, w, dash } for
8299
+ per-path overrides — fetch's broken stem needs a real stroke-dasharray
8300
+ (hand-drawn gaps were being re-filled by the round line caps at 15px),
8301
+ and its ground rail carries a heavier stroke like the VS Code original. */
8298
8302
  return E('svg', {
8299
8303
  className: 'dig-icon', width: size, height: size, viewBox: '0 0 16 16',
8300
8304
  fill: 'none', stroke: 'currentColor', strokeWidth: weight,
8301
8305
  strokeLinecap: 'round', strokeLinejoin: 'round',
8302
- }, shape.map((d, index) => E('path', { key: index, d: d })))
8306
+ }, shape.map((entry, index) => {
8307
+ const path = typeof entry === 'string' ? { d: entry } : entry
8308
+ return E('path', {
8309
+ key: index, d: path.d,
8310
+ strokeWidth: path.w === undefined ? undefined : path.w,
8311
+ strokeDasharray: path.dash === undefined ? undefined : path.dash,
8312
+ })
8313
+ }))
8303
8314
  }
8304
8315
 
8305
8316
  /* Menus are laid out INSIDE the panel on purpose: dsh-better-sidebar's bottom
@@ -8314,9 +8325,9 @@ window.__ModuleLoader__.load({
8314
8325
  const cardRef = useRef(null)
8315
8326
  const closeRef = useRef(props.onClose)
8316
8327
  const [position, setPosition] = useState({ left: props.anchor.x, top: props.anchor.y })
8317
- /* The open submenu: { items, left, top } in wrapper-relative pixels, or
8318
- null. Hover-driven — moving onto a different parent row swaps or
8319
- closes it, so no timers are needed. */
8328
+ /* The open submenu: { items, top, flip } or null. Hover-driven — moving
8329
+ onto a different parent row swaps or closes it, so no timers are
8330
+ needed. Horizontal placement is pure CSS against the wrapper. */
8320
8331
  const [sub, setSub] = useState(null)
8321
8332
  useEffect(() => { closeRef.current = props.onClose })
8322
8333
  // Layout effect: the clamp below runs before the browser paints, so the menu
@@ -8358,10 +8369,11 @@ window.__ModuleLoader__.load({
8358
8369
  const cardRect = card.getBoundingClientRect()
8359
8370
  const itemRect = event.currentTarget.getBoundingClientRect()
8360
8371
  const bounds = props.boundsWidth === undefined ? 0 : props.boundsWidth
8361
- const SUB_W = 220
8362
- let left = cardRect.width + 2
8363
- if (bounds > 0 && position.left + left + SUB_W > bounds - 4) left = -SUB_W - 2
8364
- setSub({ items: item.items, left: left, top: Math.max(0, itemRect.top - cardRect.top - 4) })
8372
+ /* Direction only — the flush offset is CSS (`left:100%` / `right:100%`
8373
+ against the wrapper, whose width IS the parent card), so the seam is
8374
+ zero no matter how wide the submenu actually renders. */
8375
+ const flip = bounds > 0 && position.left + cardRect.width + 220 > bounds - 4
8376
+ setSub({ items: item.items, flip: flip, top: Math.max(0, itemRect.top - cardRect.top - 4) })
8365
8377
  }
8366
8378
  const renderItem = (item, index, inSub) => {
8367
8379
  if (item === null) return E('div', { key: 'sep-' + index, className: 'dig-menu-sep' })
@@ -8402,7 +8414,7 @@ window.__ModuleLoader__.load({
8402
8414
  E('div', { className: 'dig-menu', ref: cardRef, 'data-menu-material': 'translucent' },
8403
8415
  E('div', { className: 'dig-menu-scroll' },
8404
8416
  props.items.map((item, index) => renderItem(item, index, false)))),
8405
- sub === null ? null : E('div', { className: 'dig-menu dig-menu-sub', 'data-menu-material': 'translucent', style: { left: sub.left + 'px', top: sub.top + 'px' } },
8417
+ sub === null ? null : E('div', { className: 'dig-menu dig-menu-sub', 'data-menu-material': 'translucent', style: sub.flip === true ? { right: '100%', top: sub.top + 'px' } : { left: '100%', top: sub.top + 'px' } },
8406
8418
  E('div', { className: 'dig-menu-scroll' },
8407
8419
  sub.items.map((item, index) => renderItem(item, index, true)))))
8408
8420
  }
@@ -9440,7 +9452,11 @@ window.__ModuleLoader__.load({
9440
9452
  const groupBy = props.groupBy === 'dir' ? 'dir' : 'flat'
9441
9453
  const setGroupBy = props.onGroupBy
9442
9454
  const sortKey = props.sortKey === undefined ? 'git' : props.sortKey
9443
- const [foldedDirs, setFoldedDirs] = useState({})
9455
+ /* foldedDirs lifted too (v0.16.1): the header row lost its four buttons —
9456
+ everything they did lives in the ⋯ menu now, so the menu drives this
9457
+ state directly through props. */
9458
+ const foldedDirs = props.foldedDirs === undefined ? {} : props.foldedDirs
9459
+ const setFoldedDirs = props.onFoldedDirs
9444
9460
  const changes = summary === null ? null : summary.changes
9445
9461
  const conflicted = changes === null ? [] : changes.conflicted
9446
9462
  const staged = changes === null ? [] : changes.staged
@@ -9568,14 +9584,6 @@ window.__ModuleLoader__.load({
9568
9584
  let used = 0
9569
9585
  while (winLast < changeItems.length && used < winLimit) { used += changeItems[winLast].h; winLast += 1 }
9570
9586
  const visibleItems = changeItems.slice(winFirst, winLast)
9571
- const foldAll = (value) => {
9572
- const next = {}
9573
- if (value === true) {
9574
- const groups = [['conflicted', conflicted], ['staged', staged], ['unstaged', unstaged], ['untracked', untracked], ['ignored', ignored]]
9575
- for (const pair of groups) for (const item of pair[1]) next[pair[0] + '|' + folderOf(item)] = true
9576
- }
9577
- setFoldedDirs(next)
9578
- }
9579
9587
  const aiConfirmDialog = aiAsk !== true ? null : E(ConfirmDialog, {
9580
9588
  t: t,
9581
9589
  title: t('changes.aiOverwriteTitle'),
@@ -9654,35 +9662,18 @@ window.__ModuleLoader__.load({
9654
9662
  E('span', null, t('changes.title')),
9655
9663
  E('span', { className: 'dig-count' }, String(total))),
9656
9664
  E('span', { className: 'dig-topbar-spacer' }),
9657
- E('button', {
9658
- type: 'button',
9659
- className: 'dig-icon-btn dig-icon-btn-small' + (groupBy === 'dir' ? ' dig-icon-btn-active' : ''),
9660
- title: t('changes.groupBy') + ': ' + (groupBy === 'dir' ? t('changes.groupDir') : t('changes.groupFlat')),
9661
- onClick: () => setGroupBy((value) => (value === 'dir' ? 'flat' : 'dir')),
9662
- }, E(Icon, { name: 'folder', size: 13 })),
9663
- E('button', {
9664
- type: 'button', className: 'dig-icon-btn dig-icon-btn-small',
9665
- title: t('changes.expandAll'), disabled: groupBy !== 'dir',
9666
- onClick: () => foldAll(false),
9667
- }, E(Icon, { name: 'expand', size: 13 })),
9668
- E('button', {
9669
- type: 'button', className: 'dig-icon-btn dig-icon-btn-small',
9670
- title: t('changes.foldAll'), disabled: groupBy !== 'dir',
9671
- onClick: () => foldAll(true),
9672
- }, E(Icon, { name: 'collapse', size: 13 })),
9673
- E('button', {
9674
- type: 'button',
9675
- className: 'dig-icon-btn dig-icon-btn-small' + (props.showIgnored === true ? ' dig-icon-btn-active' : ''),
9676
- title: t('changes.showIgnored'),
9677
- onClick: () => props.onToggleIgnored(),
9678
- }, E(Icon, { name: props.showIgnored === true ? 'eye' : 'eyeOff', size: 13 })),
9665
+ /* The four header toggles (group-by / expand / fold / ignored) moved
9666
+ INTO the ⋯ menu (v0.16.1, user request): one consistent entry point
9667
+ instead of five small buttons, same actions, same state. */
9679
9668
  props.onMoreMenu === undefined ? null : E('button', {
9680
9669
  type: 'button',
9681
- className: 'dig-icon-btn dig-icon-btn-small',
9670
+ /* Same 24px footprint and 15px glyph as the rail buttons above — the
9671
+ user asked for one consistent size across both rows. */
9672
+ className: 'dig-icon-btn',
9682
9673
  title: t('toolbar.more'),
9683
9674
  'data-action': 'more',
9684
9675
  onClick: (event) => props.onMoreMenu(event),
9685
- }, E(Icon, { name: 'more', size: 13 })))
9676
+ }, E(Icon, { name: 'more', size: 16 })))
9686
9677
  return E('div', { className: 'dig-changes' },
9687
9678
  aiConfirmDialog,
9688
9679
  head,
@@ -10511,6 +10502,10 @@ window.__ModuleLoader__.load({
10511
10502
  fetches only when a remote exists. */
10512
10503
  const remoteReady = branches !== null && Array.isArray(branches.remotes) && branches.remotes.length > 0
10513
10504
  const [refreshSpinning, setRefreshSpinning] = useState(false)
10505
+ /* Fold state of the per-directory groups, lifted from ChangesPanel so the
10506
+ ⋯ menu's 展开全部/折叠全部 entries drive the same map the folder
10507
+ headers toggle. */
10508
+ const [foldedDirs, setFoldedDirs] = useState({})
10514
10509
  const refresh = useCallback(async (opts) => {
10515
10510
  if (repoRoot === null) return
10516
10511
  const withFetch = opts !== null && typeof opts === 'object' && opts.fetchRemote === true && remoteReady === true
@@ -11325,6 +11320,26 @@ window.__ModuleLoader__.load({
11325
11320
  { id: 'sort-path', label: t('more.sortPath'), active: sortKey === 'path', run: sort('path') },
11326
11321
  { id: 'sort-name', label: t('more.sortName'), active: sortKey === 'name', run: sort('name') },
11327
11322
  { id: 'sort-status', label: t('more.sortStatus'), active: sortKey === 'status', run: sort('status') },
11323
+ null,
11324
+ /* The old header toggles live here now (v0.16.1): fold/unfold need
11325
+ the directory map, which only exists when grouping is on. */
11326
+ { id: 'view-expand', label: t('changes.expandAll'), disabled: groupBy !== 'dir', reason, run: () => setFoldedDirs({}) },
11327
+ { id: 'view-fold', label: t('changes.foldAll'), disabled: groupBy !== 'dir', reason, run: () => {
11328
+ const next = {}
11329
+ const changes = summary === null ? null : summary.changes
11330
+ if (changes !== null) {
11331
+ const groups = [['conflicted', changes.conflicted], ['staged', changes.staged], ['unstaged', changes.unstaged], ['untracked', changes.untracked], ['ignored', changes.ignored === undefined ? [] : changes.ignored]]
11332
+ for (const pair of groups) {
11333
+ for (const item of pair[1]) {
11334
+ const dir = dirName(item.path)
11335
+ next[pair[0] + '|' + (dir === '' ? t('changes.rootDir') : dir)] = true
11336
+ }
11337
+ }
11338
+ }
11339
+ setFoldedDirs(next)
11340
+ } },
11341
+ null,
11342
+ { id: 'view-ignored', label: t('changes.showIgnored'), active: showIgnored === true, run: () => setShowIgnored(showIgnored !== true) },
11328
11343
  ] },
11329
11344
  null,
11330
11345
  /* —— top-level sync (VS Code header row) —— */
@@ -11423,6 +11438,7 @@ window.__ModuleLoader__.load({
11423
11438
  const changesPane = E(ChangesPanel, {
11424
11439
  t: t, summary: summary, busy: busy, compact: compact, hideHeader: compact,
11425
11440
  groupBy: groupBy, sortKey: sortKey, onGroupBy: setGroupBy,
11441
+ foldedDirs: foldedDirs, onFoldedDirs: setFoldedDirs,
11426
11442
  onMoreMenu: moreMenu,
11427
11443
  request: request, cwd: cwd, sessionId: sessionId, commitSettings: commitSettings, base: base,
11428
11444
  showIgnored: showIgnored,
@@ -11563,7 +11579,7 @@ window.__ModuleLoader__.load({
11563
11579
  title: action.disabled === true ? action.label + ' · ' + reason : action.label,
11564
11580
  disabled: action.disabled === true,
11565
11581
  onClick: action.run,
11566
- }, E(Icon, { name: action.icon, size: 15 })))
11582
+ }, E(Icon, { name: action.icon, size: 16 })))
11567
11583
  if (overflow === true) {
11568
11584
  children.push(E('button', {
11569
11585
  key: 'more',
@@ -11597,7 +11613,7 @@ window.__ModuleLoader__.load({
11597
11613
  if (target === 'page') { openSettingsPage(); return }
11598
11614
  setRailSettings(true)
11599
11615
  },
11600
- }, E(Icon, { name: 'settings', size: 15 })))
11616
+ }, E(Icon, { name: 'settings', size: 16 })))
11601
11617
  return E('div', { className: vertical ? 'dig-rail' : 'dig-rail-row', ref: railHostRef }, children)
11602
11618
  }
11603
11619
 
@@ -12242,13 +12258,15 @@ window.__ModuleLoader__.load({
12242
12258
  '.dig-dialog-text{color:var(--dsw-alias-label-secondary);white-space:pre-wrap}',
12243
12259
  '.dig-dialog-actions{display:flex;justify-content:flex-end;gap:8px}',
12244
12260
  '.dig-dialog-actions .dig-btn-primary{margin-left:0}',
12245
- /* The wrapper carries the panel-relative anchor and the outside-click ref;
12246
- the card(s) are plain children. The submenu is a second card offset to
12247
- the parent card's edge, flipping left when the panel runs out of room
12248
- (see ContextMenu.openSub). */
12249
- '.dig-menu-wrap{position:absolute;z-index:70}',
12250
- '.dig-menu{box-sizing:border-box;min-width:min(200px,calc(100% - 8px));max-width:calc(100% - 8px);max-height:calc(100% - 8px);padding:4px;border:0;border-radius:var(--dsw-radius-md,12px);background:transparent;box-shadow:var(--dsw-elevation-prominent,0 10px 28px rgba(0,0,0,.35));--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);isolation:isolate;display:flex;flex-direction:column}',
12251
- '.dig-menu-sub{position:absolute;min-width:180px;max-width:260px;z-index:71}',
12261
+ /* The wrapper carries the panel-relative anchor and the outside-click ref,
12262
+ plus the ONLY percentage widths (its containing block is .dig-root, so
12263
+ `max-width` resolves against the panel — on the card it would resolve
12264
+ against the shrink-to-fit wrapper and collapse the menu, cutting labels).
12265
+ The submenu is a second card offset to the parent card's edge, flipping
12266
+ left when the panel runs out of room (see ContextMenu.openSub). */
12267
+ '.dig-menu-wrap{position:absolute;z-index:70;width:max-content;max-width:calc(100% - 8px)}',
12268
+ '.dig-menu{box-sizing:border-box;min-width:200px;max-height:calc(100% - 8px);padding:4px;border:0;border-radius:var(--dsw-radius-md,12px);background:transparent;box-shadow:var(--dsw-elevation-prominent,0 10px 28px rgba(0,0,0,.35));--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);isolation:isolate;display:flex;flex-direction:column}',
12269
+ '.dig-menu-sub{position:absolute;min-width:180px;max-width:300px;z-index:71}',
12252
12270
  '.dig-menu::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1)));-webkit-backdrop-filter:var(--dsw-menu-backdrop-filter,blur(14px) saturate(1.2));backdrop-filter:var(--dsw-menu-backdrop-filter,blur(14px) saturate(1.2));pointer-events:none}',
12253
12271
  '.dig-menu-scroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;display:flex;flex-direction:column}',
12254
12272
  '.dig-menu-item{display:flex;align-items:center;gap:8px;padding:4px 8px;border:none;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;text-align:left;border-radius:var(--dsw-radius-sm,8px);cursor:pointer;white-space:nowrap;overflow:hidden}',