dsh-ide-git 0.23.0 → 0.25.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/README.md +1 -0
- package/README_EN.md +1 -0
- package/dsh.plugin.json +1 -1
- package/package.json +2 -1
- package/src/client.js +358 -13
- package/src/host/commit-message.js +354 -0
- package/src/host/config.js +71 -0
- package/src/host/core.js +121 -0
- package/src/host/git.js +423 -0
- package/src/host/http.js +237 -0
- package/src/host/read.js +348 -0
- package/src/host/repos.js +222 -0
- package/src/host/undo.js +207 -0
- package/src/host/write.js +551 -0
- package/src/index.js +11 -2325
package/src/client.js
CHANGED
|
@@ -434,6 +434,10 @@ window.__ModuleLoader__.load({
|
|
|
434
434
|
'prompt.newTag': '新标签名',
|
|
435
435
|
'prompt.fromHead': '当前 HEAD',
|
|
436
436
|
'error.dismiss': '关闭',
|
|
437
|
+
'error.pushRejected': '推送被拒绝:远程分支上有你本地没有的提交。请先「同步」(拉取并推送),或拉取合并后再推送。',
|
|
438
|
+
'error.pullDiverged': '本地和远程各有新提交,无法快进。请用「拉取」合并,或改用「拉取(变基)」。',
|
|
439
|
+
'error.pullConflict': '拉取时发生冲突:请在「变更」里解决冲突文件并提交,然后继续。',
|
|
440
|
+
'error.pullDirty': '拉取会覆盖本地未提交的更改:请先提交或贮藏这些更改。',
|
|
437
441
|
'status.loading': '正在读取...',
|
|
438
442
|
'status.busy': '处理中...',
|
|
439
443
|
'counts.staged': '已暂存 {n}',
|
|
@@ -802,6 +806,10 @@ window.__ModuleLoader__.load({
|
|
|
802
806
|
'prompt.newTag': 'New tag name',
|
|
803
807
|
'prompt.fromHead': 'current HEAD',
|
|
804
808
|
'error.dismiss': 'Dismiss',
|
|
809
|
+
'error.pushRejected': 'Push rejected: the remote branch has commits you do not have. Sync (pull and push), or pull (merge) and push again.',
|
|
810
|
+
'error.pullDiverged': 'Local and remote have both moved, so a fast-forward is impossible. Pull with merge, or use Pull (Rebase).',
|
|
811
|
+
'error.pullConflict': 'Pull stopped on conflicts: resolve the conflicted files under Changes, then commit.',
|
|
812
|
+
'error.pullDirty': 'Pull would overwrite uncommitted local changes. Commit or stash them first.',
|
|
805
813
|
'status.loading': 'Reading...',
|
|
806
814
|
'status.busy': 'Working...',
|
|
807
815
|
'counts.staged': 'Staged {n}',
|
|
@@ -1178,6 +1186,10 @@ window.__ModuleLoader__.load({
|
|
|
1178
1186
|
'prompt.newTag': '新標籤名',
|
|
1179
1187
|
'prompt.fromHead': '當前 HEAD',
|
|
1180
1188
|
'error.dismiss': '關閉',
|
|
1189
|
+
'error.pushRejected': '推送被拒絕:遠端分支有啲你本地冇嘅提交。請先同步(拉取並推送),或者拉取合併之後再推送。',
|
|
1190
|
+
'error.pullDiverged': '本地同遠端各有新提交,唔可以快進。請用「拉取」合併,或者改用「拉取(變基)」。',
|
|
1191
|
+
'error.pullConflict': '拉取時有衝突:喺「變更」入面解決衝突檔案並提交,然後繼續。',
|
|
1192
|
+
'error.pullDirty': '拉取會覆蓋本地未提交嘅改動:請先提交或者貯藏呢啲改動。',
|
|
1181
1193
|
'status.loading': '讀取中...',
|
|
1182
1194
|
'status.busy': '處理中...',
|
|
1183
1195
|
'counts.staged': '已暫存 {n}',
|
|
@@ -1546,6 +1558,10 @@ window.__ModuleLoader__.load({
|
|
|
1546
1558
|
'prompt.newTag': '新標籤名稱',
|
|
1547
1559
|
'prompt.fromHead': '目前 HEAD',
|
|
1548
1560
|
'error.dismiss': '關閉',
|
|
1561
|
+
'error.pushRejected': '推送被拒絕:遠端分支上有你本機沒有的提交。請先同步(拉取並推送),或拉取合併後再推送。',
|
|
1562
|
+
'error.pullDiverged': '本機與遠端各有新提交,無法快進。請用「拉取」合併,或改用「拉取(變基)」。',
|
|
1563
|
+
'error.pullConflict': '拉取時發生衝突:請在「變更」中解決衝突檔案並提交,然後繼續。',
|
|
1564
|
+
'error.pullDirty': '拉取會覆蓋本機未提交的變更:請先提交或貯藏這些變更。',
|
|
1549
1565
|
'status.loading': '讀取中...',
|
|
1550
1566
|
'status.busy': '處理中...',
|
|
1551
1567
|
'counts.staged': '已暫存 {n}',
|
|
@@ -1914,6 +1930,10 @@ window.__ModuleLoader__.load({
|
|
|
1914
1930
|
'prompt.newTag': '新標籤名',
|
|
1915
1931
|
'prompt.fromHead': '當前 HEAD',
|
|
1916
1932
|
'error.dismiss': '關閉',
|
|
1933
|
+
'error.pushRejected': '推送被拒絕:遠端分支有啲你本地冇嘅提交。請先同步(拉取並推送),或者拉取合併之後再推送。',
|
|
1934
|
+
'error.pullDiverged': '本地同遠端各有新提交,唔可以快進。請用「拉取」合併,或者改用「拉取(變基)」。',
|
|
1935
|
+
'error.pullConflict': '拉取時有衝突:喺「變更」入面解決衝突檔案並提交,然後繼續。',
|
|
1936
|
+
'error.pullDirty': '拉取會覆蓋本地未提交嘅改動:請先提交或者貯藏呢啲改動。',
|
|
1917
1937
|
'status.loading': '讀取中...',
|
|
1918
1938
|
'status.busy': '處理中...',
|
|
1919
1939
|
'counts.staged': '已暫存 {n}',
|
|
@@ -2282,6 +2302,10 @@ window.__ModuleLoader__.load({
|
|
|
2282
2302
|
'prompt.newTag': '新しいタグ名',
|
|
2283
2303
|
'prompt.fromHead': '現在の HEAD',
|
|
2284
2304
|
'error.dismiss': '閉じる',
|
|
2305
|
+
'error.pushRejected': 'プッシュが拒否されました:リモートブランチにローカルにないコミットがあります。先に同期(プルしてプッシュ)するか、プル(マージ)してから再プッシュしてください。',
|
|
2306
|
+
'error.pullDiverged': 'ローカルとリモートの両方に新しいコミットがあり、fast-forward できません。「プル」(マージ)または「プル(リベース)」を使ってください。',
|
|
2307
|
+
'error.pullConflict': 'プルで競合が発生しました。「変更」で競合ファイルを解決してコミットしてから続行してください。',
|
|
2308
|
+
'error.pullDirty': 'プルするとローカルの未コミット変更が上書きされます。先にコミットするかスタッシュしてください。',
|
|
2285
2309
|
'status.loading': '読み込み中...',
|
|
2286
2310
|
'status.busy': '処理中...',
|
|
2287
2311
|
'counts.staged': 'ステージ済み {n}',
|
|
@@ -2650,6 +2674,10 @@ window.__ModuleLoader__.load({
|
|
|
2650
2674
|
'prompt.newTag': '새 태그 이름',
|
|
2651
2675
|
'prompt.fromHead': '현재 HEAD',
|
|
2652
2676
|
'error.dismiss': '닫기',
|
|
2677
|
+
'error.pushRejected': '푸시가 거부되었습니다: 원격 브랜치에 로컬에 없는 커밋이 있습니다. 먼저 동기화(풀 후 푸시)하거나 풀(병합)한 뒤 다시 푸시하세요.',
|
|
2678
|
+
'error.pullDiverged': '로컬과 원격에 각각 새 커밋이 있어 fast-forward할 수 없습니다. 풀(병합) 또는 풀(리베이스)을 사용하세요.',
|
|
2679
|
+
'error.pullConflict': '풀 중 충돌이 발생했습니다. "변경"에서 충돌 파일을 해결하고 커밋한 뒤 계속하세요.',
|
|
2680
|
+
'error.pullDirty': '풀을 하면 로컬의 커밋되지 않은 변경이 덮어써집니다. 먼저 커밋하거나 스태시하세요.',
|
|
2653
2681
|
'status.loading': '읽는 중...',
|
|
2654
2682
|
'status.busy': '처리 중...',
|
|
2655
2683
|
'counts.staged': '스테이지됨 {n}',
|
|
@@ -3018,6 +3046,10 @@ window.__ModuleLoader__.load({
|
|
|
3018
3046
|
'prompt.newTag': 'Name des neuen Tags',
|
|
3019
3047
|
'prompt.fromHead': 'aktuelles HEAD',
|
|
3020
3048
|
'error.dismiss': 'Schließen',
|
|
3049
|
+
'error.pushRejected': 'Push abgelehnt: Der Remote-Branch enthält Commits, die lokal fehlen. Zuerst synchronisieren (Pull und Push) oder pullen (Merge) und dann erneut pushen.',
|
|
3050
|
+
'error.pullDiverged': 'Lokal und remote sind beide weitergezogen; ein Fast-Forward ist unmöglich. Mit Merge pullen oder Pull (Rebase) verwenden.',
|
|
3051
|
+
'error.pullConflict': 'Pull wurde wegen Konflikten gestoppt: Konfliktdateien unter „Änderungen“ auflösen, committen und dann fortfahren.',
|
|
3052
|
+
'error.pullDirty': 'Pull würde lokale, nicht committete Änderungen überschreiben. Zuerst committen oder stashen.',
|
|
3021
3053
|
'status.loading': 'Wird gelesen...',
|
|
3022
3054
|
'status.busy': 'Arbeitet...',
|
|
3023
3055
|
'counts.staged': 'Vorgemerkt {n}',
|
|
@@ -3386,6 +3418,10 @@ window.__ModuleLoader__.load({
|
|
|
3386
3418
|
'prompt.newTag': 'Nom de la nouvelle étiquette',
|
|
3387
3419
|
'prompt.fromHead': 'HEAD courant',
|
|
3388
3420
|
'error.dismiss': 'Fermer',
|
|
3421
|
+
'error.pushRejected': 'Push refusé : la branche distante contient des commits absents en local. Synchronisez (pull puis push) ou faites un pull (merge), puis poussez à nouveau.',
|
|
3422
|
+
'error.pullDiverged': 'Local et distant ont tous deux avancé ; le fast-forward est impossible. Faites un pull (merge) ou un pull (rebase).',
|
|
3423
|
+
'error.pullConflict': 'Le pull s\'est arrêté sur des conflits : résolvez les fichiers en conflit dans « Modifications », puis validez.',
|
|
3424
|
+
'error.pullDirty': 'Le pull écraserait des modifications locales non validées. Validez-les ou remisez-les d\'abord.',
|
|
3389
3425
|
'status.loading': 'Lecture...',
|
|
3390
3426
|
'status.busy': 'En cours...',
|
|
3391
3427
|
'counts.staged': 'Indexé {n}',
|
|
@@ -3754,6 +3790,10 @@ window.__ModuleLoader__.load({
|
|
|
3754
3790
|
'prompt.newTag': 'Имя нового тега',
|
|
3755
3791
|
'prompt.fromHead': 'текущий HEAD',
|
|
3756
3792
|
'error.dismiss': 'Закрыть',
|
|
3793
|
+
'error.pushRejected': 'Push отклонён: в удалённой ветке есть коммиты, которых нет локально. Сначала синхронизируйтесь (pull и push) или выполните pull (merge), затем повторите push.',
|
|
3794
|
+
'error.pullDiverged': 'Локальная и удалённая ветки разошлись; fast-forward невозможен. Используйте pull (merge) или pull (rebase).',
|
|
3795
|
+
'error.pullConflict': 'Pull остановился из-за конфликтов: разрешите конфликтные файлы в «Изменениях» и закоммитьте их.',
|
|
3796
|
+
'error.pullDirty': 'Pull перезапишет незакоммиченные локальные изменения. Сначала закоммитьте или спрячьте их.',
|
|
3757
3797
|
'status.loading': 'Чтение...',
|
|
3758
3798
|
'status.busy': 'Работа...',
|
|
3759
3799
|
'counts.staged': 'В индексе {n}',
|
|
@@ -4122,6 +4162,10 @@ window.__ModuleLoader__.load({
|
|
|
4122
4162
|
'prompt.newTag': 'Nome da nova tag',
|
|
4123
4163
|
'prompt.fromHead': 'HEAD atual',
|
|
4124
4164
|
'error.dismiss': 'Fechar',
|
|
4165
|
+
'error.pushRejected': 'Push rejeitado: o branch remoto tem commits que não existem localmente. Sincronize (pull e push) ou faça pull (merge) e envie novamente.',
|
|
4166
|
+
'error.pullDiverged': 'Local e remoto avançaram; o fast-forward é impossível. Use pull (merge) ou pull (rebase).',
|
|
4167
|
+
'error.pullConflict': 'O pull parou em conflitos: resolva os arquivos em conflito em "Alterações" e faça o commit.',
|
|
4168
|
+
'error.pullDirty': 'O pull sobrescreveria alterações locais não commitadas. Faça commit ou stash primeiro.',
|
|
4125
4169
|
'status.loading': 'Lendo...',
|
|
4126
4170
|
'status.busy': 'Trabalhando...',
|
|
4127
4171
|
'counts.staged': 'Preparado {n}',
|
|
@@ -4490,6 +4534,10 @@ window.__ModuleLoader__.load({
|
|
|
4490
4534
|
'prompt.newTag': 'Nome del nuovo tag',
|
|
4491
4535
|
'prompt.fromHead': 'HEAD corrente',
|
|
4492
4536
|
'error.dismiss': 'Chiudi',
|
|
4537
|
+
'error.pushRejected': 'Push rifiutato: il branch remoto contiene commit assenti in locale. Sincronizza (pull e push) oppure esegui pull (merge) e riprova.',
|
|
4538
|
+
'error.pullDiverged': 'Locale e remoto sono avanzati entrambi; il fast-forward è impossibile. Usa pull (merge) o pull (rebase).',
|
|
4539
|
+
'error.pullConflict': 'Il pull si è fermato per conflitti: risolvi i file in conflitto in "Modifiche", poi esegui il commit.',
|
|
4540
|
+
'error.pullDirty': 'Il pull sovrascriverebbe modifiche locali non committate. Esegui prima commit o stash.',
|
|
4493
4541
|
'status.loading': 'Lettura...',
|
|
4494
4542
|
'status.busy': 'In corso...',
|
|
4495
4543
|
'counts.staged': 'In stage {n}',
|
|
@@ -4858,6 +4906,10 @@ window.__ModuleLoader__.load({
|
|
|
4858
4906
|
'prompt.newTag': 'Naam van de nieuwe tag',
|
|
4859
4907
|
'prompt.fromHead': 'huidige HEAD',
|
|
4860
4908
|
'error.dismiss': 'Sluiten',
|
|
4909
|
+
'error.pushRejected': 'Push geweigerd: de remote branch heeft commits die lokaal ontbreken. Synchroniseer (pull en push) of pull (merge) en push daarna opnieuw.',
|
|
4910
|
+
'error.pullDiverged': 'Lokaal en remote zijn beide verdergegaan; fast-forward is onmogelijk. Gebruik pull (merge) of pull (rebase).',
|
|
4911
|
+
'error.pullConflict': 'Pull stopte door conflicten: los de conflictbestanden op onder "Wijzigingen" en commit daarna.',
|
|
4912
|
+
'error.pullDirty': 'Pull zou niet-gecommitte lokale wijzigingen overschrijven. Commit of stash ze eerst.',
|
|
4861
4913
|
'status.loading': 'Lezen...',
|
|
4862
4914
|
'status.busy': 'Bezig...',
|
|
4863
4915
|
'counts.staged': 'Gestaged {n}',
|
|
@@ -5226,6 +5278,10 @@ window.__ModuleLoader__.load({
|
|
|
5226
5278
|
'prompt.newTag': 'Nazwa nowego tagu',
|
|
5227
5279
|
'prompt.fromHead': 'bieżący HEAD',
|
|
5228
5280
|
'error.dismiss': 'Zamknij',
|
|
5281
|
+
'error.pushRejected': 'Push odrzucony: zdalna gałąź ma commity, których nie ma lokalnie. Najpierw zsynchronizuj (pull i push) albo wykonaj pull (merge) i ponów push.',
|
|
5282
|
+
'error.pullDiverged': 'Lokalna i zdalna gałąź rozeszły się; fast-forward jest niemożliwy. Użyj pull (merge) lub pull (rebase).',
|
|
5283
|
+
'error.pullConflict': 'Pull zatrzymał się na konfliktach: rozwiąż pliki konfliktów w „Zmianach” i zatwierdź zmiany.',
|
|
5284
|
+
'error.pullDirty': 'Pull nadpisałby niezatwierdzone zmiany lokalne. Najpierw je zatwierdź lub schowaj.',
|
|
5229
5285
|
'status.loading': 'Wczytywanie...',
|
|
5230
5286
|
'status.busy': 'Praca...',
|
|
5231
5287
|
'counts.staged': 'W indeksie {n}',
|
|
@@ -5594,6 +5650,10 @@ window.__ModuleLoader__.load({
|
|
|
5594
5650
|
'prompt.newTag': 'Namn på ny tagg',
|
|
5595
5651
|
'prompt.fromHead': 'aktuell HEAD',
|
|
5596
5652
|
'error.dismiss': 'Stäng',
|
|
5653
|
+
'error.pushRejected': 'Push avvisades: fjärrgrenen har commits som saknas lokalt. Synkronisera (pull och push) eller gör pull (merge) och pusha igen.',
|
|
5654
|
+
'error.pullDiverged': 'Lokalt och fjärr har båda flyttats; fast-forward är omöjligt. Använd pull (merge) eller pull (rebase).',
|
|
5655
|
+
'error.pullConflict': 'Pull stoppade på konflikter: lös konfliktfilerna under "Ändringar" och commit:a.',
|
|
5656
|
+
'error.pullDirty': 'Pull skulle skriva över lokala ej commit:ade ändringar. Commit:a eller stash:a dem först.',
|
|
5597
5657
|
'status.loading': 'Läser...',
|
|
5598
5658
|
'status.busy': 'Arbetar...',
|
|
5599
5659
|
'counts.staged': 'Klädda {n}',
|
|
@@ -5962,6 +6022,10 @@ window.__ModuleLoader__.load({
|
|
|
5962
6022
|
'prompt.newTag': 'Yeni etiket adı',
|
|
5963
6023
|
'prompt.fromHead': 'geçerli HEAD',
|
|
5964
6024
|
'error.dismiss': 'Kapat',
|
|
6025
|
+
'error.pushRejected': 'Push reddedildi: uzak dalda yerelde olmayan commit\'ler var. Önce senkronize edin (pull ve push) veya pull (merge) yapıp yeniden push\'layın.',
|
|
6026
|
+
'error.pullDiverged': 'Yerel ve uzak dal ikisi de ilerledi; fast-forward mümkün değil. Pull (merge) veya pull (rebase) kullanın.',
|
|
6027
|
+
'error.pullConflict': 'Pull çakışmalarda durdu: "Değişiklikler" altında çakışan dosyaları çözüp commit\'leyin.',
|
|
6028
|
+
'error.pullDirty': 'Pull, commit\'lenmemiş yerel değişikliklerin üzerine yazar. Önce commit\'leyin veya stash\'leyin.',
|
|
5965
6029
|
'status.loading': 'Okunuyor...',
|
|
5966
6030
|
'status.busy': 'Çalışıyor...',
|
|
5967
6031
|
'counts.staged': 'Hazırlanan {n}',
|
|
@@ -6330,6 +6394,10 @@ window.__ModuleLoader__.load({
|
|
|
6330
6394
|
'prompt.newTag': 'Nama tag baru',
|
|
6331
6395
|
'prompt.fromHead': 'HEAD saat ini',
|
|
6332
6396
|
'error.dismiss': 'Tutup',
|
|
6397
|
+
'error.pushRejected': 'Push ditolak: branch remote memiliki commit yang tidak ada di lokal. Sinkronkan (pull lalu push) atau pull (merge) lalu push lagi.',
|
|
6398
|
+
'error.pullDiverged': 'Lokal dan remote sama-sama maju; fast-forward tidak mungkin. Gunakan pull (merge) atau pull (rebase).',
|
|
6399
|
+
'error.pullConflict': 'Pull berhenti karena konflik: selesaikan file konflik di "Perubahan", lalu commit.',
|
|
6400
|
+
'error.pullDirty': 'Pull akan menimpa perubahan lokal yang belum di-commit. Commit atau stash dulu.',
|
|
6333
6401
|
'status.loading': 'Membaca...',
|
|
6334
6402
|
'status.busy': 'Bekerja...',
|
|
6335
6403
|
'counts.staged': 'Ter-stage {n}',
|
|
@@ -6698,6 +6766,10 @@ window.__ModuleLoader__.load({
|
|
|
6698
6766
|
'prompt.newTag': 'Tên thẻ mới',
|
|
6699
6767
|
'prompt.fromHead': 'HEAD hiện tại',
|
|
6700
6768
|
'error.dismiss': 'Đóng',
|
|
6769
|
+
'error.pushRejected': 'Push bị từ chối: nhánh remote có commit mà local chưa có. Hãy đồng bộ (pull rồi push) hoặc pull (merge) rồi push lại.',
|
|
6770
|
+
'error.pullDiverged': 'Local và remote đều đã tiến lên; không thể fast-forward. Dùng pull (merge) hoặc pull (rebase).',
|
|
6771
|
+
'error.pullConflict': 'Pull dừng do xung đột: giải quyết các tệp xung đột trong "Thay đổi" rồi commit.',
|
|
6772
|
+
'error.pullDirty': 'Pull sẽ ghi đè thay đổi local chưa commit. Hãy commit hoặc stash trước.',
|
|
6701
6773
|
'status.loading': 'Đang đọc...',
|
|
6702
6774
|
'status.busy': 'Đang xử lý...',
|
|
6703
6775
|
'counts.staged': 'Đã stage {n}',
|
|
@@ -7066,6 +7138,10 @@ window.__ModuleLoader__.load({
|
|
|
7066
7138
|
'prompt.newTag': 'اسم الوسم الجديد',
|
|
7067
7139
|
'prompt.fromHead': 'HEAD الحالي',
|
|
7068
7140
|
'error.dismiss': 'إغلاق',
|
|
7141
|
+
'error.pushRejected': 'تم رفض الدفع: الفرع البعيد يحتوي على commits غير موجودة محليًا. زامن أولًا (pull ثم push) أو اسحب (merge) ثم أعد الدفع.',
|
|
7142
|
+
'error.pullDiverged': 'تقدّم الفرعان المحلي والبعيد؛ لا يمكن fast-forward. استخدم pull (merge) أو pull (rebase).',
|
|
7143
|
+
'error.pullConflict': 'توقف pull بسبب تعارضات: حلّ ملفات التعارض في "التغييرات" ثم commit.',
|
|
7144
|
+
'error.pullDirty': 'سيؤدي pull إلى الكتابة فوق تغييرات محلية غير مثبتة. ثبّتها أو خزّنها أولًا.',
|
|
7069
7145
|
'status.loading': 'جارٍ القراءة...',
|
|
7070
7146
|
'status.busy': 'جارٍ العمل...',
|
|
7071
7147
|
'counts.staged': 'مُجهَّز {n}',
|
|
@@ -7434,6 +7510,10 @@ window.__ModuleLoader__.load({
|
|
|
7434
7510
|
'prompt.newTag': 'नए टैग का नाम',
|
|
7435
7511
|
'prompt.fromHead': 'वर्तमान HEAD',
|
|
7436
7512
|
'error.dismiss': 'बंद करें',
|
|
7513
|
+
'error.pushRejected': 'पुश अस्वीकृत: रिमोट ब्रांच में ऐसे commits हैं जो लोकल में नहीं हैं। पहले सिंक करें (pull फिर push) या pull (merge) करके फिर से push करें।',
|
|
7514
|
+
'error.pullDiverged': 'लोकल और रिमोट दोनों आगे बढ़े हैं; fast-forward संभव नहीं। pull (merge) या pull (rebase) इस्तेमाल करें।',
|
|
7515
|
+
'error.pullConflict': 'pull conflicts पर रुक गया: "परिवर्तन" में conflict फ़ाइलें हल करें, फिर commit करें।',
|
|
7516
|
+
'error.pullDirty': 'pull लोकल की uncommitted changes को overwrite करेगा। पहले commit या stash करें।',
|
|
7437
7517
|
'status.loading': 'पढ़ा जा रहा है...',
|
|
7438
7518
|
'status.busy': 'कार्य जारी...',
|
|
7439
7519
|
'counts.staged': 'स्टेज्ड {n}',
|
|
@@ -7802,6 +7882,10 @@ window.__ModuleLoader__.load({
|
|
|
7802
7882
|
'prompt.newTag': 'ชื่อแท็กใหม่',
|
|
7803
7883
|
'prompt.fromHead': 'HEAD ปัจจุบัน',
|
|
7804
7884
|
'error.dismiss': 'ปิด',
|
|
7885
|
+
'error.pushRejected': 'Push ถูกปฏิเสธ: สาขา remote มี commit ที่ยังไม่มีในเครื่องนี้ ให้ sync (pull แล้ว push) หรือ pull (merge) แล้ว push อีกครั้ง',
|
|
7886
|
+
'error.pullDiverged': 'ทั้ง local และ remote มี commit ใหม่; fast-forward ไม่ได้ ใช้ pull (merge) หรือ pull (rebase)',
|
|
7887
|
+
'error.pullConflict': 'Pull หยุดเพราะมี conflict: แก้ไฟล์ที่ขัดแย้งใน "การเปลี่ยนแปลง" แล้ว commit',
|
|
7888
|
+
'error.pullDirty': 'Pull จะเขียนทับการเปลี่ยนแปลง local ที่ยังไม่ commit: ให้ commit หรือ stash ก่อน',
|
|
7805
7889
|
'status.loading': 'กำลังอ่าน...',
|
|
7806
7890
|
'status.busy': 'กำลังทำงาน...',
|
|
7807
7891
|
'counts.staged': 'สเตจแล้ว {n}',
|
|
@@ -13055,6 +13139,9 @@ window.__ModuleLoader__.load({
|
|
|
13055
13139
|
const [commits, setCommits] = useState([])
|
|
13056
13140
|
const [hasMore, setHasMore] = useState(false)
|
|
13057
13141
|
const [error, setError] = useState(null)
|
|
13142
|
+
/* Named failure code (push-rejected / pull-conflict / ...), kept beside
|
|
13143
|
+
the raw message so the banner can lead with an actionable sentence. */
|
|
13144
|
+
const [errorCode, setErrorCode] = useState(null)
|
|
13058
13145
|
const [note, setNote] = useState(null)
|
|
13059
13146
|
const [busy, setBusy] = useState(false)
|
|
13060
13147
|
const [view, setView] = useState('history')
|
|
@@ -13398,10 +13485,13 @@ window.__ModuleLoader__.load({
|
|
|
13398
13485
|
const guard = useCallback(async (work) => {
|
|
13399
13486
|
setBusy(true)
|
|
13400
13487
|
setError(null)
|
|
13488
|
+
setErrorCode(null)
|
|
13401
13489
|
try {
|
|
13402
13490
|
return await work()
|
|
13403
13491
|
} catch (caught) {
|
|
13404
|
-
|
|
13492
|
+
const failure = caught instanceof Error ? caught : new Error(String(caught))
|
|
13493
|
+
setError(failure.message)
|
|
13494
|
+
setErrorCode(typeof failure.code === 'string' ? failure.code : null)
|
|
13405
13495
|
return undefined
|
|
13406
13496
|
} finally {
|
|
13407
13497
|
setBusy(false)
|
|
@@ -13446,7 +13536,7 @@ window.__ModuleLoader__.load({
|
|
|
13446
13536
|
writePaintRepos(cwd, data)
|
|
13447
13537
|
apply(data, readPaintRoot(cwd))
|
|
13448
13538
|
} catch (caught) {
|
|
13449
|
-
if (!cancelled) setError(caught instanceof Error ? caught.message : String(caught))
|
|
13539
|
+
if (!cancelled) { setErrorCode(null); setError(caught instanceof Error ? caught.message : String(caught)) }
|
|
13450
13540
|
}
|
|
13451
13541
|
})()
|
|
13452
13542
|
return () => { cancelled = true }
|
|
@@ -13474,6 +13564,7 @@ window.__ModuleLoader__.load({
|
|
|
13474
13564
|
const data = await request('gitLogs', {})
|
|
13475
13565
|
setGitOutput(Array.isArray(data.entries) ? data.entries : [])
|
|
13476
13566
|
} catch (caught) {
|
|
13567
|
+
setErrorCode(null)
|
|
13477
13568
|
setError(caught instanceof Error ? caught.message : String(caught))
|
|
13478
13569
|
}
|
|
13479
13570
|
}, [request])
|
|
@@ -13699,7 +13790,11 @@ window.__ModuleLoader__.load({
|
|
|
13699
13790
|
|
|
13700
13791
|
const run = useCallback(async (method, payload) => {
|
|
13701
13792
|
const result = await guard(() => request(method, Object.assign({}, base, payload)))
|
|
13702
|
-
|
|
13793
|
+
/* A failed pull/push still changes repository state (merge conflicts,
|
|
13794
|
+
or a merge that landed before the push was rejected), so refresh on
|
|
13795
|
+
those failures too — otherwise the Changes list hides the conflict
|
|
13796
|
+
files the banner is asking the user to resolve. */
|
|
13797
|
+
if (result !== undefined || method === 'pull' || method === 'push') {
|
|
13703
13798
|
await refresh()
|
|
13704
13799
|
/* v0.21.2: the composer pill is a separate tree; without this it only
|
|
13705
13800
|
learned about an in-panel checkout/commit at the next 30s poll. The
|
|
@@ -13760,6 +13855,7 @@ window.__ModuleLoader__.load({
|
|
|
13760
13855
|
const data = await request('diff', Object.assign({}, base, seed))
|
|
13761
13856
|
setPatch(data.patch)
|
|
13762
13857
|
} catch (caught) {
|
|
13858
|
+
setErrorCode(null)
|
|
13763
13859
|
setError(caught instanceof Error ? caught.message : String(caught))
|
|
13764
13860
|
} finally {
|
|
13765
13861
|
setPatchLoading(false)
|
|
@@ -13862,6 +13958,7 @@ window.__ModuleLoader__.load({
|
|
|
13862
13958
|
try {
|
|
13863
13959
|
setDetail(await request('commitDetail', Object.assign({}, base, { hash: commit.hash })))
|
|
13864
13960
|
} catch (caught) {
|
|
13961
|
+
setErrorCode(null)
|
|
13865
13962
|
setError(caught instanceof Error ? caught.message : String(caught))
|
|
13866
13963
|
}
|
|
13867
13964
|
}, [base])
|
|
@@ -14034,6 +14131,7 @@ window.__ModuleLoader__.load({
|
|
|
14034
14131
|
const createWorktree = async (draft) => {
|
|
14035
14132
|
setBusy(true)
|
|
14036
14133
|
setError(null)
|
|
14134
|
+
setErrorCode(null)
|
|
14037
14135
|
try {
|
|
14038
14136
|
await request('worktreeAdd', Object.assign({}, base, draft))
|
|
14039
14137
|
setDialog(null)
|
|
@@ -14049,6 +14147,7 @@ window.__ModuleLoader__.load({
|
|
|
14049
14147
|
const removeWorktree = async (tree, force) => {
|
|
14050
14148
|
setBusy(true)
|
|
14051
14149
|
setError(null)
|
|
14150
|
+
setErrorCode(null)
|
|
14052
14151
|
try {
|
|
14053
14152
|
await request('worktreeRemove', Object.assign({}, base, { path: tree.path, confirm: true, force: force === true }))
|
|
14054
14153
|
setDialog(null)
|
|
@@ -14062,6 +14161,7 @@ window.__ModuleLoader__.load({
|
|
|
14062
14161
|
return message
|
|
14063
14162
|
}
|
|
14064
14163
|
setDialog(null)
|
|
14164
|
+
setErrorCode(null)
|
|
14065
14165
|
setError(message)
|
|
14066
14166
|
return message
|
|
14067
14167
|
} finally {
|
|
@@ -14071,6 +14171,7 @@ window.__ModuleLoader__.load({
|
|
|
14071
14171
|
const pruneWorktrees = async () => {
|
|
14072
14172
|
setBusy(true)
|
|
14073
14173
|
setError(null)
|
|
14174
|
+
setErrorCode(null)
|
|
14074
14175
|
try {
|
|
14075
14176
|
const data = await request('worktreePrune', Object.assign({}, base, { confirm: true }))
|
|
14076
14177
|
setDialog(null)
|
|
@@ -14079,6 +14180,7 @@ window.__ModuleLoader__.load({
|
|
|
14079
14180
|
return data
|
|
14080
14181
|
} catch (caught) {
|
|
14081
14182
|
setDialog(null)
|
|
14183
|
+
setErrorCode(null)
|
|
14082
14184
|
setError(caught instanceof Error ? caught.message : String(caught))
|
|
14083
14185
|
return null
|
|
14084
14186
|
} finally {
|
|
@@ -14259,7 +14361,26 @@ window.__ModuleLoader__.load({
|
|
|
14259
14361
|
].filter((item) => item !== null))
|
|
14260
14362
|
return
|
|
14261
14363
|
}
|
|
14262
|
-
|
|
14364
|
+
/* v0.24.0: the label was always 拉取, but this only ran git fetch.
|
|
14365
|
+
The checked-out branch now takes a real pull (merge, the new
|
|
14366
|
+
default); a non-HEAD branch keeps the safe refspec update, because
|
|
14367
|
+
git pull would merge into HEAD instead of into that branch. */
|
|
14368
|
+
const updateItem = {
|
|
14369
|
+
id: 'update', icon: 'fetch', tone: 'cyan', label: t('action.update'),
|
|
14370
|
+
run: () => {
|
|
14371
|
+
if (entry.head === true) {
|
|
14372
|
+
const upstream = entry.upstream
|
|
14373
|
+
if (upstream === null || upstream === undefined || upstream === '') { void run('fetch', { prune: true }); return }
|
|
14374
|
+
void run('pull', { mode: 'merge' })
|
|
14375
|
+
return
|
|
14376
|
+
}
|
|
14377
|
+
const upstream = entry.upstream
|
|
14378
|
+
if (upstream === null || upstream === undefined || upstream === '') { void run('fetch', { prune: true }); return }
|
|
14379
|
+
const slash = upstream.indexOf('/')
|
|
14380
|
+
const remote = slash >= 0 ? upstream.slice(0, slash) : upstream
|
|
14381
|
+
void run('fetch', { remote: remote, branch: entry.name + ':' + entry.name })
|
|
14382
|
+
},
|
|
14383
|
+
}
|
|
14263
14384
|
const pushItem = { id: 'push', icon: 'push', tone: 'success', label: t('action.push'), disabled: blocked, reason: t('operation.hint'), run: () => setDialog({ kind: 'push' }) }
|
|
14264
14385
|
const items = [
|
|
14265
14386
|
{ id: 'checkout', icon: 'checkout', tone: 'accent', label: t('action.checkout'), disabled: entry.head === true || entry.tag === true || blocked, reason: blocked ? t('operation.hint') : reason, run: () => { void checkout(entry) } },
|
|
@@ -14662,9 +14783,29 @@ window.__ModuleLoader__.load({
|
|
|
14662
14783
|
E('span', { className: 'dig-topbar-spacer' }),
|
|
14663
14784
|
busy ? E('span', { className: 'dig-busy' }, t('status.busy')) : null)
|
|
14664
14785
|
|
|
14786
|
+
/* Named git failures get a sentence that says what to do; the raw git
|
|
14787
|
+
output stays one hover away in the title. The two recoverable ones
|
|
14788
|
+
(push rejected, ff-only divergence) also offer the exact next action:
|
|
14789
|
+
Sync = pull with merge, then push again. */
|
|
14790
|
+
const errorHint = errorCode === 'push-rejected' ? t('error.pushRejected')
|
|
14791
|
+
: errorCode === 'pull-diverged' ? t('error.pullDiverged')
|
|
14792
|
+
: errorCode === 'pull-conflict' ? t('error.pullConflict')
|
|
14793
|
+
: errorCode === 'pull-dirty' ? t('error.pullDirty')
|
|
14794
|
+
: null
|
|
14795
|
+
const syncFromBanner = () => {
|
|
14796
|
+
setError(null)
|
|
14797
|
+
setErrorCode(null)
|
|
14798
|
+
void (async () => {
|
|
14799
|
+
const pulled = await run('pull', { mode: 'merge' })
|
|
14800
|
+
if (pulled !== undefined) await run('push', { confirm: true })
|
|
14801
|
+
})()
|
|
14802
|
+
}
|
|
14665
14803
|
const banner = error === null ? null : E('div', { className: 'dig-banner' },
|
|
14666
|
-
E('span', { className: 'dig-banner-text' }, error),
|
|
14667
|
-
|
|
14804
|
+
E('span', { className: 'dig-banner-text', title: errorHint === null ? undefined : error }, errorHint === null ? error : errorHint),
|
|
14805
|
+
errorHint !== null && (errorCode === 'push-rejected' || errorCode === 'pull-diverged')
|
|
14806
|
+
? E('button', { type: 'button', className: 'dig-link', onClick: syncFromBanner }, t('more.sync'))
|
|
14807
|
+
: null,
|
|
14808
|
+
E('button', { type: 'button', className: 'dig-link', onClick: () => { setError(null); setErrorCode(null) } }, t('error.dismiss')))
|
|
14668
14809
|
|
|
14669
14810
|
const noteBanner = note === null ? null : E('div', { className: 'dig-note' },
|
|
14670
14811
|
E('span', { className: 'dig-banner-text' }, note),
|
|
@@ -14708,7 +14849,12 @@ window.__ModuleLoader__.load({
|
|
|
14708
14849
|
const diffPane = patch === '' && patchLoading === false ? null : E('div', { className: 'dig-diff-pane', 'data-pane': 'diff', style: paneStyle('diff'), onContextMenu: onDiffPaneContextMenu },
|
|
14709
14850
|
E('div', { className: 'dig-diff-head' },
|
|
14710
14851
|
E('span', { className: 'dig-mono dig-diff-path' }, selectedPath === null ? '' : selectedPath),
|
|
14711
|
-
E('button', {
|
|
14852
|
+
E('button', {
|
|
14853
|
+
type: 'button',
|
|
14854
|
+
className: 'dig-icon-btn',
|
|
14855
|
+
title: t('toast.close'),
|
|
14856
|
+
onClick: () => { setPatch(''); setSelectedPath(null); setDiffContext(null) },
|
|
14857
|
+
}, E(Icon, { name: 'close', size: 12 }))),
|
|
14712
14858
|
E(DiffBody, { patch: patch, loading: patchLoading, t: t }))
|
|
14713
14859
|
|
|
14714
14860
|
/* ---------- rail (IDE-style left action strip) ---------- */
|
|
@@ -14856,7 +15002,7 @@ window.__ModuleLoader__.load({
|
|
|
14856
15002
|
] },
|
|
14857
15003
|
null,
|
|
14858
15004
|
/* —— top-level sync (VS Code header row) —— */
|
|
14859
|
-
{ id: 'mm-pull', icon: 'pull', tone: 'accent', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: '
|
|
15005
|
+
{ id: 'mm-pull', icon: 'pull', tone: 'accent', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'merge' }) } },
|
|
14860
15006
|
{ id: 'mm-push', icon: 'push', tone: 'success', label: t('toolbar.push'), disabled: remoteReady !== true || blocked, reason, run: () => setDialog({ kind: 'push' }) },
|
|
14861
15007
|
{ id: 'mm-clone', icon: 'plus', tone: 'secondary', label: t('more.clone'), run: () => setDialog({ kind: 'clone' }) },
|
|
14862
15008
|
{ id: 'mm-checkout', icon: 'checkout', tone: 'accent', label: t('action.checkout') + '...', disabled: otherBranches.length === 0 || blocked, reason, run: (ev) => pickBranchMenu(ev, 'checkout') },
|
|
@@ -14885,12 +15031,12 @@ window.__ModuleLoader__.load({
|
|
|
14885
15031
|
] },
|
|
14886
15032
|
/* —— SCM ▸ Pull, Push ▸ —— */
|
|
14887
15033
|
{ id: 'sub-pullpush', icon: 'pull', tone: 'accent', label: t('more.pullpushGroup'), items: [
|
|
14888
|
-
{ id: 'pp-sync', label: t('more.sync'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void (async () => { await run('pull', { mode: '
|
|
15034
|
+
{ id: 'pp-sync', label: t('more.sync'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void (async () => { await run('pull', { mode: 'merge' }); await run('push', { confirm: true }) })() } },
|
|
14889
15035
|
{ id: 'pp-sync-rebase', label: t('more.syncRebase'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void (async () => { await run('pull', { mode: 'rebase' }); await run('push', { confirm: true }) })() } },
|
|
14890
15036
|
null,
|
|
14891
|
-
{ id: 'pp-pull', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: '
|
|
15037
|
+
{ id: 'pp-pull', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'merge' }) } },
|
|
14892
15038
|
{ id: 'pp-pull-rebase', label: t('more.pullRebase'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'rebase' }) } },
|
|
14893
|
-
{ id: 'pp-pull-from', label: t('more.pullFrom'), disabled: remoteNames.length === 0, reason, run: pickRemote((name) => { void run('pull', { mode: '
|
|
15039
|
+
{ id: 'pp-pull-from', label: t('more.pullFrom'), disabled: remoteNames.length === 0, reason, run: pickRemote((name) => { void run('pull', { mode: 'merge', remote: name }) }) },
|
|
14894
15040
|
null,
|
|
14895
15041
|
{ id: 'pp-push', label: t('toolbar.push'), disabled: remoteReady !== true || blocked, reason, run: () => setDialog({ kind: 'push' }) },
|
|
14896
15042
|
{ id: 'pp-push-force', label: t('more.pushForce'), danger: true, disabled: remoteReady !== true || blocked, reason, run: () => setDialog({ kind: 'pushForce' }) },
|
|
@@ -15017,7 +15163,7 @@ window.__ModuleLoader__.load({
|
|
|
15017
15163
|
const pullPickedBranches = async (picked) => {
|
|
15018
15164
|
for (const item of picked) {
|
|
15019
15165
|
if (item.kind !== 'local') continue
|
|
15020
|
-
if (item.entry.head === true) { await run('pull', { mode: '
|
|
15166
|
+
if (item.entry.head === true) { await run('pull', { mode: 'merge' }); continue }
|
|
15021
15167
|
const upstream = item.entry.upstream
|
|
15022
15168
|
if (upstream === null || upstream === undefined || upstream === '') continue
|
|
15023
15169
|
const slash = upstream.indexOf('/')
|
|
@@ -15076,7 +15222,7 @@ window.__ModuleLoader__.load({
|
|
|
15076
15222
|
}
|
|
15077
15223
|
else if (spec.id === 'pull') {
|
|
15078
15224
|
if (railPickedLocals.length > 0) { entry.label = fill(t('toolbar.pullPicked'), { n: railPickedLocals.length }); entry.disabled = blocked; entry.run = () => { void pullPickedBranches(railPickedLocals) } }
|
|
15079
|
-
else { entry.disabled = remoteReady !== true || tracked !== true || blocked; entry.run = () => { void run('pull', { mode: '
|
|
15225
|
+
else { entry.disabled = remoteReady !== true || tracked !== true || blocked; entry.run = () => { void run('pull', { mode: 'merge' }) } }
|
|
15080
15226
|
}
|
|
15081
15227
|
else if (spec.id === 'push') {
|
|
15082
15228
|
if (railPickedLocals.length > 0) { entry.label = fill(t('toolbar.pushPicked'), { n: railPickedLocals.length }); entry.disabled = blocked; entry.run = () => setDialog({ kind: 'pushMany', items: railPickedLocals.map((item) => ({ name: item.name, upstream: item.entry.upstream })) }) }
|
|
@@ -16281,6 +16427,13 @@ window.__ModuleLoader__.load({
|
|
|
16281
16427
|
'.dig-wt-label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500}',
|
|
16282
16428
|
'.dig-wt-error{padding:6px 8px;margin-bottom:8px;border-radius:var(--dsw-radius-sm,8px);background:rgba(220,38,38,.12);color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px;word-break:break-word}',
|
|
16283
16429
|
'body[data-ds-dark-theme] .dig-wt-error{background:rgba(248,113,113,.16)}',
|
|
16430
|
+
/* ---- icon tooltip ---- */
|
|
16431
|
+
/* A body child, not a panel child: the bottom workbench declares
|
|
16432
|
+
`contain: layout style`, so a fixed element inside it would take the
|
|
16433
|
+
panel box as its containing block and land off-screen (invariant 13).
|
|
16434
|
+
Styled from the same menu tokens so it reads as part of the product. */
|
|
16435
|
+
'.dig-tip{position:fixed;z-index:9999;display:none;box-sizing:border-box;max-width:280px;padding:5px 8px;border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1)));color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-elevation-prominent,0 8px 22px rgba(0,0,0,.32));font-family:var(--dsw-font-family,system-ui,sans-serif);font-size:calc(11px + var(--dsh-content-font-delta-secondary,0px));font-weight:500;line-height:1.45;text-align:left;white-space:normal;overflow-wrap:anywhere;pointer-events:none}',
|
|
16436
|
+
'.dig-tip[data-show="true"]{display:block}',
|
|
16284
16437
|
].concat(LANE_TRACE_RULES).join('\n')
|
|
16285
16438
|
|
|
16286
16439
|
/* ============================== plugin ============================== */
|
|
@@ -17377,6 +17530,193 @@ window.__ModuleLoader__.load({
|
|
|
17377
17530
|
}))))
|
|
17378
17531
|
}
|
|
17379
17532
|
|
|
17533
|
+
/* ---- icon tooltips ----
|
|
17534
|
+
Every icon action already carries its name in `title` (the rail adds the
|
|
17535
|
+
disabled reason; the toolbar / mini / pill buttons carry the action
|
|
17536
|
+
verb). Native title bubbles are the wrong surface for this product: the
|
|
17537
|
+
bottom workbench is a dense horizontal strip, the bubble needs a full
|
|
17538
|
+
second of rest, it is drawn in the browser's own styling, and over a
|
|
17539
|
+
DISABLED rail button it never appears at all. Draw one themed card
|
|
17540
|
+
instead, shared by every seat (panel, composer pill, diff preview) and
|
|
17541
|
+
hung off <body> — see the `.dig-tip` note in CSS for why it can't live
|
|
17542
|
+
inside the panel. The card mirrors the anchor's own `title`, which is
|
|
17543
|
+
removed while the card is up so the native bubble never doubles it. */
|
|
17544
|
+
const TIP_ID = 'dsh-ide-git-tip'
|
|
17545
|
+
const TIP_DELAY_MS = 120
|
|
17546
|
+
const TIP_GAP = 6
|
|
17547
|
+
/* Only this plugin's own surfaces explain themselves. `.dig-root` is the
|
|
17548
|
+
panel (native and better-sidebar seats alike), `.dig-cs-root` the
|
|
17549
|
+
composer status pill, `.dig-preview-root` the external diff seat. */
|
|
17550
|
+
const TIP_ROOTS = '.dig-root, .dig-cs-root, .dig-preview-root'
|
|
17551
|
+
|
|
17552
|
+
function installHoverTips() {
|
|
17553
|
+
/* The SSR self-check drives apply() with a minimal document stub: it has
|
|
17554
|
+
no body, no id lookup and no event targets. Those environments get the
|
|
17555
|
+
no-op installer rather than a throw during plugin activation. */
|
|
17556
|
+
if (typeof document === 'undefined' || document.body == null
|
|
17557
|
+
|| typeof document.createElement !== 'function'
|
|
17558
|
+
|| typeof document.getElementById !== 'function'
|
|
17559
|
+
|| typeof document.addEventListener !== 'function'
|
|
17560
|
+
|| typeof window === 'undefined'
|
|
17561
|
+
|| typeof window.addEventListener !== 'function') return () => {}
|
|
17562
|
+
/* HMR / re-apply: never stack two cards (or two listener sets on one). */
|
|
17563
|
+
const stale = document.getElementById(TIP_ID)
|
|
17564
|
+
if (stale !== null) stale.remove()
|
|
17565
|
+
const card = document.createElement('div')
|
|
17566
|
+
card.id = TIP_ID
|
|
17567
|
+
card.className = 'dig-tip'
|
|
17568
|
+
card.setAttribute('data-dsh-ide-git', '')
|
|
17569
|
+
card.setAttribute('role', 'tooltip')
|
|
17570
|
+
card.setAttribute('aria-hidden', 'true')
|
|
17571
|
+
document.body.appendChild(card)
|
|
17572
|
+
|
|
17573
|
+
/* `anchor` is the button the pointer is on (or about to rest on, while
|
|
17574
|
+
`timer` runs); `shown` is the one whose title the card has taken. */
|
|
17575
|
+
let anchor = null
|
|
17576
|
+
let shown = null
|
|
17577
|
+
let takenTitle = null
|
|
17578
|
+
let timer = null
|
|
17579
|
+
|
|
17580
|
+
const restore = () => {
|
|
17581
|
+
if (shown === null) return
|
|
17582
|
+
/* React may have rewritten the attribute while the card was up (toggle
|
|
17583
|
+
buttons swap their copy): only put the taken text back when nothing
|
|
17584
|
+
else has claimed the slot, so a fresh label is never clobbered. */
|
|
17585
|
+
try {
|
|
17586
|
+
if (shown.getAttribute('title') === null && takenTitle !== null) shown.setAttribute('title', takenTitle)
|
|
17587
|
+
} catch (error) { void error }
|
|
17588
|
+
shown = null
|
|
17589
|
+
takenTitle = null
|
|
17590
|
+
}
|
|
17591
|
+
|
|
17592
|
+
const hide = () => {
|
|
17593
|
+
if (timer !== null) { clearTimeout(timer); timer = null }
|
|
17594
|
+
anchor = null
|
|
17595
|
+
restore()
|
|
17596
|
+
card.removeAttribute('data-show')
|
|
17597
|
+
card.setAttribute('aria-hidden', 'true')
|
|
17598
|
+
card.textContent = ''
|
|
17599
|
+
}
|
|
17600
|
+
|
|
17601
|
+
/* Viewport coordinates on a body child: no panel containment to fight. */
|
|
17602
|
+
const place = () => {
|
|
17603
|
+
const rect = anchor.getBoundingClientRect()
|
|
17604
|
+
const width = card.offsetWidth
|
|
17605
|
+
const height = card.offsetHeight
|
|
17606
|
+
const viewportW = window.innerWidth || document.documentElement.clientWidth || 0
|
|
17607
|
+
const viewportH = window.innerHeight || document.documentElement.clientHeight || 0
|
|
17608
|
+
/* The vertical action rail is a 24px column on the panel's left edge:
|
|
17609
|
+
a card under an icon there would cover the icons below it, so this one
|
|
17610
|
+
sits to its right and follows the row. (The horizontal rail uses
|
|
17611
|
+
`.dig-rail-row`, so the `closest` query only matches the vertical
|
|
17612
|
+
seat — top rows keep the below-the-button placement.) */
|
|
17613
|
+
if (anchor.closest('.dig-rail') !== null) {
|
|
17614
|
+
let left = rect.left + rect.width + TIP_GAP
|
|
17615
|
+
if (left + width > viewportW - 4) left = rect.left - TIP_GAP - width
|
|
17616
|
+
if (left < 4) left = 4
|
|
17617
|
+
let top = rect.top + (rect.height - height) / 2
|
|
17618
|
+
if (top + height > viewportH - 4) top = viewportH - height - 4
|
|
17619
|
+
if (top < 4) top = 4
|
|
17620
|
+
card.style.left = Math.round(left) + 'px'
|
|
17621
|
+
card.style.top = Math.round(top) + 'px'
|
|
17622
|
+
return
|
|
17623
|
+
}
|
|
17624
|
+
/* Below the anchor; flip above when the bottom edge would clip. */
|
|
17625
|
+
let top = rect.bottom + TIP_GAP
|
|
17626
|
+
if (top + height > viewportH - 4) top = rect.top - height - TIP_GAP
|
|
17627
|
+
if (top < 4) top = 4
|
|
17628
|
+
let left = rect.left + (rect.width - width) / 2
|
|
17629
|
+
if (left + width > viewportW - 4) left = viewportW - width - 4
|
|
17630
|
+
if (left < 4) left = 4
|
|
17631
|
+
card.style.left = Math.round(left) + 'px'
|
|
17632
|
+
card.style.top = Math.round(top) + 'px'
|
|
17633
|
+
}
|
|
17634
|
+
|
|
17635
|
+
const reveal = () => {
|
|
17636
|
+
timer = null
|
|
17637
|
+
if (anchor === null) return
|
|
17638
|
+
const label = anchor.getAttribute('title')
|
|
17639
|
+
if (label === null || label === '') { hide(); return }
|
|
17640
|
+
takenTitle = label
|
|
17641
|
+
try { anchor.removeAttribute('title') } catch (error) { void error }
|
|
17642
|
+
shown = anchor
|
|
17643
|
+
card.textContent = label
|
|
17644
|
+
card.setAttribute('data-show', 'true')
|
|
17645
|
+
card.removeAttribute('aria-hidden')
|
|
17646
|
+
place()
|
|
17647
|
+
}
|
|
17648
|
+
|
|
17649
|
+
/* Whatever sits under the pointer, not whatever received the event: a
|
|
17650
|
+
disabled button still hit-tests (rail icons), and the SVG glyph inside
|
|
17651
|
+
the button is usually the element under the cursor. `closest` walks up
|
|
17652
|
+
to the button. The label is read here, but the anchor's own copy is
|
|
17653
|
+
already taken while its card is up — so the shown button answers from
|
|
17654
|
+
the saved text, otherwise it would stop counting as a candidate the
|
|
17655
|
+
moment the card opened. */
|
|
17656
|
+
const buttonAt = (x, y) => {
|
|
17657
|
+
if (typeof document.elementFromPoint !== 'function') return null
|
|
17658
|
+
let hit = null
|
|
17659
|
+
try { hit = document.elementFromPoint(x, y) } catch (error) { void error; return null }
|
|
17660
|
+
if (hit === null || hit === undefined || typeof hit.closest !== 'function') return null
|
|
17661
|
+
if (hit.closest(TIP_ROOTS) === null) return null
|
|
17662
|
+
return hit.closest('button, [role="button"]')
|
|
17663
|
+
}
|
|
17664
|
+
|
|
17665
|
+
const labelOf = (button) => {
|
|
17666
|
+
if (button === shown) return takenTitle
|
|
17667
|
+
const label = button.getAttribute('title')
|
|
17668
|
+
return label === null || label === '' ? null : label
|
|
17669
|
+
}
|
|
17670
|
+
|
|
17671
|
+
const onPointerMove = (event) => {
|
|
17672
|
+
if (event !== null && event !== undefined && event.pointerType === 'touch') return
|
|
17673
|
+
/* A drag is not a hover: reordering rail actions, dragging a divider or
|
|
17674
|
+
selecting rows all sweep across buttons with a pressed pointer, and
|
|
17675
|
+
no card should pop up under the drag. */
|
|
17676
|
+
if (event !== null && event !== undefined && typeof event.buttons === 'number' && event.buttons !== 0) {
|
|
17677
|
+
if (anchor !== null || timer !== null || shown !== null) hide()
|
|
17678
|
+
return
|
|
17679
|
+
}
|
|
17680
|
+
const button = buttonAt(event === null || event === undefined ? 0 : event.clientX, event === null || event === undefined ? 0 : event.clientY)
|
|
17681
|
+
if (button === null || labelOf(button) === null) {
|
|
17682
|
+
if (anchor !== null || timer !== null || shown !== null) hide()
|
|
17683
|
+
return
|
|
17684
|
+
}
|
|
17685
|
+
if (button === anchor) return
|
|
17686
|
+
/* Another button: the old card (and the title it took) goes now, the
|
|
17687
|
+
new one waits out the grace period — sweeping the rail must not
|
|
17688
|
+
flash a card per icon. */
|
|
17689
|
+
if (timer !== null) { clearTimeout(timer); timer = null }
|
|
17690
|
+
restore()
|
|
17691
|
+
card.removeAttribute('data-show')
|
|
17692
|
+
anchor = button
|
|
17693
|
+
timer = setTimeout(reveal, TIP_DELAY_MS)
|
|
17694
|
+
}
|
|
17695
|
+
|
|
17696
|
+
document.addEventListener('pointermove', onPointerMove, { passive: true })
|
|
17697
|
+
document.addEventListener('pointerdown', hide, true)
|
|
17698
|
+
document.addEventListener('pointerleave', hide)
|
|
17699
|
+
document.addEventListener('wheel', hide, { passive: true, capture: true })
|
|
17700
|
+
/* Scroll and resize move the anchor under a card that cannot follow it:
|
|
17701
|
+
the card would lie about where the button is. Drop it and let the next
|
|
17702
|
+
pointer move re-open it. */
|
|
17703
|
+
window.addEventListener('scroll', hide, true)
|
|
17704
|
+
window.addEventListener('resize', hide)
|
|
17705
|
+
window.addEventListener('blur', hide)
|
|
17706
|
+
|
|
17707
|
+
return () => {
|
|
17708
|
+
hide()
|
|
17709
|
+
document.removeEventListener('pointermove', onPointerMove)
|
|
17710
|
+
document.removeEventListener('pointerdown', hide, true)
|
|
17711
|
+
document.removeEventListener('pointerleave', hide)
|
|
17712
|
+
document.removeEventListener('wheel', hide, { capture: true })
|
|
17713
|
+
window.removeEventListener('scroll', hide, true)
|
|
17714
|
+
window.removeEventListener('resize', hide)
|
|
17715
|
+
window.removeEventListener('blur', hide)
|
|
17716
|
+
card.remove()
|
|
17717
|
+
}
|
|
17718
|
+
}
|
|
17719
|
+
|
|
17380
17720
|
function apply(ctx) {
|
|
17381
17721
|
const style = document.createElement('style')
|
|
17382
17722
|
style.setAttribute('data-dsh-ide-git', '')
|
|
@@ -17384,6 +17724,11 @@ window.__ModuleLoader__.load({
|
|
|
17384
17724
|
document.head.appendChild(style)
|
|
17385
17725
|
ctx.effect(() => () => { style.remove() }, 'dsh-ide-git: panel styles')
|
|
17386
17726
|
|
|
17727
|
+
/* Icon affordances explain themselves on hover wherever this plugin
|
|
17728
|
+
draws one — the panel in any seat, the composer pill, the diff
|
|
17729
|
+
preview. One document-level card covers them all. */
|
|
17730
|
+
ctx.effect(() => installHoverTips(), 'dsh-ide-git: icon tooltips')
|
|
17731
|
+
|
|
17387
17732
|
const t = translatorOf(ctx)
|
|
17388
17733
|
|
|
17389
17734
|
/* DSH's own locale registry: bind this plugin's namespace so host-side
|