dsh-ide-git 0.7.3 → 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/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +762 -23
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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.
|
|
3
|
+
"version": "0.8.0",
|
|
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
|
@@ -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
|
}
|
|
@@ -3821,6 +4249,9 @@ window.__ModuleLoader__.load({
|
|
|
3821
4249
|
+ (item.danger === true ? ' dig-menu-danger' : '')
|
|
3822
4250
|
+ (item.disabled === true ? ' dig-menu-disabled' : '')
|
|
3823
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,
|
|
3824
4255
|
disabled: item.disabled === true,
|
|
3825
4256
|
onClick: (event) => { closeRef.current(); item.run(event) },
|
|
3826
4257
|
},
|
|
@@ -3960,6 +4391,77 @@ window.__ModuleLoader__.load({
|
|
|
3960
4391
|
}, item.label)))
|
|
3961
4392
|
}
|
|
3962
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
|
+
|
|
3963
4465
|
function RepoSelect(props) {
|
|
3964
4466
|
const t = props.t
|
|
3965
4467
|
/* While the workspace is still being scanned the panel used to print "no
|
|
@@ -4572,7 +5074,21 @@ window.__ModuleLoader__.load({
|
|
|
4572
5074
|
const [note, setNote] = useState(null)
|
|
4573
5075
|
const [busy, setBusy] = useState(false)
|
|
4574
5076
|
const [view, setView] = useState('history')
|
|
4575
|
-
|
|
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 })
|
|
4576
5092
|
const [detail, setDetail] = useState(null)
|
|
4577
5093
|
const [selectedHash, setSelectedHash] = useState(null)
|
|
4578
5094
|
const [selectedPath, setSelectedPath] = useState(null)
|
|
@@ -4622,6 +5138,145 @@ window.__ModuleLoader__.load({
|
|
|
4622
5138
|
const compact = size.width > 0 && size.width < COMPACT_MAX_WIDTH
|
|
4623
5139
|
const columns = !compact && size.width >= 600 && size.width >= size.height * 1.15
|
|
4624
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
|
+
|
|
4625
5280
|
// The action rail measures ITSELF, not the panel: how many buttons fit is a
|
|
4626
5281
|
// function of the strip that holds them (a 1200x300 workbench and a 300x900
|
|
4627
5282
|
// sidebar disagree about that). railBox keeps the last measured span; until
|
|
@@ -5074,7 +5729,7 @@ window.__ModuleLoader__.load({
|
|
|
5074
5729
|
onLoadMore: () => { void guard(() => loadCommits(commits.length)) },
|
|
5075
5730
|
})
|
|
5076
5731
|
|
|
5077
|
-
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') },
|
|
5078
5733
|
E('div', { className: 'dig-diff-head' },
|
|
5079
5734
|
E('span', { className: 'dig-mono dig-diff-path' }, selectedPath === null ? '' : selectedPath),
|
|
5080
5735
|
E('button', { type: 'button', className: 'dig-icon-btn', onClick: () => { setPatch(''); setSelectedPath(null) } }, E(Icon, { name: 'close', size: 12 }))),
|
|
@@ -5153,13 +5808,44 @@ window.__ModuleLoader__.load({
|
|
|
5153
5808
|
else if (spec.id === 'fetch') { entry.disabled = remoteReady !== true; entry.run = () => { void run('fetch', { prune: true }) } }
|
|
5154
5809
|
else if (spec.id === 'pull') { entry.disabled = remoteReady !== true || tracked !== true || blocked; entry.run = () => { void run('pull', { mode: 'ff-only' }) } }
|
|
5155
5810
|
else if (spec.id === 'push') { entry.disabled = remoteReady !== true || blocked; entry.run = () => setDialog({ kind: 'push' }) }
|
|
5156
|
-
if (
|
|
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
|
|
5157
5841
|
return entry
|
|
5158
5842
|
})
|
|
5159
5843
|
|
|
5160
5844
|
const railOrdered = railConfig.order
|
|
5161
5845
|
.map((id) => railActions.find((action) => action.id === id))
|
|
5162
|
-
|
|
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)
|
|
5163
5849
|
const railVisible = railOrdered.filter((action) => railConfig.hidden.indexOf(action.id) < 0)
|
|
5164
5850
|
|
|
5165
5851
|
const applyRail = (next) => { setRailConfig(next); writeRailConfig(next) }
|
|
@@ -5211,6 +5897,8 @@ window.__ModuleLoader__.load({
|
|
|
5211
5897
|
key: action.id,
|
|
5212
5898
|
type: 'button',
|
|
5213
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',
|
|
5214
5902
|
title: action.disabled === true ? action.label + ' · ' + reason : action.label,
|
|
5215
5903
|
disabled: action.disabled === true,
|
|
5216
5904
|
onClick: action.run,
|
|
@@ -5246,18 +5934,24 @@ window.__ModuleLoader__.load({
|
|
|
5246
5934
|
|
|
5247
5935
|
/* ---------- body per chrome ---------- */
|
|
5248
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
|
+
|
|
5249
5943
|
let body
|
|
5250
5944
|
if (repoState === null) {
|
|
5251
5945
|
/* The scan is still running: a neutral loading state, never the
|
|
5252
5946
|
"no repository found" verdict (that text is reserved for a scan
|
|
5253
5947
|
that actually came back empty). */
|
|
5254
|
-
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')))
|
|
5255
5949
|
} else if (repoRoot === null) {
|
|
5256
|
-
body = E('div', { className: 'dig-body' }, E(RepoPicker, {
|
|
5950
|
+
body = E('div', { className: 'dig-body', ref: bodyRef }, E(RepoPicker, {
|
|
5257
5951
|
t: t, cwd: cwd === undefined ? '' : cwd, repos: repoOptions, onPick: pickRepo,
|
|
5258
5952
|
}))
|
|
5259
5953
|
} else if (compact) {
|
|
5260
|
-
body = E('div', { className: 'dig-body dig-body-compact' },
|
|
5954
|
+
body = E('div', { className: 'dig-body dig-body-compact', ref: bodyRef },
|
|
5261
5955
|
E('div', { className: 'dig-compact-bar' },
|
|
5262
5956
|
E(Segmented, {
|
|
5263
5957
|
value: view === 'detail' ? 'history' : view,
|
|
@@ -5268,19 +5962,34 @@ window.__ModuleLoader__.load({
|
|
|
5268
5962
|
],
|
|
5269
5963
|
}),
|
|
5270
5964
|
E('span', { className: 'dig-topbar-spacer' }),
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
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))
|
|
5274
5979
|
} else if (columns) {
|
|
5275
|
-
body = E('div', { className: 'dig-body dig-body-columns' },
|
|
5276
|
-
treeOpen ? E('div', { className: 'dig-pane dig-pane-tree' }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
|
|
5277
|
-
|
|
5278
|
-
E('div', { className: 'dig-pane dig-pane-
|
|
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))
|
|
5279
5986
|
} else {
|
|
5280
|
-
body = E('div', { className: 'dig-body dig-body-stack' },
|
|
5281
|
-
treeOpen ? E('div', { className: 'dig-pane dig-pane-tree-stack' }, E(BranchTree, { t: t, branches: branches, favorites: favorites, onCheckout: checkout, onBranchMenu: branchMenu })) : null,
|
|
5282
|
-
|
|
5283
|
-
E('div', { className: 'dig-pane dig-pane-
|
|
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))
|
|
5284
5993
|
}
|
|
5285
5994
|
|
|
5286
5995
|
/* ---------- overlays ---------- */
|
|
@@ -5406,7 +6115,14 @@ window.__ModuleLoader__.load({
|
|
|
5406
6115
|
// narrow ones (the native right sidebar) get the same actions as one
|
|
5407
6116
|
// horizontal row under the repo/branch bar, and every action lives in
|
|
5408
6117
|
// exactly one place.
|
|
5409
|
-
|
|
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,
|
|
5410
6126
|
columns === true || repoRoot === null ? null : renderRail(false),
|
|
5411
6127
|
banner, noteBanner,
|
|
5412
6128
|
E('div', { className: 'dig-shell' },
|
|
@@ -5470,6 +6186,11 @@ window.__ModuleLoader__.load({
|
|
|
5470
6186
|
'.dig-tone-violet{color:#b083f0}',
|
|
5471
6187
|
'.dig-tone-cyan{color:#59b0d6}',
|
|
5472
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}',
|
|
5473
6194
|
'.dig-toasts{position:absolute;right:8px;bottom:8px;display:flex;flex-direction:column;gap:6px;z-index:80;max-width:min(340px,92%)}',
|
|
5474
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))}',
|
|
5475
6196
|
'.dig-toast-icon{display:inline-flex;flex:none;color:var(--dsw-alias-label-secondary)}',
|
|
@@ -5499,6 +6220,20 @@ window.__ModuleLoader__.load({
|
|
|
5499
6220
|
'.dig-pane-changes-stack{max-height:46%;flex:none;border-bottom:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
|
|
5500
6221
|
'.dig-compact-bar{display:flex;align-items:center;gap:6px;padding:4px 6px;flex:none}',
|
|
5501
6222
|
'.dig-compact-tree{max-height:42%;flex:none;border-bottom:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
|
|
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}',
|
|
5502
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}',
|
|
5503
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}',
|
|
5504
6239
|
'.dig-seg-item:hover{color:var(--dsw-alias-label-primary)}',
|
|
@@ -5632,13 +6367,13 @@ window.__ModuleLoader__.load({
|
|
|
5632
6367
|
'.dig-diff-hunk{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent)}',
|
|
5633
6368
|
'.dig-diff-meta{color:var(--dsw-alias-label-tertiary)}',
|
|
5634
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}',
|
|
5635
|
-
'.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: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))}',
|
|
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))}',
|
|
5636
6371
|
'.dig-dialog-wide{min-width:min(360px,92%)}',
|
|
5637
6372
|
'.dig-dialog-title{font-weight:600}',
|
|
5638
6373
|
'.dig-dialog-text{color:var(--dsw-alias-label-secondary);white-space:pre-wrap}',
|
|
5639
6374
|
'.dig-dialog-actions{display:flex;justify-content:flex-end;gap:8px}',
|
|
5640
6375
|
'.dig-dialog-actions .dig-btn-primary{margin-left:0}',
|
|
5641
|
-
'.dig-menu{position:absolute;z-index:70;box-sizing:border-box;min-width:200px;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}',
|
|
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}',
|
|
5642
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}',
|
|
5643
6378
|
'.dig-menu-scroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;display:flex;flex-direction:column}',
|
|
5644
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}',
|
|
@@ -5680,7 +6415,7 @@ window.__ModuleLoader__.load({
|
|
|
5680
6415
|
return typeof unsubscribe === 'function' ? unsubscribe : undefined
|
|
5681
6416
|
}, [])
|
|
5682
6417
|
void tick
|
|
5683
|
-
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 })
|
|
5684
6419
|
}
|
|
5685
6420
|
|
|
5686
6421
|
function NativePanel(props) {
|
|
@@ -5698,7 +6433,11 @@ window.__ModuleLoader__.load({
|
|
|
5698
6433
|
}
|
|
5699
6434
|
}
|
|
5700
6435
|
const scope = useMemo(() => ({ cwd: cwd, sessionId: sessionId }), [cwd, sessionId])
|
|
5701
|
-
|
|
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 })
|
|
5702
6441
|
}
|
|
5703
6442
|
|
|
5704
6443
|
function apply(ctx) {
|