dsh-ide-git 0.21.1 → 0.21.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.21.1",
3
+ "version": "0.21.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.21.1",
3
+ "version": "0.21.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
@@ -173,7 +173,7 @@ window.__ModuleLoader__.load({
173
173
  'action.mergeIntoCurrent': '合并到当前分支',
174
174
  'action.rebaseCurrentOnto': '将当前分支变基到此',
175
175
  'action.compare': '与当前分支比较',
176
- 'action.update': '更新(Fetch)',
176
+ 'action.update': '拉取',
177
177
  'action.push': '推送',
178
178
  'action.newTag': '新建标签...',
179
179
  'action.cherryPick': '优选(Cherry-Pick)',
@@ -515,7 +515,7 @@ window.__ModuleLoader__.load({
515
515
  'action.mergeIntoCurrent': 'Merge into current branch',
516
516
  'action.rebaseCurrentOnto': 'Rebase current branch onto this',
517
517
  'action.compare': 'Compare with current branch',
518
- 'action.update': 'Update (fetch)',
518
+ 'action.update': 'Pull',
519
519
  'action.push': 'Push',
520
520
  'action.newTag': 'New tag...',
521
521
  'action.cherryPick': 'Cherry-pick',
@@ -865,7 +865,7 @@ window.__ModuleLoader__.load({
865
865
  'action.mergeIntoCurrent': '合併到當前分支',
866
866
  'action.rebaseCurrentOnto': '將當前分支變基到此',
867
867
  'action.compare': '同當前分支比較',
868
- 'action.update': '更新(抓取)',
868
+ 'action.update': '拉取',
869
869
  'action.push': '推送',
870
870
  'action.newTag': '新建標籤...',
871
871
  'action.cherryPick': '揀選',
@@ -1207,7 +1207,7 @@ window.__ModuleLoader__.load({
1207
1207
  'action.mergeIntoCurrent': '合併到目前分支',
1208
1208
  'action.rebaseCurrentOnto': '將目前分支變基到此',
1209
1209
  'action.compare': '與目前分支比較',
1210
- 'action.update': '更新(抓取)',
1210
+ 'action.update': '拉取',
1211
1211
  'action.push': '推送',
1212
1212
  'action.newTag': '新建標籤...',
1213
1213
  'action.cherryPick': '揀選',
@@ -1549,7 +1549,7 @@ window.__ModuleLoader__.load({
1549
1549
  'action.mergeIntoCurrent': '合併到當前分支',
1550
1550
  'action.rebaseCurrentOnto': '將當前分支變基到此',
1551
1551
  'action.compare': '同當前分支比較',
1552
- 'action.update': '更新(抓取)',
1552
+ 'action.update': '拉取',
1553
1553
  'action.push': '推送',
1554
1554
  'action.newTag': '新建標籤...',
1555
1555
  'action.cherryPick': '揀選',
@@ -1891,7 +1891,7 @@ window.__ModuleLoader__.load({
1891
1891
  'action.mergeIntoCurrent': '現在のブランチにマージ',
1892
1892
  'action.rebaseCurrentOnto': '現在のブランチをここにリベース',
1893
1893
  'action.compare': '現在のブランチと比較',
1894
- 'action.update': '更新(フェッチ)',
1894
+ 'action.update': 'プル',
1895
1895
  'action.push': 'プッシュ',
1896
1896
  'action.newTag': '新しいタグ...',
1897
1897
  'action.cherryPick': 'チェリーピック',
@@ -2233,7 +2233,7 @@ window.__ModuleLoader__.load({
2233
2233
  'action.mergeIntoCurrent': '현재 브랜치로 병합',
2234
2234
  'action.rebaseCurrentOnto': '현재 브랜치를 여기로 리베이스',
2235
2235
  'action.compare': '현재 브랜치와 비교',
2236
- 'action.update': '업데이트(페치)',
2236
+ 'action.update': '풀',
2237
2237
  'action.push': '푸시',
2238
2238
  'action.newTag': '새 태그...',
2239
2239
  'action.cherryPick': '체리픽',
@@ -2575,7 +2575,7 @@ window.__ModuleLoader__.load({
2575
2575
  'action.mergeIntoCurrent': 'In aktuellen Branch mergen',
2576
2576
  'action.rebaseCurrentOnto': 'Aktuellen Branch hierauf rebasen',
2577
2577
  'action.compare': 'Mit aktuellem Branch vergleichen',
2578
- 'action.update': 'Aktualisieren (fetch)',
2578
+ 'action.update': 'Pull',
2579
2579
  'action.push': 'Push',
2580
2580
  'action.newTag': 'Neuer Tag...',
2581
2581
  'action.cherryPick': 'Cherry-Pick',
@@ -2917,7 +2917,7 @@ window.__ModuleLoader__.load({
2917
2917
  'action.mergeIntoCurrent': 'Fusionner dans la branche courante',
2918
2918
  'action.rebaseCurrentOnto': 'Rebaser la branche courante sur celle-ci',
2919
2919
  'action.compare': 'Comparer avec la branche courante',
2920
- 'action.update': 'Mettre à jour (fetch)',
2920
+ 'action.update': 'Tirer',
2921
2921
  'action.push': 'Pousser',
2922
2922
  'action.newTag': 'Nouvelle étiquette...',
2923
2923
  'action.cherryPick': 'Cherry-pick',
@@ -3259,7 +3259,7 @@ window.__ModuleLoader__.load({
3259
3259
  'action.mergeIntoCurrent': 'Влить в текущую ветку',
3260
3260
  'action.rebaseCurrentOnto': 'Перебазировать текущую ветку сюда',
3261
3261
  'action.compare': 'Сравнить с текущей веткой',
3262
- 'action.update': 'Обновить (fetch)',
3262
+ 'action.update': 'Забрать',
3263
3263
  'action.push': 'Отправить',
3264
3264
  'action.newTag': 'Новый тег...',
3265
3265
  'action.cherryPick': 'Cherry-pick',
@@ -3601,7 +3601,7 @@ window.__ModuleLoader__.load({
3601
3601
  'action.mergeIntoCurrent': 'Mesclar na branch atual',
3602
3602
  'action.rebaseCurrentOnto': 'Rebasear a branch atual sobre esta',
3603
3603
  'action.compare': 'Comparar com a branch atual',
3604
- 'action.update': 'Atualizar (fetch)',
3604
+ 'action.update': 'Puxar',
3605
3605
  'action.push': 'Enviar',
3606
3606
  'action.newTag': 'Nova tag...',
3607
3607
  'action.cherryPick': 'Cherry-pick',
@@ -3943,7 +3943,7 @@ window.__ModuleLoader__.load({
3943
3943
  'action.mergeIntoCurrent': 'Unisci nel branch corrente',
3944
3944
  'action.rebaseCurrentOnto': 'Rebase del branch corrente su questo',
3945
3945
  'action.compare': 'Confronta con il branch corrente',
3946
- 'action.update': 'Aggiorna (fetch)',
3946
+ 'action.update': 'Pull',
3947
3947
  'action.push': 'Push',
3948
3948
  'action.newTag': 'Nuovo tag...',
3949
3949
  'action.cherryPick': 'Cherry-pick',
@@ -4285,7 +4285,7 @@ window.__ModuleLoader__.load({
4285
4285
  'action.mergeIntoCurrent': 'Samenvoegen in huidige branch',
4286
4286
  'action.rebaseCurrentOnto': 'Huidige branch rebasen op deze',
4287
4287
  'action.compare': 'Vergelijken met huidige branch',
4288
- 'action.update': 'Bijwerken (fetch)',
4288
+ 'action.update': 'Pull',
4289
4289
  'action.push': 'Pushen',
4290
4290
  'action.newTag': 'Nieuwe tag...',
4291
4291
  'action.cherryPick': 'Cherry-pick',
@@ -4627,7 +4627,7 @@ window.__ModuleLoader__.load({
4627
4627
  'action.mergeIntoCurrent': 'Scal z bieżącą gałęzią',
4628
4628
  'action.rebaseCurrentOnto': 'Zrebase’uj bieżącą gałąź na tę',
4629
4629
  'action.compare': 'Porównaj z bieżącą gałęzią',
4630
- 'action.update': 'Odśwież (fetch)',
4630
+ 'action.update': 'Ściągnij',
4631
4631
  'action.push': 'Wypchnij',
4632
4632
  'action.newTag': 'Nowy tag...',
4633
4633
  'action.cherryPick': 'Cherry-pick',
@@ -4969,7 +4969,7 @@ window.__ModuleLoader__.load({
4969
4969
  'action.mergeIntoCurrent': 'Sammanfoga in i aktuell gren',
4970
4970
  'action.rebaseCurrentOnto': 'Rebasa aktuell gren mot denna',
4971
4971
  'action.compare': 'Jämför med aktuell gren',
4972
- 'action.update': 'Uppdatera (fetch)',
4972
+ 'action.update': 'Pull',
4973
4973
  'action.push': 'Pusha',
4974
4974
  'action.newTag': 'Ny tagg...',
4975
4975
  'action.cherryPick': 'Cherry-pick',
@@ -5311,7 +5311,7 @@ window.__ModuleLoader__.load({
5311
5311
  'action.mergeIntoCurrent': 'Geçerli dala birleştir',
5312
5312
  'action.rebaseCurrentOnto': 'Geçerli dalı bunun üzerine rebase et',
5313
5313
  'action.compare': 'Geçerli dalla karşılaştır',
5314
- 'action.update': 'Güncelle (fetch)',
5314
+ 'action.update': 'Pull',
5315
5315
  'action.push': 'Gönder',
5316
5316
  'action.newTag': 'Yeni etiket...',
5317
5317
  'action.cherryPick': 'Cherry-pick',
@@ -5653,7 +5653,7 @@ window.__ModuleLoader__.load({
5653
5653
  'action.mergeIntoCurrent': 'Gabungkan ke branch saat ini',
5654
5654
  'action.rebaseCurrentOnto': 'Rebase branch saat ini ke ini',
5655
5655
  'action.compare': 'Bandingkan dengan branch saat ini',
5656
- 'action.update': 'Perbarui (fetch)',
5656
+ 'action.update': 'Pull',
5657
5657
  'action.push': 'Push',
5658
5658
  'action.newTag': 'Tag baru...',
5659
5659
  'action.cherryPick': 'Cherry-pick',
@@ -5995,7 +5995,7 @@ window.__ModuleLoader__.load({
5995
5995
  'action.mergeIntoCurrent': 'Hợp nhất vào nhánh hiện tại',
5996
5996
  'action.rebaseCurrentOnto': 'Rebase nhánh hiện tại lên nhánh này',
5997
5997
  'action.compare': 'So sánh với nhánh hiện tại',
5998
- 'action.update': 'Cập nhật (fetch)',
5998
+ 'action.update': 'Pull',
5999
5999
  'action.push': 'Push',
6000
6000
  'action.newTag': 'Thẻ mới...',
6001
6001
  'action.cherryPick': 'Cherry-pick',
@@ -6337,7 +6337,7 @@ window.__ModuleLoader__.load({
6337
6337
  'action.mergeIntoCurrent': 'الدمج في الفرع الحالي',
6338
6338
  'action.rebaseCurrentOnto': 'إعادة تأسيس الفرع الحالي على هذا',
6339
6339
  'action.compare': 'المقارنة مع الفرع الحالي',
6340
- 'action.update': 'تحديث (fetch)',
6340
+ 'action.update': 'سحب',
6341
6341
  'action.push': 'دفع',
6342
6342
  'action.newTag': 'وسم جديد...',
6343
6343
  'action.cherryPick': 'انتقاء',
@@ -6679,7 +6679,7 @@ window.__ModuleLoader__.load({
6679
6679
  'action.mergeIntoCurrent': 'वर्तमान शाखा में मर्ज करें',
6680
6680
  'action.rebaseCurrentOnto': 'वर्तमान शाखा को इस पर रीबेस करें',
6681
6681
  'action.compare': 'वर्तमान शाखा से तुलना करें',
6682
- 'action.update': 'अद्यतन करें (fetch)',
6682
+ 'action.update': 'पुल',
6683
6683
  'action.push': 'पुश',
6684
6684
  'action.newTag': 'नया टैग...',
6685
6685
  'action.cherryPick': 'चेरी-पिक',
@@ -7021,7 +7021,7 @@ window.__ModuleLoader__.load({
7021
7021
  'action.mergeIntoCurrent': 'รวมเข้าแบรนช์ปัจจุบัน',
7022
7022
  'action.rebaseCurrentOnto': 'รีเบสแบรนช์ปัจจุบันไปที่นี้',
7023
7023
  'action.compare': 'เปรียบเทียบกับแบรนช์ปัจจุบัน',
7024
- 'action.update': 'อัปเดต (fetch)',
7024
+ 'action.update': 'Pull',
7025
7025
  'action.push': 'พุช',
7026
7026
  'action.newTag': 'แท็กใหม่...',
7027
7027
  'action.cherryPick': 'เชอร์รีพิก',
@@ -9388,6 +9388,21 @@ window.__ModuleLoader__.load({
9388
9388
  try { window.localStorage.setItem(LINEDIFF_KEY, JSON.stringify(prefs)) } catch (error) { void error }
9389
9389
  }
9390
9390
 
9391
+ /* The changes view mode (flat vs by directory) is a habit too, not tab
9392
+ state: one localStorage home read on mount, so moving between
9393
+ repositories or reopening the panel keeps the view the user chose
9394
+ instead of snapping back to flat (v0.21.2). */
9395
+ const CHANGES_GROUP_KEY = 'dsh-ide-git.groupBy.v1'
9396
+ function readChangesGroupBy() {
9397
+ try {
9398
+ return window.localStorage.getItem(CHANGES_GROUP_KEY) === 'dir' ? 'dir' : 'flat'
9399
+ } catch (error) { void error }
9400
+ return 'flat'
9401
+ }
9402
+ function rememberChangesGroupBy(value) {
9403
+ try { window.localStorage.setItem(CHANGES_GROUP_KEY, value === 'dir' ? 'dir' : 'flat') } catch (error) { void error }
9404
+ }
9405
+
9391
9406
  /* The seed blocks the official review uses, resolved once. Every consumer
9392
9407
  null-checks: a host whose primitives predate an export keeps working
9393
9408
  through the hand-drawn fallbacks (labels instead of icons, a plain
@@ -10808,10 +10823,9 @@ window.__ModuleLoader__.load({
10808
10823
  },
10809
10824
  E('span', { className: 'dig-row-icon dig-tone-' + tone + (starred === true ? ' dig-star-filled' : '') }, E(Icon, { name: glyph, size: 12 })),
10810
10825
  E('span', { className: 'dig-row-label' }, props.label === undefined ? entry.name : props.label),
10811
- // Inline tracking target (issue #18): the title already said "name →
10812
- // upstream", but nobody hovers a tree to learn a binding. Local rows
10813
- // show it as muted small text; remote rows ARE the upstream.
10814
- kind !== 'remote' && kind !== 'tag' && entry.upstream !== null && entry.upstream !== undefined ? E('span', { className: 'dig-row-upstream' }, '→ ' + entry.upstream) : null,
10826
+ // The inline "→ upstream" target was removed (v0.21.2): it ate the
10827
+ // label's width so long branch names truncated early, and the binding
10828
+ // is still one hover away (the row title keeps "name → upstream").
10815
10829
  // A bare 'W' told nobody anything (reported right after the section counts):
10816
10830
  // it means this branch is checked out in ANOTHER working tree, which is why
10817
10831
  // git refuses to check it out here. An icon plus a spelled-out tooltip says so.
@@ -12728,7 +12742,14 @@ window.__ModuleLoader__.load({
12728
12742
  rail action, invariant 14) can drive the same groupBy and the sort
12729
12743
  order it introduces — one source of truth, wherever the click came
12730
12744
  from. sortKey 'git' keeps git's own status order. */
12731
- const [groupBy, setGroupBy] = useState('flat')
12745
+ /* Both the panel and the ⋯ menu write through this wrapper, so the
12746
+ choice survives a remount (the menu used to be the only writer that
12747
+ did not persist, and a repo/session switch silently reset it). */
12748
+ const [groupBy, setGroupByState] = useState(readChangesGroupBy)
12749
+ const setGroupBy = useCallback((value) => {
12750
+ setGroupByState(value)
12751
+ rememberChangesGroupBy(value)
12752
+ }, [])
12732
12753
  const [sortKey, setSortKey] = useState('git')
12733
12754
  /* The Git-output drawer (VS Code showOutput): null = closed, an array =
12734
12755
  the entries it currently shows. Opened on demand, refreshed by hand or
@@ -12964,9 +12985,15 @@ window.__ModuleLoader__.load({
12964
12985
 
12965
12986
  const run = useCallback(async (method, payload) => {
12966
12987
  const result = await guard(() => request(method, Object.assign({}, base, payload)))
12967
- if (result !== undefined) await refresh()
12988
+ if (result !== undefined) {
12989
+ await refresh()
12990
+ /* v0.21.2: the composer pill is a separate tree; without this it only
12991
+ learned about an in-panel checkout/commit at the next 30s poll. The
12992
+ panel's own watcher skips source 'panel' (refresh already ran). */
12993
+ if (repoRoot !== null) notifyBranchSwitched(sessionId, repoRoot, 'panel')
12994
+ }
12968
12995
  return result
12969
- }, [base, guard, refresh])
12996
+ }, [base, guard, refresh, sessionId, repoRoot])
12970
12997
 
12971
12998
  const pickRepo = useCallback((path) => {
12972
12999
  setRepoRoot(path)
@@ -12999,8 +13026,9 @@ window.__ModuleLoader__.load({
12999
13026
  if (sid !== sessionId || typeof root !== 'string' || root === '') return
13000
13027
  setRepoRoot((current) => (current === root ? current : pickRepo(root)))
13001
13028
  }
13002
- const onBranch = () => {
13003
- if (repoRoot === null) return
13029
+ const onBranch = (sid, root, source) => {
13030
+ if (sid !== sessionId || source === 'panel') return
13031
+ if (repoRoot === null || root !== repoRoot) return
13004
13032
  void refresh()
13005
13033
  }
13006
13034
  composerRepoWatchers.add(onRepo)
@@ -13451,8 +13479,15 @@ window.__ModuleLoader__.load({
13451
13479
  ].filter((item) => item !== null))
13452
13480
  return
13453
13481
  }
13482
+ const updateItem = { id: 'update', icon: 'fetch', tone: 'cyan', label: t('action.update'), run: () => { void run('fetch', { prune: true }) } }
13483
+ const pushItem = { id: 'push', icon: 'push', tone: 'success', label: t('action.push'), disabled: blocked, reason: t('operation.hint'), run: () => setDialog({ kind: 'push' }) }
13454
13484
  const items = [
13455
13485
  { id: 'checkout', icon: 'checkout', tone: 'accent', label: t('action.checkout'), disabled: entry.head === true || entry.tag === true || blocked, reason: blocked ? t('operation.hint') : reason, run: () => { void checkout(entry) } },
13486
+ // v0.21.2: on a LOCAL row the two high-frequency verbs sit right under
13487
+ // 签出 (2nd/3rd) instead of past 删除; remote/tag rows keep the old
13488
+ // tail placement (their menu is about the ref, not the working tree).
13489
+ isLocal ? updateItem : null,
13490
+ isLocal ? pushItem : null,
13456
13491
  isLocal ? { id: 'rebase', icon: 'compare', tone: 'violet', label: t('action.rebaseCurrentOnto'), disabled: entry.head === true || blocked, reason: blocked ? t('operation.hint') : reason, run: () => { void run('rebase', { onto: entry.name }) } } : null,
13457
13492
  isLocal ? { id: 'merge', icon: 'compare', tone: 'accent', label: t('action.mergeIntoCurrent'), disabled: entry.head === true || blocked, reason: blocked ? t('operation.hint') : reason, run: () => { void run('merge', { branch: entry.name }) } } : null,
13458
13493
  { id: 'compare', icon: 'filter', tone: 'violet', label: t('action.compare'), disabled: entry.head === true || current === '', reason: reason, run: () => { void compareWith(current, entry.name) } },
@@ -13494,8 +13529,8 @@ window.__ModuleLoader__.load({
13494
13529
  },
13495
13530
  }),
13496
13531
  null,
13497
- { id: 'update', icon: 'fetch', tone: 'cyan', label: t('action.update'), run: () => { void run('fetch', { prune: true }) } },
13498
- { id: 'push', icon: 'push', tone: 'success', label: t('action.push'), disabled: blocked, reason: t('operation.hint'), run: () => setDialog({ kind: 'push' }) },
13532
+ isLocal ? null : updateItem,
13533
+ isLocal ? null : pushItem,
13499
13534
  ].filter((item) => item !== null)
13500
13535
  openMenuAt(event, items)
13501
13536
  }, [branches, checkout, run, t, compareWith, toggleFavoriteBranch, openMenuAt, favorites, blocked, sendGitSummaryToChat, repoLabel, copyValues])
@@ -15012,7 +15047,15 @@ window.__ModuleLoader__.load({
15012
15047
  tone) via the host's body[data-ds-dark-theme] switch. Selection reads
15013
15048
  as gray on both themes, wallpaper/frost stays visible, never white. */
15014
15049
  '.dig-row-selected{background:color-mix(in srgb,var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover)) 58%,var(--dsw-static-neutral-bluish-75,var(--dsw-alias-bg-layer-3)) 42%);color:var(--dsw-alias-label-primary);box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary)}',
15015
- 'body[data-ds-dark-theme] .dig-row-selected{background:color-mix(in srgb,var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover)) 58%,var(--dsw-static-neutral-bluish-800,var(--dsw-alias-bg-layer-3)) 42%)}',
15050
+ /* v0.21.2: the checked-out row is amber; selecting it must not wash that
15051
+ out to primary — the selection is a frame, not a recolour. */
15052
+ '.dig-row-head.dig-row-selected{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary))}',
15053
+ /* v0.21.2 (user): on the dark theme the 58/42 charcoal mix still read as
15054
+ a bright frame over a wallpaper — the selection should be a BLACK frame
15055
+ that the wallpaper shows through, not a lit one. rgba(0,0,0,.55) is
15056
+ opaque enough to hold the amber/primary label, transparent enough to
15057
+ stay in the any-background story. The light theme keeps the host gray. */
15058
+ 'body[data-ds-dark-theme] .dig-row-selected{background:rgba(0,0,0,.55)}',
15016
15059
  '.dig-row-remote .dig-row-label{color:var(--dsw-alias-label-secondary);font-weight:500}',
15017
15060
  '.dig-row-tag .dig-row-label{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary));font-weight:500}',
15018
15061
  '.dig-row-head .dig-row-label{font-weight:600}',
@@ -15020,7 +15063,6 @@ window.__ModuleLoader__.load({
15020
15063
  '.dig-row-label{overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;font-weight:600}',
15021
15064
  // Inline tracking target (issue #18): flex:none so it never starves the
15022
15065
  // label, capped and ellipsised so a long origin/… cannot push the badges.
15023
- '.dig-row-upstream{flex:none;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary))}',
15024
15066
  '.dig-row-sub{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;max-width:45%;overflow:hidden;text-overflow:ellipsis}',
15025
15067
  '.dig-row-dir{margin-left:auto;opacity:.75}',
15026
15068
  '.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}',
@@ -15470,7 +15512,9 @@ window.__ModuleLoader__.load({
15470
15512
  right repository without any extra wiring. */
15471
15513
 
15472
15514
  const composerRepoWatchers = new Set() /* (sessionId, repoRoot) */
15473
- const composerBranchWatchers = new Set() /* (sessionId, repoRoot) */
15515
+ /* (sessionId, repoRoot, source) — source is 'composer' | 'panel' so the
15516
+ seat that performed the switch can skip its own echo (v0.21.2). */
15517
+ const composerBranchWatchers = new Set()
15474
15518
 
15475
15519
  function rememberRepoShared(sessionId, repoRoot) {
15476
15520
  rememberRepo(sessionId, repoRoot)
@@ -15479,9 +15523,9 @@ window.__ModuleLoader__.load({
15479
15523
  }
15480
15524
  }
15481
15525
 
15482
- function notifyBranchSwitched(sessionId, repoRoot) {
15526
+ function notifyBranchSwitched(sessionId, repoRoot, source) {
15483
15527
  for (const watcher of composerBranchWatchers) {
15484
- try { watcher(sessionId, repoRoot) } catch (error) { void error }
15528
+ try { watcher(sessionId, repoRoot, source) } catch (error) { void error }
15485
15529
  }
15486
15530
  }
15487
15531
 
@@ -15916,7 +15960,10 @@ window.__ModuleLoader__.load({
15916
15960
  /* A branch switched in the panel (or here) refreshes the summary now
15917
15961
  instead of at the next poll tick. */
15918
15962
  useEffect(() => {
15919
- const watcher = () => { setTick((value) => value + 1) }
15963
+ const watcher = (sid, root, source) => {
15964
+ if (sid !== sessionId || source === 'composer') return
15965
+ setTick((value) => value + 1)
15966
+ }
15920
15967
  composerBranchWatchers.add(watcher)
15921
15968
  return () => { composerBranchWatchers.delete(watcher) }
15922
15969
  }, [sessionId])
@@ -15994,7 +16041,7 @@ window.__ModuleLoader__.load({
15994
16041
  await request('checkout', { cwd: cwd, repoRoot: repoRoot, branch: name, confirm: true })
15995
16042
  setMenu(null)
15996
16043
  setTick((value) => value + 1)
15997
- notifyBranchSwitched(sessionId, repoRoot)
16044
+ notifyBranchSwitched(sessionId, repoRoot, 'composer')
15998
16045
  } catch (caught) {
15999
16046
  setMenu(null)
16000
16047
  setError(caught instanceof Error ? caught.message : String(caught))