dsh-ide-git 0.7.2 → 0.8.0

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/src/client.js CHANGED
@@ -115,6 +115,11 @@ window.__ModuleLoader__.load({
115
115
  'action.showDiff': '显示差异',
116
116
  'action.copyPath': '复制路径',
117
117
  'action.checkout': '签出',
118
+ 'action.float': '浮动本页',
119
+ 'action.dock': '收回停靠',
120
+ 'action.split': '分栏',
121
+ 'action.fullscreen': '全屏',
122
+ 'action.exitFullscreen': '退出全屏',
118
123
  'action.newBranchFrom': '从该分支新建分支...',
119
124
  'action.newBranchHere': '在此新建分支...',
120
125
  'action.rename': '重命名...',
@@ -138,6 +143,8 @@ window.__ModuleLoader__.load({
138
143
  'detail.back': '返回历史',
139
144
  'detail.noFiles': '没有文件变更',
140
145
  'diff.empty': '没有可显示的文本差异',
146
+ 'pane.diff': '差异视图',
147
+ 'pane.resize': '拖动调整{name}尺寸,双击复位,方向键微调',
141
148
  'diff.binary': '二进制文件',
142
149
  'diff.loading': '正在加载差异...',
143
150
  'toolbar.refresh': '刷新',
@@ -269,6 +276,11 @@ window.__ModuleLoader__.load({
269
276
  'action.showDiff': 'Show diff',
270
277
  'action.copyPath': 'Copy path',
271
278
  'action.checkout': 'Checkout',
279
+ 'action.float': 'Float this page',
280
+ 'action.dock': 'Dock back',
281
+ 'action.split': 'Split pane',
282
+ 'action.fullscreen': 'Full screen',
283
+ 'action.exitFullscreen': 'Exit full screen',
272
284
  'action.newBranchFrom': 'New branch from here...',
273
285
  'action.newBranchHere': 'New branch here...',
274
286
  'action.rename': 'Rename...',
@@ -292,6 +304,8 @@ window.__ModuleLoader__.load({
292
304
  'detail.back': 'Back to history',
293
305
  'detail.noFiles': 'No file changes',
294
306
  'diff.empty': 'No textual diff to show',
307
+ 'pane.diff': 'diff view',
308
+ 'pane.resize': 'Drag to resize {name}: double-click resets, arrow keys nudge',
295
309
  'diff.binary': 'Binary file',
296
310
  'diff.loading': 'Loading diff...',
297
311
  'toolbar.refresh': 'Refresh',
@@ -431,6 +445,11 @@ window.__ModuleLoader__.load({
431
445
  'action.showDiff': '顯示差異',
432
446
  'action.copyPath': '複製路徑',
433
447
  'action.checkout': '簽出',
448
+ 'action.float': '浮動本頁',
449
+ 'action.dock': '收回停靠',
450
+ 'action.split': '分欄',
451
+ 'action.fullscreen': '全螢幕',
452
+ 'action.exitFullscreen': '結束全螢幕',
434
453
  'action.newBranchFrom': '由此新建分支...',
435
454
  'action.newBranchHere': '喺呢度新建分支...',
436
455
  'action.rename': '重新命名...',
@@ -454,6 +473,8 @@ window.__ModuleLoader__.load({
454
473
  'detail.back': '返回歷史',
455
474
  'detail.noFiles': '冇檔案變更',
456
475
  'diff.empty': '冇文字差異可以顯示',
476
+ 'pane.diff': '差異檢視',
477
+ 'pane.resize': '拖動調整{name}尺寸,雙擊復位,方向鍵微調',
457
478
  'diff.binary': '二進位檔案',
458
479
  'diff.loading': '正在載入差異...',
459
480
  'toolbar.refresh': '重新整理',
@@ -585,6 +606,11 @@ window.__ModuleLoader__.load({
585
606
  'action.showDiff': '顯示差異',
586
607
  'action.copyPath': '複製路徑',
587
608
  'action.checkout': '簽出',
609
+ 'action.float': '浮動本頁',
610
+ 'action.dock': '收回停靠',
611
+ 'action.split': '分割欄',
612
+ 'action.fullscreen': '全螢幕',
613
+ 'action.exitFullscreen': '結束全螢幕',
588
614
  'action.newBranchFrom': '由此新建分支...',
589
615
  'action.newBranchHere': '在此新建分支...',
590
616
  'action.rename': '重新命名...',
@@ -608,6 +634,8 @@ window.__ModuleLoader__.load({
608
634
  'detail.back': '返回歷史',
609
635
  'detail.noFiles': '沒有檔案變更',
610
636
  'diff.empty': '沒有文字差異可顯示',
637
+ 'pane.diff': '差異檢視',
638
+ 'pane.resize': '拖曳調整{name}尺寸,雙擊重設,方向鍵微調',
611
639
  'diff.binary': '二進位檔案',
612
640
  'diff.loading': '正在載入差異...',
613
641
  'toolbar.refresh': '重新整理',
@@ -739,6 +767,11 @@ window.__ModuleLoader__.load({
739
767
  'action.showDiff': '顯示差異',
740
768
  'action.copyPath': '複製路徑',
741
769
  'action.checkout': '簽出',
770
+ 'action.float': '浮動本頁',
771
+ 'action.dock': '收回停靠',
772
+ 'action.split': '分欄',
773
+ 'action.fullscreen': '全螢幕',
774
+ 'action.exitFullscreen': '結束全螢幕',
742
775
  'action.newBranchFrom': '由此新建分支...',
743
776
  'action.newBranchHere': '喺呢度新建分支...',
744
777
  'action.rename': '重新命名...',
@@ -762,6 +795,8 @@ window.__ModuleLoader__.load({
762
795
  'detail.back': '返回歷史',
763
796
  'detail.noFiles': '冇檔案變更',
764
797
  'diff.empty': '冇文字差異可以顯示',
798
+ 'pane.diff': '差異檢視',
799
+ 'pane.resize': '拖動調整{name}尺寸,雙擊復位,方向鍵微調',
765
800
  'diff.binary': '二進位檔案',
766
801
  'diff.loading': '正在載入差異...',
767
802
  'toolbar.refresh': '重新整理',
@@ -893,6 +928,11 @@ window.__ModuleLoader__.load({
893
928
  'action.showDiff': '差分を表示',
894
929
  'action.copyPath': 'パスをコピー',
895
930
  'action.checkout': 'チェックアウト',
931
+ 'action.float': 'このページを浮動表示',
932
+ 'action.dock': 'ドックに戻す',
933
+ 'action.split': '分割',
934
+ 'action.fullscreen': '全画面',
935
+ 'action.exitFullscreen': '全画面を終了',
896
936
  'action.newBranchFrom': 'ここから新しいブランチ...',
897
937
  'action.newBranchHere': 'ここに新しいブランチ...',
898
938
  'action.rename': '名前を変更...',
@@ -916,6 +956,8 @@ window.__ModuleLoader__.load({
916
956
  'detail.back': '履歴に戻る',
917
957
  'detail.noFiles': 'ファイルの変更はありません',
918
958
  'diff.empty': '表示できるテキスト差分がありません',
959
+ 'pane.diff': '差分ビュー',
960
+ 'pane.resize': '{name}のサイズをドラッグで調整(ダブルクリックでリセット、矢印キーで微調整)',
919
961
  'diff.binary': 'バイナリファイル',
920
962
  'diff.loading': '差分を読み込み中...',
921
963
  'toolbar.refresh': '更新',
@@ -1047,6 +1089,11 @@ window.__ModuleLoader__.load({
1047
1089
  'action.showDiff': '차이 보기',
1048
1090
  'action.copyPath': '경로 복사',
1049
1091
  'action.checkout': '체크아웃',
1092
+ 'action.float': '이 페이지 띄우기',
1093
+ 'action.dock': '도킹으로 복귀',
1094
+ 'action.split': '분할',
1095
+ 'action.fullscreen': '전체 화면',
1096
+ 'action.exitFullscreen': '전체 화면 종료',
1050
1097
  'action.newBranchFrom': '여기서 새 브랜치...',
1051
1098
  'action.newBranchHere': '여기에 새 브랜치...',
1052
1099
  'action.rename': '이름 바꾸기...',
@@ -1070,6 +1117,8 @@ window.__ModuleLoader__.load({
1070
1117
  'detail.back': '기록으로 돌아가기',
1071
1118
  'detail.noFiles': '파일 변경 없음',
1072
1119
  'diff.empty': '표시할 텍스트 차이가 없습니다',
1120
+ 'pane.diff': '차이 보기',
1121
+ 'pane.resize': '{name} 크기를 드래그해 조절(더블클릭 초기화, 화살표 키 미세 조정)',
1073
1122
  'diff.binary': '바이너리 파일',
1074
1123
  'diff.loading': '차이 불러오는 중...',
1075
1124
  'toolbar.refresh': '새로 고침',
@@ -1201,6 +1250,11 @@ window.__ModuleLoader__.load({
1201
1250
  'action.showDiff': 'Diff anzeigen',
1202
1251
  'action.copyPath': 'Pfad kopieren',
1203
1252
  'action.checkout': 'Auschecken',
1253
+ 'action.float': 'Diese Seite schweben lassen',
1254
+ 'action.dock': 'Zurück andocken',
1255
+ 'action.split': 'Teilen',
1256
+ 'action.fullscreen': 'Vollbild',
1257
+ 'action.exitFullscreen': 'Vollbild beenden',
1204
1258
  'action.newBranchFrom': 'Neuer Branch von hier...',
1205
1259
  'action.newBranchHere': 'Neuer Branch hier...',
1206
1260
  'action.rename': 'Umbenennen...',
@@ -1224,6 +1278,8 @@ window.__ModuleLoader__.load({
1224
1278
  'detail.back': 'Zurück zum Verlauf',
1225
1279
  'detail.noFiles': 'Keine Dateiänderungen',
1226
1280
  'diff.empty': 'Kein textueller Diff vorhanden',
1281
+ 'pane.diff': 'Diff-Ansicht',
1282
+ 'pane.resize': 'Größe von {name} per Ziehen ändern (Doppelklick setzt zurück, Pfeiltasten justieren)',
1227
1283
  'diff.binary': 'Binärdatei',
1228
1284
  'diff.loading': 'Diff wird geladen...',
1229
1285
  'toolbar.refresh': 'Aktualisieren',
@@ -1355,6 +1411,11 @@ window.__ModuleLoader__.load({
1355
1411
  'action.showDiff': 'Afficher le diff',
1356
1412
  'action.copyPath': 'Copier le chemin',
1357
1413
  'action.checkout': 'Extraire',
1414
+ 'action.float': 'Détacher cette page',
1415
+ 'action.dock': 'Réancrer',
1416
+ 'action.split': 'Fractionner',
1417
+ 'action.fullscreen': 'Plein écran',
1418
+ 'action.exitFullscreen': 'Quitter le plein écran',
1358
1419
  'action.newBranchFrom': 'Nouvelle branche depuis ici...',
1359
1420
  'action.newBranchHere': 'Nouvelle branche ici...',
1360
1421
  'action.rename': 'Renommer...',
@@ -1378,6 +1439,8 @@ window.__ModuleLoader__.load({
1378
1439
  'detail.back': 'Retour à l\'historique',
1379
1440
  'detail.noFiles': 'Aucune modification de fichier',
1380
1441
  'diff.empty': 'Aucun diff textuel à afficher',
1442
+ 'pane.diff': 'vue du diff',
1443
+ 'pane.resize': 'Glisser pour redimensionner {name} (double-clic pour réinitialiser, flèches pour ajuster)',
1381
1444
  'diff.binary': 'Fichier binaire',
1382
1445
  'diff.loading': 'Chargement du diff...',
1383
1446
  'toolbar.refresh': 'Rafraîchir',
@@ -1509,6 +1572,11 @@ window.__ModuleLoader__.load({
1509
1572
  'action.showDiff': 'Показать различия',
1510
1573
  'action.copyPath': 'Скопировать путь',
1511
1574
  'action.checkout': 'Переключиться',
1575
+ 'action.float': 'Открепить страницу',
1576
+ 'action.dock': 'Вернуть в док',
1577
+ 'action.split': 'Разделить',
1578
+ 'action.fullscreen': 'Полный экран',
1579
+ 'action.exitFullscreen': 'Выйти из полного экрана',
1512
1580
  'action.newBranchFrom': 'Новая ветка отсюда...',
1513
1581
  'action.newBranchHere': 'Новая ветка здесь...',
1514
1582
  'action.rename': 'Переименовать...',
@@ -1532,6 +1600,8 @@ window.__ModuleLoader__.load({
1532
1600
  'detail.back': 'Назад к истории',
1533
1601
  'detail.noFiles': 'Файлы не изменялись',
1534
1602
  'diff.empty': 'Нет текстовых различий для показа',
1603
+ 'pane.diff': 'просмотр diff',
1604
+ 'pane.resize': 'Перетащите, чтобы изменить размер {name} (двойной клик — сброс, стрелки — точная настройка)',
1535
1605
  'diff.binary': 'Двоичный файл',
1536
1606
  'diff.loading': 'Загрузка различий...',
1537
1607
  'toolbar.refresh': 'Обновить',
@@ -1663,6 +1733,11 @@ window.__ModuleLoader__.load({
1663
1733
  'action.showDiff': 'Mostrar diff',
1664
1734
  'action.copyPath': 'Copiar caminho',
1665
1735
  'action.checkout': 'Checkout',
1736
+ 'action.float': 'Flutuar esta página',
1737
+ 'action.dock': 'Reacoplar',
1738
+ 'action.split': 'Dividir',
1739
+ 'action.fullscreen': 'Tela cheia',
1740
+ 'action.exitFullscreen': 'Sair da tela cheia',
1666
1741
  'action.newBranchFrom': 'Nova branch a partir daqui...',
1667
1742
  'action.newBranchHere': 'Nova branch aqui...',
1668
1743
  'action.rename': 'Renomear...',
@@ -1686,6 +1761,8 @@ window.__ModuleLoader__.load({
1686
1761
  'detail.back': 'Voltar ao histórico',
1687
1762
  'detail.noFiles': 'Nenhuma alteração de arquivo',
1688
1763
  'diff.empty': 'Nenhum diff textual para mostrar',
1764
+ 'pane.diff': 'visualização do diff',
1765
+ 'pane.resize': 'Arraste para redimensionar {name} (duplo clique restaura, setas ajustam)',
1689
1766
  'diff.binary': 'Arquivo binário',
1690
1767
  'diff.loading': 'Carregando diff...',
1691
1768
  'toolbar.refresh': 'Atualizar',
@@ -1817,6 +1894,11 @@ window.__ModuleLoader__.load({
1817
1894
  'action.showDiff': 'Mostra il diff',
1818
1895
  'action.copyPath': 'Copia percorso',
1819
1896
  'action.checkout': 'Checkout',
1897
+ 'action.float': 'Fluttua questa pagina',
1898
+ 'action.dock': 'Riaggancia',
1899
+ 'action.split': 'Dividi',
1900
+ 'action.fullscreen': 'Schermo intero',
1901
+ 'action.exitFullscreen': 'Esci da schermo intero',
1820
1902
  'action.newBranchFrom': 'Nuovo branch da qui...',
1821
1903
  'action.newBranchHere': 'Nuovo branch qui...',
1822
1904
  'action.rename': 'Rinomina...',
@@ -1840,6 +1922,8 @@ window.__ModuleLoader__.load({
1840
1922
  'detail.back': 'Torna alla cronologia',
1841
1923
  'detail.noFiles': 'Nessuna modifica ai file',
1842
1924
  'diff.empty': 'Nessun diff testuale da mostrare',
1925
+ 'pane.diff': 'vista diff',
1926
+ 'pane.resize': 'Trascina per ridimensionare {name} (doppio clic ripristina, frecce per regolare)',
1843
1927
  'diff.binary': 'File binario',
1844
1928
  'diff.loading': 'Caricamento del diff...',
1845
1929
  'toolbar.refresh': 'Aggiorna',
@@ -1971,6 +2055,11 @@ window.__ModuleLoader__.load({
1971
2055
  'action.showDiff': 'Diff tonen',
1972
2056
  'action.copyPath': 'Pad kopiëren',
1973
2057
  'action.checkout': 'Uitchecken',
2058
+ 'action.float': 'Deze pagina losmaken',
2059
+ 'action.dock': 'Terugdokken',
2060
+ 'action.split': 'Splitsen',
2061
+ 'action.fullscreen': 'Volledig scherm',
2062
+ 'action.exitFullscreen': 'Volledig scherm sluiten',
1974
2063
  'action.newBranchFrom': 'Nieuwe branch vanaf hier...',
1975
2064
  'action.newBranchHere': 'Nieuwe branch hier...',
1976
2065
  'action.rename': 'Hernoemen...',
@@ -1994,6 +2083,8 @@ window.__ModuleLoader__.load({
1994
2083
  'detail.back': 'Terug naar geschiedenis',
1995
2084
  'detail.noFiles': 'Geen bestandswijzigingen',
1996
2085
  'diff.empty': 'Geen tekstuele diff om te tonen',
2086
+ 'pane.diff': 'diff-weergave',
2087
+ 'pane.resize': 'Sleep om {name} te vergroten (dubbelklik herstelt, pijltjes stellen fijn af)',
1997
2088
  'diff.binary': 'Binair bestand',
1998
2089
  'diff.loading': 'Diff laden...',
1999
2090
  'toolbar.refresh': 'Vernieuwen',
@@ -2125,6 +2216,11 @@ window.__ModuleLoader__.load({
2125
2216
  'action.showDiff': 'Pokaż różnice',
2126
2217
  'action.copyPath': 'Kopiuj ścieżkę',
2127
2218
  'action.checkout': 'Przełącz',
2219
+ 'action.float': 'Odczep tę stronę',
2220
+ 'action.dock': 'Zadokuj z powrotem',
2221
+ 'action.split': 'Podziel',
2222
+ 'action.fullscreen': 'Pełny ekran',
2223
+ 'action.exitFullscreen': 'Wyjdź z pełnego ekranu',
2128
2224
  'action.newBranchFrom': 'Nowa gałąź stąd...',
2129
2225
  'action.newBranchHere': 'Nowa gałąź tutaj...',
2130
2226
  'action.rename': 'Zmień nazwę...',
@@ -2148,6 +2244,8 @@ window.__ModuleLoader__.load({
2148
2244
  'detail.back': 'Powrót do historii',
2149
2245
  'detail.noFiles': 'Brak zmian w plikach',
2150
2246
  'diff.empty': 'Brak różnic tekstowych do pokazania',
2247
+ 'pane.diff': 'widok diff',
2248
+ 'pane.resize': 'Przeciągnij, aby zmienić rozmiar {name} (podwójne kliknięcie przywraca, strzałki korygują)',
2151
2249
  'diff.binary': 'Plik binarny',
2152
2250
  'diff.loading': 'Wczytywanie różnic...',
2153
2251
  'toolbar.refresh': 'Odśwież',
@@ -2279,6 +2377,11 @@ window.__ModuleLoader__.load({
2279
2377
  'action.showDiff': 'Visa diff',
2280
2378
  'action.copyPath': 'Kopiera sökväg',
2281
2379
  'action.checkout': 'Checka ut',
2380
+ 'action.float': 'Friställ sidan',
2381
+ 'action.dock': 'Docka tillbaka',
2382
+ 'action.split': 'Dela',
2383
+ 'action.fullscreen': 'Helskärm',
2384
+ 'action.exitFullscreen': 'Avsluta helskärm',
2282
2385
  'action.newBranchFrom': 'Ny gren härifrån...',
2283
2386
  'action.newBranchHere': 'Ny gren här...',
2284
2387
  'action.rename': 'Byt namn...',
@@ -2302,6 +2405,8 @@ window.__ModuleLoader__.load({
2302
2405
  'detail.back': 'Tillbaka till historiken',
2303
2406
  'detail.noFiles': 'Inga filändringar',
2304
2407
  'diff.empty': 'Ingen textdiff att visa',
2408
+ 'pane.diff': 'diff-vy',
2409
+ 'pane.resize': 'Dra för att ändra storleken på {name} (dubbelklick återställer, piltangenter finjusterar)',
2305
2410
  'diff.binary': 'Binär fil',
2306
2411
  'diff.loading': 'Laddar diff...',
2307
2412
  'toolbar.refresh': 'Uppdatera',
@@ -2433,6 +2538,11 @@ window.__ModuleLoader__.load({
2433
2538
  'action.showDiff': 'Farkı göster',
2434
2539
  'action.copyPath': 'Yolu kopyala',
2435
2540
  'action.checkout': 'Çıkar',
2541
+ 'action.float': 'Bu sayfayı yüzdür',
2542
+ 'action.dock': 'Yuvaya geri al',
2543
+ 'action.split': 'Böl',
2544
+ 'action.fullscreen': 'Tam ekran',
2545
+ 'action.exitFullscreen': 'Tam ekrandan çık',
2436
2546
  'action.newBranchFrom': 'Buradan yeni dal...',
2437
2547
  'action.newBranchHere': 'Burada yeni dal...',
2438
2548
  'action.rename': 'Yeniden adlandır...',
@@ -2456,6 +2566,8 @@ window.__ModuleLoader__.load({
2456
2566
  'detail.back': 'Geçmişe dön',
2457
2567
  'detail.noFiles': 'Dosya değişikliği yok',
2458
2568
  'diff.empty': 'Gösterilecek metin farkı yok',
2569
+ 'pane.diff': 'diff görünümü',
2570
+ 'pane.resize': '{name} boyutunu sürükleyerek ayarlayın (çift tıklama sıfırlar, ok tuşları ince ayar yapar)',
2459
2571
  'diff.binary': 'İkili dosya',
2460
2572
  'diff.loading': 'Fark yükleniyor...',
2461
2573
  'toolbar.refresh': 'Yenile',
@@ -2587,6 +2699,11 @@ window.__ModuleLoader__.load({
2587
2699
  'action.showDiff': 'Tampilkan diff',
2588
2700
  'action.copyPath': 'Salin jalur',
2589
2701
  'action.checkout': 'Checkout',
2702
+ 'action.float': 'Apungkan halaman ini',
2703
+ 'action.dock': 'Kembalikan ke dok',
2704
+ 'action.split': 'Bagi',
2705
+ 'action.fullscreen': 'Layar penuh',
2706
+ 'action.exitFullscreen': 'Keluar dari layar penuh',
2590
2707
  'action.newBranchFrom': 'Branch baru dari sini...',
2591
2708
  'action.newBranchHere': 'Branch baru di sini...',
2592
2709
  'action.rename': 'Ganti nama...',
@@ -2610,6 +2727,8 @@ window.__ModuleLoader__.load({
2610
2727
  'detail.back': 'Kembali ke riwayat',
2611
2728
  'detail.noFiles': 'Tidak ada perubahan berkas',
2612
2729
  'diff.empty': 'Tidak ada diff teks untuk ditampilkan',
2730
+ 'pane.diff': 'tampilan diff',
2731
+ 'pane.resize': 'Seret untuk mengubah ukuran {name} (klik ganda mengatur ulang, tombol panah menyesuaikan)',
2613
2732
  'diff.binary': 'Berkas biner',
2614
2733
  'diff.loading': 'Memuat diff...',
2615
2734
  'toolbar.refresh': 'Segarkan',
@@ -2741,6 +2860,11 @@ window.__ModuleLoader__.load({
2741
2860
  'action.showDiff': 'Xem diff',
2742
2861
  'action.copyPath': 'Sao chép đường dẫn',
2743
2862
  'action.checkout': 'Checkout',
2863
+ 'action.float': 'Tách trang này',
2864
+ 'action.dock': 'Ghép lại vào dock',
2865
+ 'action.split': 'Chia',
2866
+ 'action.fullscreen': 'Toàn màn hình',
2867
+ 'action.exitFullscreen': 'Thoát toàn màn hình',
2744
2868
  'action.newBranchFrom': 'Nhánh mới từ đây...',
2745
2869
  'action.newBranchHere': 'Nhánh mới tại đây...',
2746
2870
  'action.rename': 'Đổi tên...',
@@ -2764,6 +2888,8 @@ window.__ModuleLoader__.load({
2764
2888
  'detail.back': 'Quay lại lịch sử',
2765
2889
  'detail.noFiles': 'Không có thay đổi tệp',
2766
2890
  'diff.empty': 'Không có diff văn bản để hiển thị',
2891
+ 'pane.diff': 'chế độ xem diff',
2892
+ 'pane.resize': 'Kéo để đổi kích thước {name} (nhấp đúp để đặt lại, phím mũi tên để tinh chỉnh)',
2767
2893
  'diff.binary': 'Tệp nhị phân',
2768
2894
  'diff.loading': 'Đang tải diff...',
2769
2895
  'toolbar.refresh': 'Làm mới',
@@ -2895,6 +3021,11 @@ window.__ModuleLoader__.load({
2895
3021
  'action.showDiff': 'عرض الفروق',
2896
3022
  'action.copyPath': 'نسخ المسار',
2897
3023
  'action.checkout': 'تبديل',
3024
+ 'action.float': 'تعويم هذه الصفحة',
3025
+ 'action.dock': 'الإرجاع إلى الرصيف',
3026
+ 'action.split': 'تقسيم',
3027
+ 'action.fullscreen': 'ملء الشاشة',
3028
+ 'action.exitFullscreen': 'إنهاء ملء الشاشة',
2898
3029
  'action.newBranchFrom': 'فرع جديد من هنا...',
2899
3030
  'action.newBranchHere': 'فرع جديد هنا...',
2900
3031
  'action.rename': 'إعادة التسمية...',
@@ -2918,6 +3049,8 @@ window.__ModuleLoader__.load({
2918
3049
  'detail.back': 'رجوع إلى السجل',
2919
3050
  'detail.noFiles': 'لا توجد تغييرات في الملفات',
2920
3051
  'diff.empty': 'لا توجد فروق نصية لعرضها',
3052
+ 'pane.diff': 'عرض الفروق',
3053
+ 'pane.resize': 'اسحب لتغيير حجم {name} (النقر المزدوج يعيد الضبط، ومفاتيح الأسهم للضبط الدقيق)',
2921
3054
  'diff.binary': 'ملف ثنائي',
2922
3055
  'diff.loading': 'جارٍ تحميل الفروق...',
2923
3056
  'toolbar.refresh': 'تحديث',
@@ -3049,6 +3182,11 @@ window.__ModuleLoader__.load({
3049
3182
  'action.showDiff': 'अंतर दिखाएँ',
3050
3183
  'action.copyPath': 'पथ कॉपी करें',
3051
3184
  'action.checkout': 'चेकआउट',
3185
+ 'action.float': 'इस पृष्ठ को तैराएँ',
3186
+ 'action.dock': 'डॉक पर वापस लाएँ',
3187
+ 'action.split': 'विभाजित करें',
3188
+ 'action.fullscreen': 'पूर्ण स्क्रीन',
3189
+ 'action.exitFullscreen': 'पूर्ण स्क्रीन से बाहर',
3052
3190
  'action.newBranchFrom': 'यहाँ से नई शाखा...',
3053
3191
  'action.newBranchHere': 'यहाँ नई शाखा...',
3054
3192
  'action.rename': 'नाम बदलें...',
@@ -3072,6 +3210,8 @@ window.__ModuleLoader__.load({
3072
3210
  'detail.back': 'इतिहास पर वापस',
3073
3211
  'detail.noFiles': 'कोई फ़ाइल परिवर्तन नहीं',
3074
3212
  'diff.empty': 'दिखाने के लिए कोई टेक्स्ट अंतर नहीं',
3213
+ 'pane.diff': 'डिफ़ व्यू',
3214
+ 'pane.resize': '{name} का आकार बदलने के लिए खींचें (डबल-क्लिक रीसेट, तीर कुंजियाँ सूक्ष्म समायोजन)',
3075
3215
  'diff.binary': 'बाइनरी फ़ाइल',
3076
3216
  'diff.loading': 'अंतर लोड हो रहा है...',
3077
3217
  'toolbar.refresh': 'ताज़ा करें',
@@ -3203,6 +3343,11 @@ window.__ModuleLoader__.load({
3203
3343
  'action.showDiff': 'แสดงความต่าง',
3204
3344
  'action.copyPath': 'คัดลอกพาธ',
3205
3345
  'action.checkout': 'เช็กเอาต์',
3346
+ 'action.float': 'ลอยหน้านี้',
3347
+ 'action.dock': 'กลับเข้าด็อก',
3348
+ 'action.split': 'แบ่งหน้าต่าง',
3349
+ 'action.fullscreen': 'เต็มหน้าจอ',
3350
+ 'action.exitFullscreen': 'ออกจากเต็มหน้าจอ',
3206
3351
  'action.newBranchFrom': 'แบรนช์ใหม่จากที่นี่...',
3207
3352
  'action.newBranchHere': 'แบรนช์ใหม่ที่นี่...',
3208
3353
  'action.rename': 'เปลี่ยนชื่อ...',
@@ -3226,6 +3371,8 @@ window.__ModuleLoader__.load({
3226
3371
  'detail.back': 'กลับไปที่ประวัติ',
3227
3372
  'detail.noFiles': 'ไม่มีการเปลี่ยนแปลงไฟล์',
3228
3373
  'diff.empty': 'ไม่มีความต่างเชิงข้อความให้แสดง',
3374
+ 'pane.diff': 'มุมมอง diff',
3375
+ 'pane.resize': 'ลากเพื่อปรับขนาด {name} (ดับเบิลคลิกเพื่อรีเซ็ต ปุ่มลูกศรปรับละเอียด)',
3229
3376
  'diff.binary': 'ไฟล์ไบนารี',
3230
3377
  'diff.loading': 'กำลังโหลดความต่าง...',
3231
3378
  'toolbar.refresh': 'รีเฟรช',
@@ -3448,6 +3595,16 @@ window.__ModuleLoader__.load({
3448
3595
  so a version that adds an action never breaks a stored config. */
3449
3596
  const RAIL_SPECS = [
3450
3597
  { id: 'refresh', icon: 'refresh', key: 'toolbar.refresh', tone: 'accent' },
3598
+ /* View actions. The tree toggle lives here as well as in the compact header
3599
+ because a persisted treeOpen=false would otherwise have no way back:
3600
+ compact is the only chrome with a header button, and it used to render
3601
+ that button only while the tree was OPEN. */
3602
+ { id: 'tree', icon: 'branch', key: 'toolbar.tree', tone: 'secondary' },
3603
+ /* The official right-sidebar dock (float / re-dock / split / full screen).
3604
+ They only render where that service exists — see dockFaceOf(). */
3605
+ { id: 'float', icon: 'float', key: 'action.float', tone: 'accent' },
3606
+ { id: 'split', icon: 'split', key: 'action.split', tone: 'secondary' },
3607
+ { id: 'fullscreen', icon: 'fullscreen', key: 'action.fullscreen', tone: 'secondary' },
3451
3608
  { id: 'newBranch', icon: 'plus', key: 'toolbar.newBranch', tone: 'success' },
3452
3609
  { id: 'checkout', icon: 'checkout', key: 'action.checkout', tone: 'accent' },
3453
3610
  { id: 'delete', icon: 'trash', key: 'action.delete', tone: 'danger' },
@@ -3461,6 +3618,10 @@ window.__ModuleLoader__.load({
3461
3618
  { id: 'push', icon: 'push', key: 'toolbar.push', tone: 'success' },
3462
3619
  ]
3463
3620
  const RAIL_IDS = RAIL_SPECS.map((spec) => spec.id)
3621
+ /* Actions that arrange the view rather than touch the repository, so a running
3622
+ git command never greys them out. */
3623
+ const RAIL_VIEW_IDS = ['tree', 'float', 'split', 'fullscreen']
3624
+ const RAIL_DOCK_IDS = ['float', 'split', 'fullscreen']
3464
3625
 
3465
3626
  function normalizeRail(raw) {
3466
3627
  const source = raw !== null && typeof raw === 'object' ? raw : {}
@@ -3491,6 +3652,268 @@ window.__ModuleLoader__.load({
3491
3652
  try { window.localStorage.setItem(RAIL_KEY, JSON.stringify(config)) } catch (error) { void error }
3492
3653
  }
3493
3654
 
3655
+ /* ============================== pane sizes ============================== */
3656
+
3657
+ /* ---- pane sizing core (pure: tests/smoke.mjs slices this block and drives
3658
+ it through new Function, so it must stay free of window/DOM/React) ---- */
3659
+
3660
+ /* Every fixed pane size in the layout became draggable (issue #5), and the
3661
+ result is remembered PER SURFACE. The same registration renders in the
3662
+ bottom workbench (wide and flat) and in the native right sidebar (narrow
3663
+ and tall), and a 290px changes column that is right in one is half the
3664
+ panel in the other. The bucket is therefore `chrome:wide|tall`, and what
3665
+ is stored is a RATIO of the measured container: resizing the dock or the
3666
+ window rescales the panes instead of breaking the layout. */
3667
+ const PANES_KEY = 'dsh-ide-git.panes.v1'
3668
+ const PANE_CHROME_KEYS = {
3669
+ columns: ['tree', 'changes'],
3670
+ stack: ['tree', 'changes', 'diff'],
3671
+ compact: ['tree', 'diff'],
3672
+ }
3673
+ /* Whether a key measures a height (stacked chromes) or a width (columns). */
3674
+ const PANE_HEIGHT_CHROME = { columns: false, stack: true, compact: true }
3675
+ /* The pane a separator resizes, and the key its label is translated from. */
3676
+ const PANE_NAME_KEYS = { tree: 'toolbar.tree', changes: 'changes.title', diff: 'pane.diff' }
3677
+ /* Hard limits in px: a gutter moves a pane between these and nothing else,
3678
+ so no pane can ever be dragged down to zero. */
3679
+ const PANE_LIMITS = {
3680
+ 'columns:tree': { min: 140, max: 560 },
3681
+ 'columns:changes': { min: 200, max: 640 },
3682
+ 'stack:tree': { min: 100, max: 420 },
3683
+ 'stack:changes': { min: 120, max: 460 },
3684
+ 'stack:diff': { min: 120, max: 900 },
3685
+ 'compact:tree': { min: 100, max: 420 },
3686
+ 'compact:diff': { min: 120, max: 900 },
3687
+ }
3688
+ /* Today's fixed sizes stay the default — and stay in the stylesheet: a pane
3689
+ the user never dragged keeps exactly the look it had before, while a
3690
+ dragged one is remembered as a ratio. */
3691
+ const PANE_DEFAULTS = {
3692
+ 'columns:tree': { px: 200 },
3693
+ 'columns:changes': { px: 290 },
3694
+ 'stack:tree': { ratio: 0.36 },
3695
+ 'stack:changes': { ratio: 0.46 },
3696
+ 'compact:tree': { ratio: 0.42 },
3697
+ }
3698
+ const PANE_MAIN_MIN_W = 240
3699
+ const PANE_MAIN_MIN_H = 150
3700
+ /* The reserve above is the IDEAL the clamp aims for. When the panel is simply
3701
+ too small for every default at once, aiming for it would put a pane's
3702
+ ceiling BELOW the box that pane already occupies — and the first drag would
3703
+ then shrink it instead of growing it. Below that point the middle pane
3704
+ keeps this hard floor instead, and the pane's own current size always wins
3705
+ (see paneGeometry). */
3706
+ const PANE_MAIN_FLOOR_PX = 120
3707
+ const PANE_HISTORY_MIN_H = 120
3708
+ /* Hit area of one gutter, and how many of them sit in the body. The
3709
+ history↔diff separator lives inside the middle pane, so it is not counted
3710
+ against the body's own budget. */
3711
+ const PANE_GUTTER_PX = 8
3712
+ const PANE_BODY_GUTTERS = { columns: 2, stack: 2, compact: 1 }
3713
+ const PANE_BUCKETS = Object.keys(PANE_CHROME_KEYS).reduce(
3714
+ (all, chrome) => all.concat([chrome + ':wide', chrome + ':tall']), [])
3715
+
3716
+ /* Two measurements are the same when every pane they know about is. */
3717
+ function samePaneSizes(current, next) {
3718
+ const keys = Object.keys(current)
3719
+ if (keys.length !== Object.keys(next).length) return false
3720
+ for (const key of keys) { if (current[key] !== next[key]) return false }
3721
+ return true
3722
+ }
3723
+
3724
+ function panBucket(chrome, box) {
3725
+ return chrome + (box.height > box.width ? ':tall' : ':wide')
3726
+ }
3727
+
3728
+ /* Storage is untrusted, so this keeps normalizeRail()'s discipline: unknown
3729
+ buckets, unknown keys, non-finite numbers and out-of-range ratios are all
3730
+ dropped. A ratio is a fraction of the container, so it lives in (0, 1]. */
3731
+ function normalizePanes(raw) {
3732
+ const source = raw !== null && typeof raw === 'object' ? raw : {}
3733
+ const stored = source.panes !== null && typeof source.panes === 'object' ? source.panes : {}
3734
+ const panes = {}
3735
+ for (const bucket of PANE_BUCKETS) {
3736
+ const entry = stored[bucket]
3737
+ if (entry === null || typeof entry !== 'object') continue
3738
+ const keys = PANE_CHROME_KEYS[bucket.slice(0, bucket.indexOf(':'))]
3739
+ const clean = {}
3740
+ for (const key of keys) {
3741
+ const value = entry[key]
3742
+ if (typeof value !== 'number' || Number.isFinite(value) === false) continue
3743
+ if (value <= 0 || value > 1) continue
3744
+ clean[key] = value
3745
+ }
3746
+ if (Object.keys(clean).length > 0) panes[bucket] = clean
3747
+ }
3748
+ return { treeOpen: source.treeOpen !== false, panes: panes }
3749
+ }
3750
+
3751
+ /* Immutable single-key update. ratio === null removes the override, which is
3752
+ exactly what the double-click reset does. */
3753
+ function withPaneRatio(config, bucket, key, ratio) {
3754
+ const panes = {}
3755
+ for (const name of PANE_BUCKETS) {
3756
+ if (config.panes[name] !== undefined) panes[name] = config.panes[name]
3757
+ }
3758
+ const entry = {}
3759
+ const current = panes[bucket] === undefined ? {} : panes[bucket]
3760
+ for (const name of PANE_CHROME_KEYS[bucket.slice(0, bucket.indexOf(':'))]) {
3761
+ if (current[name] !== undefined) entry[name] = current[name]
3762
+ }
3763
+ if (ratio === null) delete entry[key]
3764
+ else entry[key] = ratio
3765
+ if (Object.keys(entry).length === 0) delete panes[bucket]
3766
+ else panes[bucket] = entry
3767
+ return { treeOpen: config.treeOpen, panes: panes }
3768
+ }
3769
+
3770
+ /* Stored ratios (or today's defaults) → concrete px for the current chrome,
3771
+ clamped against the measured container. `overrides` holds only the panes
3772
+ the user actually dragged (absent = leave the stylesheet default alone);
3773
+ `effective` is what the clamp math used; `limits` is the range each gutter
3774
+ is allowed to hand back. `laidOut` is what the panes actually measure right
3775
+ now, which beats the stylesheet default when a pane is content-sized: the
3776
+ stacked chrome caps the changes pane at 46%, but a three-file summary only
3777
+ takes 30%, and reserving the cap would freeze the tree above it. */
3778
+ function paneGeometry(chrome, box, config, laidOut) {
3779
+ const height = PANE_HEIGHT_CHROME[chrome] === true
3780
+ const reference = height ? box.height : box.width
3781
+ const bucket = panBucket(chrome, box)
3782
+ const ready = config !== null && typeof config === 'object'
3783
+ const source = ready && config.panes !== null && typeof config.panes === 'object' ? config.panes : {}
3784
+ const ratios = source[bucket] !== null && typeof source[bucket] === 'object' ? source[bucket] : {}
3785
+ const measured = laidOut !== null && typeof laidOut === 'object' ? laidOut : {}
3786
+ const keys = PANE_CHROME_KEYS[chrome].slice()
3787
+ /* A folded tree has no pane and no gutter, so it must not reserve room. */
3788
+ if (ready && config.treeOpen === false) {
3789
+ const at = keys.indexOf('tree')
3790
+ if (at >= 0) keys.splice(at, 1)
3791
+ }
3792
+ const overrides = {}
3793
+ const effective = {}
3794
+ const limits = {}
3795
+ const sizeOf = (key) => (effective[key] === undefined ? 0 : effective[key])
3796
+ for (const key of keys) {
3797
+ const spec = PANE_DEFAULTS[chrome + ':' + key]
3798
+ const fallback = spec === undefined ? 0 : (spec.px === undefined ? spec.ratio * reference : spec.px)
3799
+ const live = measured[key]
3800
+ effective[key] = typeof live === 'number' && Number.isFinite(live) && live > 0 ? live : fallback
3801
+ }
3802
+ const space = Math.max(0, reference - PANE_BODY_GUTTERS[chrome] * PANE_GUTTER_PX)
3803
+ const reserve = height ? PANE_MAIN_MIN_H : PANE_MAIN_MIN_W
3804
+ /* Deterministic order: changes, then tree, then diff. The diff is carved
3805
+ out of the middle pane, so it is clamped last — against whatever the
3806
+ first two left behind, minus the history list's own minimum. */
3807
+ for (const key of ['changes', 'tree', 'diff']) {
3808
+ if (keys.indexOf(key) < 0) continue
3809
+ const spec = PANE_LIMITS[chrome + ':' + key]
3810
+ const current = sizeOf(key)
3811
+ let ceiling = spec.max
3812
+ if (key === 'diff') {
3813
+ const main = space - sizeOf('changes') - sizeOf('tree') - PANE_GUTTER_PX
3814
+ ceiling = Math.min(ceiling, main - PANE_HISTORY_MIN_H)
3815
+ } else {
3816
+ let taken = 0
3817
+ for (const other of keys) { if (other !== key && other !== 'diff') taken += sizeOf(other) }
3818
+ /* Two tiers, because one number cannot serve both jobs. The IDEAL keeps
3819
+ the middle pane at its reserve — that is what a roomy panel gets. The
3820
+ relaxed tier only kicks in when the ideal would land BELOW the box
3821
+ this pane already occupies (a 674px column cannot hold 200 + 290 + 240
3822
+ at once), and it exists so the clamp degrades to "you may not grow
3823
+ much" instead of "the first drag to the right shrinks you". */
3824
+ const ideal = space - reserve - taken
3825
+ ceiling = Math.min(ceiling, current > ideal ? space - PANE_MAIN_FLOOR_PX - taken : ideal)
3826
+ }
3827
+ /* The clamp may refuse to GROW a pane; it may never shrink one the user
3828
+ did not touch — so the ceiling is never below the pane's current box,
3829
+ and the aria window says exactly that. */
3830
+ const cap = Math.max(spec.max, Math.round(current))
3831
+ const high = Math.min(cap, Math.max(spec.min, Math.round(ceiling), Math.round(current)))
3832
+ limits[key] = { min: spec.min, max: high }
3833
+ const ratio = ratios[key]
3834
+ if (typeof ratio === 'number' && Number.isFinite(ratio)) {
3835
+ const px = Math.round(Math.min(Math.max(ratio * reference, spec.min), high))
3836
+ overrides[key] = px
3837
+ effective[key] = px
3838
+ }
3839
+ }
3840
+ return { bucket: bucket, overrides: overrides, effective: effective, limits: limits }
3841
+ }
3842
+ /* ---- end pane sizing core ---- */
3843
+
3844
+ function readPanes() {
3845
+ try {
3846
+ const raw = window.localStorage.getItem(PANES_KEY)
3847
+ if (raw === null || raw === '') return normalizePanes(null)
3848
+ return normalizePanes(JSON.parse(raw))
3849
+ } catch (error) { void error }
3850
+ return normalizePanes(null)
3851
+ }
3852
+
3853
+ function writePanes(config) {
3854
+ try { window.localStorage.setItem(PANES_KEY, JSON.stringify(normalizePanes(config))) } catch (error) { void error }
3855
+ }
3856
+
3857
+ /* ============================== dock actions ============================== */
3858
+
3859
+ /* ---- dock action core (pure: tests/smoke.mjs slices this block) ---- */
3860
+
3861
+ /* The official right sidebar (ui-sidebar-right) is the only surface that can
3862
+ float a tab out, dock it back, split its pane or take it full screen, and it
3863
+ is NOT there on every host: with dsh-better-sidebar loaded this panel lives
3864
+ in THAT dock, an old host has no sidebarRight at all, and even on a host that
3865
+ ships it the service is only provided once ui-sidebar-right has mounted —
3866
+ reading it during apply() returns undefined (measured on 0.1.7-rc.2). So the
3867
+ service is read at render time, and a missing one means these actions do not
3868
+ exist for this mount: no button, no error, no console noise. */
3869
+ function dockFaceOf(ctx) {
3870
+ if (ctx === null || ctx === undefined || typeof ctx.get !== 'function') return null
3871
+ let service
3872
+ try { service = ctx.get('sidebarRight') } catch (error) { return null }
3873
+ if (service === null || service === undefined || typeof service !== 'object') return null
3874
+ if (typeof service.float !== 'function' || typeof service.dock !== 'function') return null
3875
+ return service
3876
+ }
3877
+
3878
+ /* What this mount can offer. `dock` is the wiring handed down to the panel
3879
+ (null when nothing is wired, e.g. the better-sidebar door), `floatPaneId` is
3880
+ the floating pane the panel is currently drawn in, read from the float
3881
+ layer's own marker. The face publishes operations only — no layout snapshot —
3882
+ so the float state has to come from there. */
3883
+ function dockActionState(dock, floatPaneId) {
3884
+ const wired = dock !== null && typeof dock === 'object' && typeof dock.tabId === 'string'
3885
+ && dock.tabId !== '' && dock.service !== null && typeof dock.service === 'object'
3886
+ const floating = typeof floatPaneId === 'string' && floatPaneId !== ''
3887
+ return {
3888
+ wired: wired,
3889
+ floating: floating,
3890
+ float: wired,
3891
+ /* A float panel hosts exactly one tab: splitting it is not a thing. */
3892
+ split: wired && floating === false,
3893
+ fullscreen: wired && typeof dock.service.toggleFullscreen === 'function',
3894
+ }
3895
+ }
3896
+ /* ---- end dock action core ---- */
3897
+
3898
+ /* The slot hands every tab body its own reader; it THROWS while the tab is not
3899
+ committed in the session, so an unreadable tab means no dock actions rather
3900
+ than a broken panel. Called from the component that owns the hook. */
3901
+ function readDockInfo(useTabInfo, ctx) {
3902
+ if (typeof useTabInfo !== 'function') return null
3903
+ const service = dockFaceOf(ctx)
3904
+ if (service === null) return null
3905
+ let info = null
3906
+ try { info = useTabInfo() } catch (error) { void error }
3907
+ if (info === null || typeof info !== 'object' || info.tab === undefined || info.panel === undefined) return null
3908
+ if (typeof info.tab.id !== 'string') return null
3909
+ return {
3910
+ service: service,
3911
+ tabId: info.tab.id,
3912
+ paneId: typeof info.panel.id === 'string' ? info.panel.id : '',
3913
+ fullscreen: info.sidebar !== undefined && info.sidebar !== null && info.sidebar.fullscreen === true,
3914
+ }
3915
+ }
3916
+
3494
3917
  /* ============================== formatting ============================== */
3495
3918
 
3496
3919
  function relativeTime(iso) {
@@ -3743,6 +4166,11 @@ window.__ModuleLoader__.load({
3743
4166
  grip: ['M5.6 4.6h4.8', 'M5.6 8h4.8', 'M5.6 11.4h4.8'],
3744
4167
  filter: ['M2 3.6h12l-4.6 5.2v4.2l-2.8-1.4V8.8z'],
3745
4168
  check: ['M3.2 8.4 6.4 11.6 12.8 4.6'],
4169
+ // dock actions (official right sidebar): lift a panel out, drop it back into
4170
+ // the dock, split the pane, and the full-screen pair reuses expand/collapse.
4171
+ float: ['M2.5 6.5v7h7v-3', 'M6 10 13.2 2.8', 'M9.4 2.8h3.8v3.8'],
4172
+ dock: ['M2.5 3h11v10h-11z', 'M8 5.2v5.6', 'M5.4 8.2 8 10.8l2.6-2.6'],
4173
+ split: ['M2.5 3h11v10h-11z', 'M8 3v10'],
3746
4174
  expand: ['M4.5 6.2 8 9.7l3.5-3.5', 'M3 12.4h10'],
3747
4175
  collapse: ['M4.5 9.8 8 6.3l3.5 3.5', 'M3 3.6h10'],
3748
4176
  }
@@ -3798,22 +4226,37 @@ window.__ModuleLoader__.load({
3798
4226
  document.removeEventListener('keydown', onKey)
3799
4227
  }
3800
4228
  }, [])
3801
- return E('div', { className: 'dig-menu', ref: ref, style: { left: position.left + 'px', top: position.top + 'px' } },
3802
- props.items.map((item, index) => (item === null
3803
- ? E('div', { key: 'sep-' + index, className: 'dig-menu-sep' })
3804
- : E('button', {
3805
- key: item.id + '-' + index,
3806
- type: 'button',
3807
- title: item.disabled === true && item.reason !== undefined ? item.reason : '',
3808
- className: 'dig-menu-item'
3809
- + (item.danger === true ? ' dig-menu-danger' : '')
3810
- + (item.disabled === true ? ' dig-menu-disabled' : '')
3811
- + (item.active === true ? ' dig-menu-item-active' : ''),
3812
- disabled: item.disabled === true,
3813
- onClick: (event) => { closeRef.current(); item.run(event) },
3814
- },
3815
- item.icon === undefined ? null : E('span', { className: 'dig-menu-icon dig-tone-' + (item.tone === undefined ? 'secondary' : item.tone) }, E(Icon, { name: item.icon, size: 13 })),
3816
- E('span', { className: 'dig-menu-label' }, item.label)))))
4229
+ /* Two boxes on purpose (dsh 0.1.7-rc.2 menu-material contract): the outer
4230
+ card owns the material (a `::before` painted on its own layer, so the
4231
+ backdrop filter never becomes the containing block or backdrop root of
4232
+ the rows) and the inner box owns the scroll. `data-menu-material` is the
4233
+ theme's hook for the dark-menu stroke rebind — the same hook the shipped
4234
+ MenuSurface sets. The card must stay a plain `position: absolute` box
4235
+ inside `.dig-root`: panel-internal positioning is an invariant (13), and
4236
+ a portal to `document.body` would land outside the panel. */
4237
+ return E('div', {
4238
+ className: 'dig-menu', ref: ref, 'data-menu-material': 'translucent',
4239
+ style: { left: position.left + 'px', top: position.top + 'px' },
4240
+ },
4241
+ E('div', { className: 'dig-menu-scroll' },
4242
+ props.items.map((item, index) => (item === null
4243
+ ? E('div', { key: 'sep-' + index, className: 'dig-menu-sep' })
4244
+ : E('button', {
4245
+ key: item.id + '-' + index,
4246
+ type: 'button',
4247
+ title: item.disabled === true && item.reason !== undefined ? item.reason : '',
4248
+ className: 'dig-menu-item'
4249
+ + (item.danger === true ? ' dig-menu-danger' : '')
4250
+ + (item.disabled === true ? ' dig-menu-disabled' : '')
4251
+ + (item.active === true ? ' dig-menu-item-active' : ''),
4252
+ // The row keeps its id on the DOM so a `⋯ 更多` entry is addressable
4253
+ // (the rail's own buttons do the same; probes click through either).
4254
+ 'data-action': item.id === undefined ? undefined : item.id,
4255
+ disabled: item.disabled === true,
4256
+ onClick: (event) => { closeRef.current(); item.run(event) },
4257
+ },
4258
+ item.icon === undefined ? null : E('span', { className: 'dig-menu-icon dig-tone-' + (item.tone === undefined ? 'secondary' : item.tone) }, E(Icon, { name: item.icon, size: 13 })),
4259
+ E('span', { className: 'dig-menu-label' }, item.label))))))
3817
4260
  }
3818
4261
 
3819
4262
  /* Lets the user choose which actions the rail shows and in which order. The
@@ -3948,6 +4391,77 @@ window.__ModuleLoader__.load({
3948
4391
  }, item.label)))
3949
4392
  }
3950
4393
 
4394
+ /* A draggable divider between two panes. The pane it resizes is the sibling
4395
+ it is rendered against, and its size is MEASURED at pointerdown — so a pane
4396
+ still living on its stylesheet default (a percentage cap, or a diff pane
4397
+ sized by its own content) drags from exactly where it looks. Pointer capture
4398
+ keeps the drag alive once the pointer leaves the 8px hit area, and
4399
+ touch-action:none is what stops a phone from scrolling the panel instead of
4400
+ resizing it. Arrow keys nudge (Shift = 4x), double-click resets, and the
4401
+ whole thing is focusable so the divider is reachable without a mouse. */
4402
+ function Gutter(props) {
4403
+ const dragRef = useRef(null)
4404
+ const targetOf = (node) => (props.side === 'next' ? node.nextElementSibling : node.previousElementSibling)
4405
+ const extentOf = (node) => {
4406
+ if (node === null || typeof node.getBoundingClientRect !== 'function') return 0
4407
+ const rect = node.getBoundingClientRect()
4408
+ return props.vertical === true ? rect.width : rect.height
4409
+ }
4410
+ const sign = props.side === 'next' ? -1 : 1
4411
+ const onPointerDown = (event) => {
4412
+ if (event.button !== undefined && event.button !== 0) return
4413
+ dragRef.current = {
4414
+ id: event.pointerId,
4415
+ at: props.vertical === true ? event.clientX : event.clientY,
4416
+ size: extentOf(targetOf(event.currentTarget)),
4417
+ }
4418
+ if (typeof event.currentTarget.setPointerCapture === 'function') event.currentTarget.setPointerCapture(event.pointerId)
4419
+ if (typeof event.currentTarget.focus === 'function') event.currentTarget.focus()
4420
+ props.onStart()
4421
+ }
4422
+ const onPointerMove = (event) => {
4423
+ const drag = dragRef.current
4424
+ if (drag === null || drag.id !== event.pointerId) return
4425
+ const at = props.vertical === true ? event.clientX : event.clientY
4426
+ props.onDrag(drag.size + sign * (at - drag.at))
4427
+ }
4428
+ const onPointerUp = () => {
4429
+ if (dragRef.current === null) return
4430
+ dragRef.current = null
4431
+ props.onEnd()
4432
+ }
4433
+ const onKeyDown = (event) => {
4434
+ const step = event.shiftKey === true ? 32 : 8
4435
+ const axis = props.vertical === true
4436
+ ? (event.key === 'ArrowLeft' ? -1 : event.key === 'ArrowRight' ? 1 : 0)
4437
+ : (event.key === 'ArrowUp' ? -1 : event.key === 'ArrowDown' ? 1 : 0)
4438
+ if (axis === 0) return
4439
+ event.preventDefault()
4440
+ props.onDrag(extentOf(targetOf(event.currentTarget)) + sign * axis * step)
4441
+ props.onEnd()
4442
+ }
4443
+ const label = fill(props.t('pane.resize'), { name: props.t(props.nameKey) })
4444
+ return E('div', {
4445
+ className: 'dig-gutter ' + (props.vertical === true ? 'dig-gutter-v' : 'dig-gutter-h')
4446
+ + (props.active === true ? ' dig-gutter-active' : ''),
4447
+ role: 'separator',
4448
+ tabIndex: 0,
4449
+ 'aria-orientation': props.vertical === true ? 'vertical' : 'horizontal',
4450
+ 'aria-label': label,
4451
+ 'aria-valuenow': props.value,
4452
+ 'aria-valuemin': props.min,
4453
+ 'aria-valuemax': props.max,
4454
+ title: label,
4455
+ onPointerDown: onPointerDown,
4456
+ onPointerMove: onPointerMove,
4457
+ onPointerUp: onPointerUp,
4458
+ onPointerCancel: onPointerUp,
4459
+ onLostPointerCapture: onPointerUp,
4460
+ onDoubleClick: () => props.onReset(),
4461
+ onKeyDown: onKeyDown,
4462
+ })
4463
+ }
4464
+
3951
4465
  function RepoSelect(props) {
3952
4466
  const t = props.t
3953
4467
  /* While the workspace is still being scanned the panel used to print "no
@@ -4560,7 +5074,21 @@ window.__ModuleLoader__.load({
4560
5074
  const [note, setNote] = useState(null)
4561
5075
  const [busy, setBusy] = useState(false)
4562
5076
  const [view, setView] = useState('history')
4563
- const [treeOpen, setTreeOpen] = useState(true)
5077
+ /* Pane sizes are remembered per surface (see the pane sizing core). The ref
5078
+ is the value the pointer handlers write through: a drag updates state for
5079
+ the preview and the ref for the single write that pointerup performs. */
5080
+ const [panes, setPanes] = useState(readPanes)
5081
+ const panesRef = useRef(panes)
5082
+ const [treeOpen, setTreeOpen] = useState(panes.treeOpen)
5083
+ const [paneDragging, setPaneDragging] = useState(null)
5084
+ const [paneSizes, setPaneSizes] = useState({})
5085
+ /* The float pane this panel is drawn in, or null while it is docked. The
5086
+ dock face exposes operations only, so the state comes from the float
5087
+ layer's own marker around this panel — the same marker carries the pane
5088
+ id dock() needs to put it back. */
5089
+ const [floatPaneId, setFloatPaneId] = useState(null)
5090
+ const bodyRef = useRef(null)
5091
+ const [bodyBox, setBodyBox] = useState({ width: 0, height: 0 })
4564
5092
  const [detail, setDetail] = useState(null)
4565
5093
  const [selectedHash, setSelectedHash] = useState(null)
4566
5094
  const [selectedPath, setSelectedPath] = useState(null)
@@ -4610,6 +5138,145 @@ window.__ModuleLoader__.load({
4610
5138
  const compact = size.width > 0 && size.width < COMPACT_MAX_WIDTH
4611
5139
  const columns = !compact && size.width >= 600 && size.width >= size.height * 1.15
4612
5140
 
5141
+ /* ---------- draggable panes (issue #5) ---------- */
5142
+
5143
+ // Like the rail, the panes measure the box that actually holds them instead
5144
+ // of trusting the root probe: the column chrome keeps a vertical rail
5145
+ // beside the body, and the header sits above it.
5146
+ useEffect(() => {
5147
+ const element = bodyRef.current
5148
+ if (element === null) { setBodyBox({ width: 0, height: 0 }); return undefined }
5149
+ const measure = () => {
5150
+ const rect = element.getBoundingClientRect()
5151
+ setBodyBox({ width: Math.round(rect.width), height: Math.round(rect.height) })
5152
+ }
5153
+ measure()
5154
+ if (typeof ResizeObserver === 'function') {
5155
+ const observer = new ResizeObserver(measure)
5156
+ observer.observe(element)
5157
+ return () => observer.disconnect()
5158
+ }
5159
+ window.addEventListener('resize', measure)
5160
+ return () => window.removeEventListener('resize', measure)
5161
+ }, [])
5162
+
5163
+ const chrome = compact ? 'compact' : columns ? 'columns' : 'stack'
5164
+ const measured = bodyBox.width > 0 || bodyBox.height > 0
5165
+ const paneBox = measured ? bodyBox : size
5166
+ /* The column chrome's dividers are vertical LINES, and the panes they move
5167
+ are sized by width; the stacked chromes move heights. One name for that
5168
+ switch: it decides the divider's orientation, the inline size, the ratio
5169
+ reference and the drag cursor, and getting it half-right is how a height
5170
+ drag ends up writing a width. */
5171
+ const paneByWidth = PANE_HEIGHT_CHROME[chrome] !== true
5172
+ /* What the panes actually measure right now. The clamp needs the real box of
5173
+ the pane it is NOT moving: a content-sized pane is usually shorter than
5174
+ its own percentage cap, and reserving the cap would block the drag (or
5175
+ snap a stored size back on the next render). The markers are data
5176
+ attributes rather than class sniffing so the diff pane, nested inside the
5177
+ middle pane, is found the same way. */
5178
+ const measurePanes = () => {
5179
+ const found = {}
5180
+ const element = bodyRef.current
5181
+ if (element === null) return found
5182
+ for (const node of element.querySelectorAll('[data-pane]')) {
5183
+ const rect = node.getBoundingClientRect()
5184
+ found[node.getAttribute('data-pane')] = Math.round(paneByWidth ? rect.width : rect.height)
5185
+ }
5186
+ return found
5187
+ }
5188
+ /* Kept in state, not in a ref: a pane's box changes without the panel
5189
+ re-rendering (content arrives, a dock resizes), and the render that
5190
+ re-clamps a stored size has to see the same numbers the drag did — a drag
5191
+ allowed further than the render allows snaps back under the cursor. The
5192
+ bail-out comparison is what keeps this from looping. */
5193
+ useEffect(() => {
5194
+ const found = measurePanes()
5195
+ setPaneSizes((current) => (samePaneSizes(current, found) ? current : found))
5196
+ })
5197
+ useEffect(() => {
5198
+ const node = hostRef.current
5199
+ const holder = node === null || typeof node.closest !== 'function' ? null : node.closest('[data-dockkit-float]')
5200
+ const next = holder === null ? null : holder.getAttribute('data-dockkit-float')
5201
+ setFloatPaneId((current) => (current === next ? current : next))
5202
+ })
5203
+ const dock = props.dock === undefined ? null : props.dock
5204
+ const dockState = dockActionState(dock, floatPaneId)
5205
+ const paneLayout = paneGeometry(chrome, paneBox, panes, paneSizes)
5206
+
5207
+ const commitPanes = (next) => {
5208
+ panesRef.current = next
5209
+ setPanes(next)
5210
+ }
5211
+
5212
+ /* The gutter hands back the px it wants; the clamp lives here, and what
5213
+ gets stored is the ratio that reproduces it on this container. The live
5214
+ measurement is pushed into state alongside the new ratio, so the render
5215
+ that follows this gesture clamps with the very same box. */
5216
+ const dragPane = (key, rawPx) => {
5217
+ const live = measurePanes()
5218
+ setPaneSizes((current) => (samePaneSizes(current, live) ? current : live))
5219
+ const layout = paneGeometry(chrome, paneBox, panesRef.current, live)
5220
+ const limit = layout.limits[key]
5221
+ const reference = paneByWidth ? paneBox.width : paneBox.height
5222
+ if (limit === undefined || !(reference > 0)) return
5223
+ const px = Math.round(Math.min(Math.max(rawPx, limit.min), limit.max))
5224
+ commitPanes(withPaneRatio(panesRef.current, layout.bucket, key, px / reference))
5225
+ }
5226
+
5227
+ /* One write per gesture (pointerup / keyboard nudge / double-click), not one
5228
+ per pointermove. */
5229
+ const endPaneDrag = () => {
5230
+ setPaneDragging(null)
5231
+ writePanes(panesRef.current)
5232
+ }
5233
+
5234
+ const resetPane = (key) => {
5235
+ commitPanes(withPaneRatio(panesRef.current, paneLayout.bucket, key, null))
5236
+ writePanes(panesRef.current)
5237
+ }
5238
+
5239
+ const foldTree = (open) => {
5240
+ setTreeOpen(open)
5241
+ commitPanes({ treeOpen: open, panes: panesRef.current.panes })
5242
+ writePanes(panesRef.current)
5243
+ }
5244
+
5245
+ /* Only a pane the user actually dragged gets an inline size; everything
5246
+ else keeps the stylesheet default (200px tree, 290px changes, the
5247
+ percentage caps) — which is also what a double-click returns to. */
5248
+ const paneStyle = (key) => {
5249
+ const px = paneLayout.overrides[key]
5250
+ if (px === undefined) return null
5251
+ return paneByWidth ? { width: px + 'px', maxWidth: px + 'px' } : { height: px + 'px', maxHeight: px + 'px' }
5252
+ }
5253
+
5254
+ /* A divider is a control, so it only exists when it can actually move
5255
+ something: no pane of that key in this chrome, or a window with no travel
5256
+ at all, means no element — and therefore no tab stop either (v0.8.0 shipped
5257
+ a 0x0 focusable separator in the columns chrome that way). */
5258
+ const gutter = (key, side) => {
5259
+ const limit = paneLayout.limits[key]
5260
+ if (limit === undefined || limit.max <= limit.min) return null
5261
+ return E(Gutter, {
5262
+ key: 'gutter-' + key,
5263
+ t: t,
5264
+ nameKey: PANE_NAME_KEYS[key],
5265
+ vertical: paneByWidth,
5266
+ side: side,
5267
+ active: paneDragging === key,
5268
+ // Now is clamped into [min, max]: an aria window the panel contradicts is
5269
+ // worse than no window at all.
5270
+ value: Math.min(Math.max(Math.round(paneLayout.effective[key] === undefined ? 0 : paneLayout.effective[key]), limit.min), limit.max),
5271
+ min: limit.min,
5272
+ max: limit.max,
5273
+ onStart: () => setPaneDragging(key),
5274
+ onDrag: (px) => dragPane(key, px),
5275
+ onEnd: endPaneDrag,
5276
+ onReset: () => resetPane(key),
5277
+ })
5278
+ }
5279
+
4613
5280
  // The action rail measures ITSELF, not the panel: how many buttons fit is a
4614
5281
  // function of the strip that holds them (a 1200x300 workbench and a 300x900
4615
5282
  // sidebar disagree about that). railBox keeps the last measured span; until
@@ -5062,7 +5729,7 @@ window.__ModuleLoader__.load({
5062
5729
  onLoadMore: () => { void guard(() => loadCommits(commits.length)) },
5063
5730
  })
5064
5731
 
5065
- const diffPane = patch === '' && patchLoading === false ? null : E('div', { className: 'dig-diff-pane' },
5732
+ const diffPane = patch === '' && patchLoading === false ? null : E('div', { className: 'dig-diff-pane', 'data-pane': 'diff', style: paneStyle('diff') },
5066
5733
  E('div', { className: 'dig-diff-head' },
5067
5734
  E('span', { className: 'dig-mono dig-diff-path' }, selectedPath === null ? '' : selectedPath),
5068
5735
  E('button', { type: 'button', className: 'dig-icon-btn', onClick: () => { setPatch(''); setSelectedPath(null) } }, E(Icon, { name: 'close', size: 12 }))),
@@ -5141,13 +5808,44 @@ window.__ModuleLoader__.load({
5141
5808
  else if (spec.id === 'fetch') { entry.disabled = remoteReady !== true; entry.run = () => { void run('fetch', { prune: true }) } }
5142
5809
  else if (spec.id === 'pull') { entry.disabled = remoteReady !== true || tracked !== true || blocked; entry.run = () => { void run('pull', { mode: 'ff-only' }) } }
5143
5810
  else if (spec.id === 'push') { entry.disabled = remoteReady !== true || blocked; entry.run = () => setDialog({ kind: 'push' }) }
5144
- if (busy === true && spec.id !== 'refresh') entry.disabled = true
5811
+ else if (spec.id === 'tree') { entry.active = treeOpen; entry.run = () => foldTree(!treeOpen) }
5812
+ else if (spec.id === 'float') {
5813
+ // Label AND icon follow where the panel actually is: floating panels get
5814
+ // the way back, docked ones get the way out.
5815
+ entry.available = dockState.float
5816
+ entry.active = dockState.floating
5817
+ entry.icon = dockState.floating === true ? 'dock' : 'float'
5818
+ entry.label = dockState.floating === true ? t('action.dock') : t('action.float')
5819
+ entry.run = () => {
5820
+ if (dockState.floating === true) { dock.service.dock(floatPaneId); return }
5821
+ dock.service.float(dock.tabId)
5822
+ }
5823
+ }
5824
+ else if (spec.id === 'split') {
5825
+ entry.available = dockState.split
5826
+ entry.run = () => { dock.service.split(dock.paneId) }
5827
+ }
5828
+ else if (spec.id === 'fullscreen') {
5829
+ entry.available = dockState.fullscreen
5830
+ entry.active = dock !== null && dock.fullscreen === true
5831
+ entry.icon = entry.active === true ? 'collapse' : 'expand'
5832
+ entry.label = entry.active === true ? t('action.exitFullscreen') : t('action.fullscreen')
5833
+ entry.run = () => {
5834
+ const service = dock.service
5835
+ const target = typeof service.commandTarget === 'function' ? service.commandTarget(document.activeElement) : undefined
5836
+ if (target !== undefined && target !== null) service.toggleFullscreen(target)
5837
+ }
5838
+ }
5839
+ // A running git command greys out the repository actions, never the view.
5840
+ if (busy === true && spec.id !== 'refresh' && RAIL_VIEW_IDS.indexOf(spec.id) < 0) entry.disabled = true
5145
5841
  return entry
5146
5842
  })
5147
5843
 
5148
5844
  const railOrdered = railConfig.order
5149
5845
  .map((id) => railActions.find((action) => action.id === id))
5150
- .filter((action) => action !== undefined)
5846
+ // An action this mount cannot perform is not rendered at all — hidden, not
5847
+ // disabled: a greyed button for a service the host does not have is noise.
5848
+ .filter((action) => action !== undefined && action.available !== false)
5151
5849
  const railVisible = railOrdered.filter((action) => railConfig.hidden.indexOf(action.id) < 0)
5152
5850
 
5153
5851
  const applyRail = (next) => { setRailConfig(next); writeRailConfig(next) }
@@ -5199,6 +5897,8 @@ window.__ModuleLoader__.load({
5199
5897
  key: action.id,
5200
5898
  type: 'button',
5201
5899
  className: 'dig-rail-btn dig-tone-' + action.tone + (action.active === true ? ' dig-rail-btn-active' : ''),
5900
+ 'data-action': action.id,
5901
+ 'aria-pressed': action.active === true ? 'true' : 'false',
5202
5902
  title: action.disabled === true ? action.label + ' · ' + reason : action.label,
5203
5903
  disabled: action.disabled === true,
5204
5904
  onClick: action.run,
@@ -5234,18 +5934,24 @@ window.__ModuleLoader__.load({
5234
5934
 
5235
5935
  /* ---------- body per chrome ---------- */
5236
5936
 
5937
+ /* ---------- body per chrome ---------- */
5938
+
5939
+ /* The history↔diff separator only exists while a diff is on screen, and it
5940
+ lives inside the middle pane — the diff is the pane below it. */
5941
+ const diffGutter = diffPane === null ? null : gutter('diff', 'next')
5942
+
5237
5943
  let body
5238
5944
  if (repoState === null) {
5239
5945
  /* The scan is still running: a neutral loading state, never the
5240
5946
  "no repository found" verdict (that text is reserved for a scan
5241
5947
  that actually came back empty). */
5242
- body = E('div', { className: 'dig-body' }, E('div', { className: 'dig-empty' }, t('status.loading')))
5948
+ body = E('div', { className: 'dig-body', ref: bodyRef }, E('div', { className: 'dig-empty' }, t('status.loading')))
5243
5949
  } else if (repoRoot === null) {
5244
- body = E('div', { className: 'dig-body' }, E(RepoPicker, {
5950
+ body = E('div', { className: 'dig-body', ref: bodyRef }, E(RepoPicker, {
5245
5951
  t: t, cwd: cwd === undefined ? '' : cwd, repos: repoOptions, onPick: pickRepo,
5246
5952
  }))
5247
5953
  } else if (compact) {
5248
- body = E('div', { className: 'dig-body dig-body-compact' },
5954
+ body = E('div', { className: 'dig-body dig-body-compact', ref: bodyRef },
5249
5955
  E('div', { className: 'dig-compact-bar' },
5250
5956
  E(Segmented, {
5251
5957
  value: view === 'detail' ? 'history' : view,
@@ -5256,19 +5962,34 @@ window.__ModuleLoader__.load({
5256
5962
  ],
5257
5963
  }),
5258
5964
  E('span', { className: 'dig-topbar-spacer' }),
5259
- treeOpen ? E('button', { type: 'button', className: 'dig-icon-btn dig-icon-btn-active', title: t('toolbar.tree'), onClick: () => setTreeOpen(false) }, E(Icon, { name: 'branch' })) : null),
5260
- treeOpen ? E('div', { className: 'dig-compact-tree' }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
5261
- view === 'changes' ? changesPane : E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffPane))
5965
+ // Two-way on purpose: treeOpen is persisted, so a header button that
5966
+ // only rendered while the tree was OPEN left a folded tree with no way
5967
+ // back on this chrome (the rail now carries one too, for the others).
5968
+ E('button', {
5969
+ type: 'button',
5970
+ className: 'dig-icon-btn' + (treeOpen ? ' dig-icon-btn-active' : ''),
5971
+ 'data-action': 'tree',
5972
+ 'aria-pressed': treeOpen ? 'true' : 'false',
5973
+ title: t('toolbar.tree'),
5974
+ onClick: () => foldTree(!treeOpen),
5975
+ }, E(Icon, { name: treeOpen ? 'eye' : 'eyeOff' }))),
5976
+ treeOpen ? E('div', { className: 'dig-compact-tree', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
5977
+ treeOpen ? gutter('tree', 'prev') : null,
5978
+ view === 'changes' ? changesPane : E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffGutter, diffPane))
5262
5979
  } else if (columns) {
5263
- body = E('div', { className: 'dig-body dig-body-columns' },
5264
- treeOpen ? E('div', { className: 'dig-pane dig-pane-tree' }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
5265
- E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffPane),
5266
- E('div', { className: 'dig-pane dig-pane-changes' }, changesPane))
5980
+ body = E('div', { className: 'dig-body dig-body-columns', ref: bodyRef },
5981
+ treeOpen ? E('div', { className: 'dig-pane dig-pane-tree', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
5982
+ treeOpen ? gutter('tree', 'prev') : null,
5983
+ E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffGutter, diffPane),
5984
+ gutter('changes', 'next'),
5985
+ E('div', { className: 'dig-pane dig-pane-changes', 'data-pane': 'changes', style: paneStyle('changes') }, changesPane))
5267
5986
  } else {
5268
- body = E('div', { className: 'dig-body dig-body-stack' },
5269
- treeOpen ? E('div', { className: 'dig-pane dig-pane-tree-stack' }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
5270
- E('div', { className: 'dig-pane dig-pane-changes-stack' }, changesPane),
5271
- E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffPane))
5987
+ body = E('div', { className: 'dig-body dig-body-stack', ref: bodyRef },
5988
+ treeOpen ? E('div', { className: 'dig-pane dig-pane-tree-stack', 'data-pane': 'tree', style: paneStyle('tree') }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
5989
+ treeOpen ? gutter('tree', 'prev') : null,
5990
+ E('div', { className: 'dig-pane dig-pane-changes-stack', 'data-pane': 'changes', style: paneStyle('changes') }, changesPane),
5991
+ gutter('changes', 'prev'),
5992
+ E('div', { className: 'dig-pane dig-pane-main' }, historyPane, diffGutter, diffPane))
5272
5993
  }
5273
5994
 
5274
5995
  /* ---------- overlays ---------- */
@@ -5394,7 +6115,14 @@ window.__ModuleLoader__.load({
5394
6115
  // narrow ones (the native right sidebar) get the same actions as one
5395
6116
  // horizontal row under the repo/branch bar, and every action lives in
5396
6117
  // exactly one place.
5397
- return E('div', { className: 'dig-root', ref: hostRef }, topBar,
6118
+ // While a divider is being dragged the whole panel stops selecting text and
6119
+ // keeps the resize cursor, even though the pointer has left the 8px gutter.
6120
+ return E('div', {
6121
+ className: 'dig-root'
6122
+ + (compact ? ' dig-root-narrow' : '')
6123
+ + (paneDragging === null ? '' : ' dig-pane-dragging' + (paneByWidth ? '-v' : '-h')),
6124
+ ref: hostRef,
6125
+ }, topBar,
5398
6126
  columns === true || repoRoot === null ? null : renderRail(false),
5399
6127
  banner, noteBanner,
5400
6128
  E('div', { className: 'dig-shell' },
@@ -5421,9 +6149,9 @@ window.__ModuleLoader__.load({
5421
6149
  '.dig-busy{color:var(--dsw-alias-label-tertiary);flex:none;white-space:nowrap}',
5422
6150
  '.dig-track{color:var(--dsw-alias-label-secondary);flex:none;font-variant-numeric:tabular-nums;white-space:nowrap}',
5423
6151
  '.dig-select-wrap{display:inline-flex;align-items:center;gap:4px;min-width:0;color:var(--dsw-alias-label-secondary)}',
5424
- '.dig-select{appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;padding:1px 6px;height:22px;max-width:190px;min-width:0;cursor:pointer;text-overflow:ellipsis}',
6152
+ '.dig-select{appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,8px);color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;padding:1px 6px;height:22px;max-width:190px;min-width:0;cursor:pointer;text-overflow:ellipsis}',
5425
6153
  '.dig-select:hover{background:var(--dsw-alias-interactive-bg-hover)}',
5426
- '.dig-select:focus{outline:1px solid var(--dsw-alias-brand-primary);outline-offset:-1px}',
6154
+ '.dig-select:focus-visible{outline:1px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-1px}',
5427
6155
  '.dig-select-btn{display:inline-flex;align-items:center;gap:4px;text-align:left;white-space:nowrap;overflow:hidden}',
5428
6156
  '.dig-select-btn .dig-repo-name{flex:1;min-width:0;max-width:none}',
5429
6157
  '.dig-select-repo{max-width:200px}',
@@ -5431,7 +6159,7 @@ window.__ModuleLoader__.load({
5431
6159
  '.dig-select-branch{max-width:150px}',
5432
6160
  '.dig-repo-static{display:inline-flex;align-items:center;gap:5px;min-width:0;color:var(--dsw-alias-label-secondary)}',
5433
6161
  '.dig-repo-name{color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px}',
5434
- '.dig-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;padding:0;flex:none}',
6162
+ '.dig-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;padding:0;flex:none}',
5435
6163
  '.dig-icon-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
5436
6164
  '.dig-icon-btn:disabled{opacity:.4;cursor:default}',
5437
6165
  '.dig-icon-btn-active{background:var(--dsw-alias-interactive-bg-active,var(--dsw-alias-interactive-bg-hover));color:var(--dsw-alias-label-primary)}',
@@ -5443,7 +6171,7 @@ window.__ModuleLoader__.load({
5443
6171
  '.dig-rail-gap{flex:1;min-height:2px}',
5444
6172
  '.dig-rail-gap-x{flex:1;min-width:2px}',
5445
6173
  '.dig-rail-row{display:flex;align-items:center;gap:2px;padding:3px 6px;flex:none;overflow:hidden;border-bottom:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
5446
- '.dig-rail-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;padding:0;flex:none}',
6174
+ '.dig-rail-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;padding:0;flex:none}',
5447
6175
  '.dig-rail-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
5448
6176
  '.dig-rail-btn:disabled{opacity:.28;cursor:default}',
5449
6177
  '.dig-rail-btn-active{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent)}',
@@ -5457,18 +6185,23 @@ window.__ModuleLoader__.load({
5457
6185
  '.dig-tone-danger{color:var(--dsw-alias-state-error-primary)}',
5458
6186
  '.dig-tone-violet{color:#b083f0}',
5459
6187
  '.dig-tone-cyan{color:#59b0d6}',
5460
- '.dig-icon-btn-small{width:20px;height:20px;border-radius:5px}',
6188
+ '.dig-icon-btn-small{width:20px;height:20px;border-radius:var(--dsw-radius-xs,4px)}',
6189
+ // A panel dragged narrower than COMPACT_MAX_WIDTH (the compact chrome) is the
6190
+ // case where 'nowrap + ellipsis' stops being readable: the overlays clamp
6191
+ // their own width, and their text wraps inside that box instead of clipping.
6192
+ '.dig-root-narrow .dig-menu-item{white-space:normal}',
6193
+ '.dig-root-narrow .dig-toast-text{white-space:normal}',
5461
6194
  '.dig-toasts{position:absolute;right:8px;bottom:8px;display:flex;flex-direction:column;gap:6px;z-index:80;max-width:min(340px,92%)}',
5462
- '.dig-toast{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));border:1px solid var(--dsw-alias-border-l2);box-shadow:0 8px 22px rgba(0,0,0,.32)}',
6195
+ '.dig-toast{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));border:0;box-shadow:var(--dsw-elevation-prominent,0 8px 22px rgba(0,0,0,.32))}',
5463
6196
  '.dig-toast-icon{display:inline-flex;flex:none;color:var(--dsw-alias-label-secondary)}',
5464
6197
  '.dig-toast-warn .dig-toast-icon{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary))}',
5465
6198
  '.dig-toast-ok .dig-toast-icon{color:var(--dsw-alias-state-success-primary)}',
5466
6199
  '.dig-toast-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}',
5467
6200
  '.dig-toast-action{border:none;background:transparent;color:var(--dsw-alias-brand-primary);font:inherit;font-weight:600;cursor:pointer;padding:0 2px;flex:none}',
5468
6201
  '.dig-toast-action:hover{text-decoration:underline}',
5469
- '.dig-opchip{flex:none;padding:0 6px;border-radius:999px;background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary)) 20%, transparent);color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary));font-weight:500;white-space:nowrap}',
5470
- '.dig-rail-list{display:flex;flex-direction:column;gap:2px;max-height:min(50vh,300px);overflow:auto;padding:2px;border:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1));border-radius:8px}',
5471
- '.dig-rail-item{display:flex;align-items:center;gap:6px;padding:2px 4px;border-radius:6px}',
6202
+ '.dig-opchip{flex:none;padding:0 6px;border-radius:999px;corner-shape:round;background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary)) 20%, transparent);color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary));font-weight:500;white-space:nowrap}',
6203
+ '.dig-rail-list{display:flex;flex-direction:column;gap:2px;max-height:min(50vh,300px);overflow:auto;padding:2px;border:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1));border-radius:var(--dsw-radius-sm,8px)}',
6204
+ '.dig-rail-item{display:flex;align-items:center;gap:6px;padding:2px 4px;border-radius:calc(var(--dsw-radius-sm,8px) - 2px)}',
5472
6205
  '.dig-rail-item:hover{background:var(--dsw-alias-interactive-bg-hover)}',
5473
6206
  '.dig-rail-item-off{opacity:.5}',
5474
6207
  '.dig-rail-item-drag{outline:1px solid var(--dsw-alias-brand-primary)}',
@@ -5487,19 +6220,33 @@ window.__ModuleLoader__.load({
5487
6220
  '.dig-pane-changes-stack{max-height:46%;flex:none;border-bottom:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
5488
6221
  '.dig-compact-bar{display:flex;align-items:center;gap:6px;padding:4px 6px;flex:none}',
5489
6222
  '.dig-compact-tree{max-height:42%;flex:none;border-bottom:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
5490
- '.dig-seg{display:inline-flex;gap:2px;padding:2px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover);flex:none}',
5491
- '.dig-seg-item{border:none;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-weight:500;padding:2px 10px;border-radius:6px;cursor:pointer;white-space:nowrap}',
6223
+ // A divider: an 8px hit area (a fingertip needs it) painted as a 1px hairline
6224
+ // that lights up on hover, focus or while dragging. touch-action:none is what
6225
+ // makes a touch drag resize the pane instead of scrolling the panel.
6226
+ '.dig-gutter{flex:none;align-self:stretch;position:relative;z-index:1;touch-action:none;background:transparent;border:0;padding:0;cursor:col-resize}',
6227
+ '.dig-gutter-v{width:8px}',
6228
+ '.dig-gutter-h{height:8px;cursor:row-resize}',
6229
+ '.dig-gutter::after{content:"";position:absolute;background:var(--dsw-alias-hairline,var(--dsw-alias-border-l1));transition:background .12s ease}',
6230
+ '.dig-gutter-v::after{top:0;bottom:0;left:50%;width:1px;margin-left:-.5px}',
6231
+ '.dig-gutter-h::after{left:0;right:0;top:50%;height:1px;margin-top:-.5px}',
6232
+ '.dig-gutter:hover::after,.dig-gutter:focus-visible::after,.dig-gutter-active::after{background:var(--dsw-alias-brand-primary)}',
6233
+ '.dig-gutter:focus-visible{outline:1px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-1px}',
6234
+ '.dig-pane-dragging,.dig-pane-dragging *{user-select:none}',
6235
+ '.dig-pane-dragging-v{cursor:col-resize}',
6236
+ '.dig-pane-dragging-h{cursor:row-resize}',
6237
+ '.dig-seg{display:inline-flex;gap:2px;padding:2px;border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-interactive-bg-hover);flex:none}',
6238
+ '.dig-seg-item{border:none;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-weight:500;padding:2px 10px;border-radius:calc(var(--dsw-radius-sm,8px) - 2px);cursor:pointer;white-space:nowrap}',
5492
6239
  '.dig-seg-item:hover{color:var(--dsw-alias-label-primary)}',
5493
6240
  '.dig-seg-item[data-active="true"]{background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-primary)}',
5494
6241
  '.dig-search{padding:4px 6px;flex:none}',
5495
- '.dig-input{width:100%;box-sizing:border-box;padding:4px 6px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}',
6242
+ '.dig-input{width:100%;box-sizing:border-box;padding:4px 6px;border-radius:var(--dsw-radius-sm,8px);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}',
5496
6243
  '.dig-input::placeholder{color:var(--dsw-alias-label-tertiary)}',
5497
- '.dig-input:focus{border-color:var(--dsw-alias-brand-primary)}',
6244
+ '.dig-input:focus{border-color:var(--dsw-alias-state-business-primary,var(--dsw-alias-brand-primary))}',
5498
6245
  '.dig-input-compact{padding:3px 6px}',
5499
6246
  '.dig-picker{display:flex;flex-direction:column;gap:2px;padding:12px;overflow:auto;flex:1;min-height:0}',
5500
6247
  '.dig-picker-title{color:var(--dsw-alias-label-primary);font-weight:600;margin-bottom:2px}',
5501
6248
  '.dig-picker-sub{color:var(--dsw-alias-label-tertiary);margin-bottom:8px;word-break:break-all}',
5502
- '.dig-picker-row{display:flex;align-items:center;gap:8px;width:100%;text-align:left;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;padding:6px 8px;border-radius:8px;cursor:pointer}',
6249
+ '.dig-picker-row{display:flex;align-items:center;gap:8px;width:100%;text-align:left;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;padding:6px 8px;border-radius:var(--dsw-radius-sm,8px);cursor:pointer}',
5503
6250
  '.dig-picker-row:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-brand-primary)}',
5504
6251
  '.dig-picker-icon{display:inline-flex;color:var(--dsw-alias-label-secondary);flex:none}',
5505
6252
  '.dig-picker-name{flex:none;font-weight:600}',
@@ -5537,7 +6284,7 @@ window.__ModuleLoader__.load({
5537
6284
  '.dig-row-label{overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;font-weight:600}',
5538
6285
  '.dig-row-sub{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;max-width:45%;overflow:hidden;text-overflow:ellipsis}',
5539
6286
  '.dig-row-dir{margin-left:auto;opacity:.75}',
5540
- '.dig-badge{font-size:10px;padding:0 5px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);flex:none}',
6287
+ '.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}',
5541
6288
  '.dig-badge-muted{opacity:.8}',
5542
6289
  '.dig-badge-worktree{padding:0 3px;display:inline-flex;align-items:center;height:14px}',
5543
6290
  '.dig-history{display:flex;flex-direction:column;min-height:0;flex:1}',
@@ -5550,7 +6297,7 @@ window.__ModuleLoader__.load({
5550
6297
  '.dig-commit-author{color:var(--dsw-alias-label-secondary);flex:none;width:96px;overflow:hidden;text-overflow:ellipsis;font-weight:500}',
5551
6298
  '.dig-commit-subject{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}',
5552
6299
  '.dig-commit-fill{flex:1 1 0;min-width:0}',
5553
- '.dig-ref{font-size:10px;padding:0 5px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);flex:none;max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}',
6300
+ '.dig-ref{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;max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}',
5554
6301
  '.dig-ref-head{color:var(--dsw-alias-state-success-primary)}',
5555
6302
  '.dig-ref-local{color:var(--dsw-alias-brand-primary)}',
5556
6303
  '.dig-ref-remote{color:#b083f0}',
@@ -5558,13 +6305,13 @@ window.__ModuleLoader__.load({
5558
6305
  '.dig-ref-more{color:var(--dsw-alias-label-tertiary)}',
5559
6306
  '.dig-filters{display:flex;align-items:center;gap:4px;padding:4px 6px;flex:none;min-width:0;overflow-x:auto;overflow-y:hidden;border-bottom:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
5560
6307
  '.dig-filter-text{flex:1 1 90px;min-width:80px;width:auto}',
5561
- '.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}',
6308
+ '.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:var(--dsw-radius-sm,8px);color:var(--dsw-alias-label-secondary);font:inherit;font-weight:500;height:22px;padding:0 4px;cursor:pointer;text-overflow:ellipsis}',
5562
6309
  '.dig-filter-select:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
5563
6310
  '.dig-filter-select{display:inline-flex;align-items:center;gap:3px}',
5564
6311
  '.dig-filter-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:96px}',
5565
6312
  '.dig-filter-on{color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}',
5566
6313
  '.dig-filter-path{flex:0 1 112px;min-width:70px;display:flex}',
5567
- '.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}',
6314
+ '.dig-load-more{margin:6px auto;display:block;padding:3px 12px;border-radius:var(--dsw-radius-sm,8px);border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer}',
5568
6315
  '.dig-load-more:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
5569
6316
  '.dig-empty{padding:14px;text-align:center;color:var(--dsw-alias-label-tertiary);font-weight:500}',
5570
6317
  '.dig-changes{display:flex;flex-direction:column;min-height:0;flex:1}',
@@ -5583,17 +6330,17 @@ window.__ModuleLoader__.load({
5583
6330
  '.dig-file-status-I{color:var(--dsw-alias-label-tertiary)}',
5584
6331
  '.dig-stat-add{color:var(--dsw-alias-state-success-primary);flex:none;font-variant-numeric:tabular-nums}',
5585
6332
  '.dig-stat-del{color:var(--dsw-alias-state-error-primary);flex:none;font-variant-numeric:tabular-nums}',
5586
- '.dig-mini{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border:none;border-radius:5px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;flex:none;padding:0}',
6333
+ '.dig-mini{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border:none;border-radius:var(--dsw-radius-xs,4px);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;flex:none;padding:0}',
5587
6334
  '.dig-row:hover .dig-mini{opacity:1}',
5588
6335
  '.dig-mini:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
5589
6336
  '.dig-commit-box{flex:none;padding:6px;display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
5590
6337
  '.dig-commit-box-compact{flex-direction:row;align-items:center;gap:6px;padding:4px 6px}',
5591
- '.dig-textarea{width:100%;box-sizing:border-box;min-height:50px;resize:vertical;padding:5px 6px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}',
6338
+ '.dig-textarea{width:100%;box-sizing:border-box;min-height:50px;resize:vertical;padding:5px 6px;border-radius:var(--dsw-radius-sm,8px);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}',
5592
6339
  '.dig-textarea::placeholder{color:var(--dsw-alias-label-tertiary)}',
5593
- '.dig-textarea:focus{border-color:var(--dsw-alias-brand-primary)}',
6340
+ '.dig-textarea:focus{border-color:var(--dsw-alias-state-business-primary,var(--dsw-alias-brand-primary))}',
5594
6341
  '.dig-commit-actions{display:flex;align-items:center;gap:8px}',
5595
6342
  '.dig-check{display:inline-flex;align-items:center;gap:4px;color:var(--dsw-alias-label-secondary);cursor:pointer}',
5596
- '.dig-btn{padding:3px 10px;border-radius:6px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;cursor:pointer;flex:none}',
6343
+ '.dig-btn{padding:3px 10px;border-radius:var(--dsw-radius-sm,8px);border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-weight:500;cursor:pointer;flex:none}',
5597
6344
  '.dig-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}',
5598
6345
  '.dig-btn:disabled{opacity:.45;cursor:default}',
5599
6346
  '.dig-btn-small{padding:2px 8px;font-size:11px}',
@@ -5619,15 +6366,17 @@ window.__ModuleLoader__.load({
5619
6366
  '.dig-diff-del{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 16%, transparent)}',
5620
6367
  '.dig-diff-hunk{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent)}',
5621
6368
  '.dig-diff-meta{color:var(--dsw-alias-label-tertiary)}',
5622
- '.dig-overlay{position:absolute;inset:0;padding:8px;box-sizing:border-box;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.42));display:flex;align-items:center;justify-content:center;z-index:60}',
5623
- '.dig-dialog{min-width:240px;max-width:min(420px,94%);max-height:100%;overflow:auto;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:14px;display:flex;flex-direction:column;gap:10px;box-shadow:0 12px 32px rgba(0,0,0,.35)}',
6369
+ '.dig-overlay{position:absolute;inset:0;padding:8px;box-sizing:border-box;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.42));-webkit-backdrop-filter:var(--dsw-mask-blur,none);backdrop-filter:var(--dsw-mask-blur,none);display:flex;align-items:center;justify-content:center;z-index:60}',
6370
+ '.dig-dialog{box-sizing:border-box;min-width:min(240px,100%);max-width:min(420px,94%);max-height:100%;overflow:auto;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));border:0;border-radius:var(--dsw-radius-panel,28px);padding:14px;display:flex;flex-direction:column;gap:10px;box-shadow:var(--dsw-elevation-prominent,0 12px 32px rgba(0,0,0,.35))}',
5624
6371
  '.dig-dialog-wide{min-width:min(360px,92%)}',
5625
6372
  '.dig-dialog-title{font-weight:600}',
5626
6373
  '.dig-dialog-text{color:var(--dsw-alias-label-secondary);white-space:pre-wrap}',
5627
6374
  '.dig-dialog-actions{display:flex;justify-content:flex-end;gap:8px}',
5628
6375
  '.dig-dialog-actions .dig-btn-primary{margin-left:0}',
5629
- '.dig-menu{position:absolute;z-index:70;min-width:200px;max-width:calc(100% - 8px);max-height:calc(100% - 8px);overflow:auto;overscroll-behavior:contain;padding:4px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));-webkit-backdrop-filter:var(--dsh-any-blur-card-panels,blur(14px) saturate(1.2));backdrop-filter:var(--dsh-any-blur-card-panels,blur(14px) saturate(1.2));border:1px solid var(--dsw-alias-border-l2);box-shadow:0 10px 28px rgba(0,0,0,.35);display:flex;flex-direction:column}',
5630
- '.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:5px;cursor:pointer;white-space:nowrap;overflow:hidden}',
6376
+ '.dig-menu{position:absolute;z-index:70;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}',
6377
+ '.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}',
6378
+ '.dig-menu-scroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;display:flex;flex-direction:column}',
6379
+ '.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}',
5631
6380
  '.dig-menu-icon{display:inline-flex;flex:none}',
5632
6381
  '.dig-menu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}',
5633
6382
  '.dig-menu-item:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}',
@@ -5666,7 +6415,7 @@ window.__ModuleLoader__.load({
5666
6415
  return typeof unsubscribe === 'function' ? unsubscribe : undefined
5667
6416
  }, [])
5668
6417
  void tick
5669
- return E(Panel, { scope: props.scope, t: props.t, visible: props.visible })
6418
+ return E(Panel, { scope: props.scope, t: props.t, visible: props.visible, dock: props.dock })
5670
6419
  }
5671
6420
 
5672
6421
  function NativePanel(props) {
@@ -5684,7 +6433,11 @@ window.__ModuleLoader__.load({
5684
6433
  }
5685
6434
  }
5686
6435
  const scope = useMemo(() => ({ cwd: cwd, sessionId: sessionId }), [cwd, sessionId])
5687
- return E(LocaleLive, { ctx: props.ctx, scope: scope, t: props.t, visible: true })
6436
+ /* The dock actions need this tab's own identity, which only the native seat
6437
+ binds; the better-sidebar door has no sidebarRight tab id and therefore
6438
+ wires nothing (a panel hosted elsewhere must not float a foreign tab). */
6439
+ const dock = readDockInfo(props.useTabInfo, props.ctx)
6440
+ return E(LocaleLive, { ctx: props.ctx, scope: scope, t: props.t, visible: true, dock: dock })
5688
6441
  }
5689
6442
 
5690
6443
  function apply(ctx) {