dsh-ide-git 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/client.js CHANGED
@@ -315,6 +315,8 @@ window.__ModuleLoader__.load({
315
315
  'settings.placement.tabHint': '关闭后仍可从右侧栏或底部面板打开。',
316
316
  'settings.placement.immersive': '沉浸模式(占满全高,隐藏输入框)',
317
317
  'settings.placement.immersiveHint': '开启后 Git 标签页占满整个会话区高度,输入框自动收起;审批与提问仍会正常显示。',
318
+ 'settings.diff.inline': '在面板内预览差异',
319
+ 'settings.diff.inlineHint': '开启后差异在 Git 面板内打开,可使用左右分栏与自动换行;关闭后交给侧边栏的 diff 预览。',
318
320
  'preview.openFile': '打开文件预览',
319
321
  'lf.added': '+{count}',
320
322
  'lf.deleted': '-{count}',
@@ -432,6 +434,10 @@ window.__ModuleLoader__.load({
432
434
  'prompt.newTag': '新标签名',
433
435
  'prompt.fromHead': '当前 HEAD',
434
436
  'error.dismiss': '关闭',
437
+ 'error.pushRejected': '推送被拒绝:远程分支上有你本地没有的提交。请先「同步」(拉取并推送),或拉取合并后再推送。',
438
+ 'error.pullDiverged': '本地和远程各有新提交,无法快进。请用「拉取」合并,或改用「拉取(变基)」。',
439
+ 'error.pullConflict': '拉取时发生冲突:请在「变更」里解决冲突文件并提交,然后继续。',
440
+ 'error.pullDirty': '拉取会覆盖本地未提交的更改:请先提交或贮藏这些更改。',
435
441
  'status.loading': '正在读取...',
436
442
  'status.busy': '处理中...',
437
443
  'counts.staged': '已暂存 {n}',
@@ -681,6 +687,8 @@ window.__ModuleLoader__.load({
681
687
  'settings.placement.tabHint': 'Off, the panel stays reachable from the right sidebar and the bottom panel.',
682
688
  'settings.placement.immersive': 'Immersive mode (full height, hidden composer)',
683
689
  'settings.placement.immersiveHint': 'On, the Git tab fills the whole conversation area and the composer steps aside; approvals and questions still show.',
690
+ 'settings.diff.inline': 'Preview diffs in the panel',
691
+ 'settings.diff.inlineHint': 'On, diffs open inside the Git panel with split view and line wrap. Off, the sidebar\'s diff preview takes over.',
684
692
  'preview.openFile': 'Open in the file preview',
685
693
  'lf.added': '+{count}',
686
694
  'lf.deleted': '-{count}',
@@ -798,6 +806,10 @@ window.__ModuleLoader__.load({
798
806
  'prompt.newTag': 'New tag name',
799
807
  'prompt.fromHead': 'current HEAD',
800
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.',
801
813
  'status.loading': 'Reading...',
802
814
  'status.busy': 'Working...',
803
815
  'counts.staged': 'Staged {n}',
@@ -1055,6 +1067,8 @@ window.__ModuleLoader__.load({
1055
1067
  'settings.placement.tabHint': '關閉之後仍可從右側欄或底部面板開啟。',
1056
1068
  'settings.placement.immersive': '沉浸模式(佔滿全高,隱藏輸入框)',
1057
1069
  'settings.placement.immersiveHint': '開啟之後 Git 分頁會佔滿整個會話區高度,輸入框自動收起;審批與提問仍會正常顯示。',
1070
+ 'settings.diff.inline': '喺面板內預覽差異',
1071
+ 'settings.diff.inlineHint': '開啟之後差異喺 Git 面板內開啟,可以用左右分欄同自動換行;關閉之後交畀側邊欄嘅 diff 預覽。',
1058
1072
  'preview.openFile': '開啟檔案預覽',
1059
1073
  'lf.added': '+{count}',
1060
1074
  'lf.deleted': '-{count}',
@@ -1172,6 +1186,10 @@ window.__ModuleLoader__.load({
1172
1186
  'prompt.newTag': '新標籤名',
1173
1187
  'prompt.fromHead': '當前 HEAD',
1174
1188
  'error.dismiss': '關閉',
1189
+ 'error.pushRejected': '推送被拒絕:遠端分支有啲你本地冇嘅提交。請先同步(拉取並推送),或者拉取合併之後再推送。',
1190
+ 'error.pullDiverged': '本地同遠端各有新提交,唔可以快進。請用「拉取」合併,或者改用「拉取(變基)」。',
1191
+ 'error.pullConflict': '拉取時有衝突:喺「變更」入面解決衝突檔案並提交,然後繼續。',
1192
+ 'error.pullDirty': '拉取會覆蓋本地未提交嘅改動:請先提交或者貯藏呢啲改動。',
1175
1193
  'status.loading': '讀取中...',
1176
1194
  'status.busy': '處理中...',
1177
1195
  'counts.staged': '已暫存 {n}',
@@ -1421,6 +1439,8 @@ window.__ModuleLoader__.load({
1421
1439
  'settings.placement.tabHint': '關閉之後仍可從右側欄或底部面板開啟。',
1422
1440
  'settings.placement.immersive': '沉浸模式(佔滿全高,隱藏輸入框)',
1423
1441
  'settings.placement.immersiveHint': '開啟之後 Git 分頁會佔滿整個對話區高度,輸入框自動收起;審核與提問仍會正常顯示。',
1442
+ 'settings.diff.inline': '在面板內預覽差異',
1443
+ 'settings.diff.inlineHint': '開啟後差異在 Git 面板內開啟,可使用左右分欄與自動換行;關閉後交給側邊欄的 diff 預覽。',
1424
1444
  'preview.openFile': '開啟檔案預覽',
1425
1445
  'lf.added': '+{count}',
1426
1446
  'lf.deleted': '-{count}',
@@ -1538,6 +1558,10 @@ window.__ModuleLoader__.load({
1538
1558
  'prompt.newTag': '新標籤名稱',
1539
1559
  'prompt.fromHead': '目前 HEAD',
1540
1560
  'error.dismiss': '關閉',
1561
+ 'error.pushRejected': '推送被拒絕:遠端分支上有你本機沒有的提交。請先同步(拉取並推送),或拉取合併後再推送。',
1562
+ 'error.pullDiverged': '本機與遠端各有新提交,無法快進。請用「拉取」合併,或改用「拉取(變基)」。',
1563
+ 'error.pullConflict': '拉取時發生衝突:請在「變更」中解決衝突檔案並提交,然後繼續。',
1564
+ 'error.pullDirty': '拉取會覆蓋本機未提交的變更:請先提交或貯藏這些變更。',
1541
1565
  'status.loading': '讀取中...',
1542
1566
  'status.busy': '處理中...',
1543
1567
  'counts.staged': '已暫存 {n}',
@@ -1787,6 +1811,8 @@ window.__ModuleLoader__.load({
1787
1811
  'settings.placement.tabHint': '關閉之後仍可從右側欄或底部面板開啟。',
1788
1812
  'settings.placement.immersive': '沉浸模式(佔滿全高,隱藏輸入框)',
1789
1813
  'settings.placement.immersiveHint': '開啟之後 Git 分頁會佔滿整個會話區高度,輸入框自動收起;審批與提問仍會正常顯示。',
1814
+ 'settings.diff.inline': '在面板內預覽差異',
1815
+ 'settings.diff.inlineHint': '開啟後差異在 Git 面板內開啟,可使用左右分欄與自動換行;關閉後交給側邊欄的 diff 預覽。',
1790
1816
  'preview.openFile': '開啟檔案預覽',
1791
1817
  'lf.added': '+{count}',
1792
1818
  'lf.deleted': '-{count}',
@@ -1904,6 +1930,10 @@ window.__ModuleLoader__.load({
1904
1930
  'prompt.newTag': '新標籤名',
1905
1931
  'prompt.fromHead': '當前 HEAD',
1906
1932
  'error.dismiss': '關閉',
1933
+ 'error.pushRejected': '推送被拒絕:遠端分支有啲你本地冇嘅提交。請先同步(拉取並推送),或者拉取合併之後再推送。',
1934
+ 'error.pullDiverged': '本地同遠端各有新提交,唔可以快進。請用「拉取」合併,或者改用「拉取(變基)」。',
1935
+ 'error.pullConflict': '拉取時有衝突:喺「變更」入面解決衝突檔案並提交,然後繼續。',
1936
+ 'error.pullDirty': '拉取會覆蓋本地未提交嘅改動:請先提交或者貯藏呢啲改動。',
1907
1937
  'status.loading': '讀取中...',
1908
1938
  'status.busy': '處理中...',
1909
1939
  'counts.staged': '已暫存 {n}',
@@ -2153,6 +2183,8 @@ window.__ModuleLoader__.load({
2153
2183
  'settings.placement.tabHint': 'オフにしても右サイドバーと下部パネルから開けます。',
2154
2184
  'settings.placement.immersive': '没入モード(全高表示・入力欄を非表示)',
2155
2185
  'settings.placement.immersiveHint': 'オンにすると Git タブが会話エリアの高さいっぱいに広がり、入力欄は引っ込みます。承認や質問は通常どおり表示されます。',
2186
+ 'settings.diff.inline': 'パネル内で差分をプレビュー',
2187
+ 'settings.diff.inlineHint': 'オンの場合、差分は Git パネル内で開き、左右分割と折り返しを使用できます。オフの場合はサイドバーの差分プレビューに任せます。',
2156
2188
  'preview.openFile': 'ファイルプレビューを開く',
2157
2189
  'lf.added': '+{count}',
2158
2190
  'lf.deleted': '-{count}',
@@ -2270,6 +2302,10 @@ window.__ModuleLoader__.load({
2270
2302
  'prompt.newTag': '新しいタグ名',
2271
2303
  'prompt.fromHead': '現在の HEAD',
2272
2304
  'error.dismiss': '閉じる',
2305
+ 'error.pushRejected': 'プッシュが拒否されました:リモートブランチにローカルにないコミットがあります。先に同期(プルしてプッシュ)するか、プル(マージ)してから再プッシュしてください。',
2306
+ 'error.pullDiverged': 'ローカルとリモートの両方に新しいコミットがあり、fast-forward できません。「プル」(マージ)または「プル(リベース)」を使ってください。',
2307
+ 'error.pullConflict': 'プルで競合が発生しました。「変更」で競合ファイルを解決してコミットしてから続行してください。',
2308
+ 'error.pullDirty': 'プルするとローカルの未コミット変更が上書きされます。先にコミットするかスタッシュしてください。',
2273
2309
  'status.loading': '読み込み中...',
2274
2310
  'status.busy': '処理中...',
2275
2311
  'counts.staged': 'ステージ済み {n}',
@@ -2519,6 +2555,8 @@ window.__ModuleLoader__.load({
2519
2555
  'settings.placement.tabHint': '꺼도 오른쪽 사이드바와 하단 패널에서 열 수 있습니다.',
2520
2556
  'settings.placement.immersive': '몰입 모드(전체 높이, 입력창 숨김)',
2521
2557
  'settings.placement.immersiveHint': '켜면 Git 탭이 대화 영역 전체 높이를 차지하고 입력창은 숨겨집니다. 승인과 질문은 정상적으로 표시됩니다.',
2558
+ 'settings.diff.inline': '패널에서 차이 미리보기',
2559
+ 'settings.diff.inlineHint': '켜면 차이가 Git 패널 안에서 열리고 좌우 분할과 자동 줄바꿈을 사용할 수 있습니다. 끄면 사이드바의 차이 미리보기로 넘깁니다.',
2522
2560
  'preview.openFile': '파일 미리보기로 열기',
2523
2561
  'lf.added': '+{count}',
2524
2562
  'lf.deleted': '-{count}',
@@ -2636,6 +2674,10 @@ window.__ModuleLoader__.load({
2636
2674
  'prompt.newTag': '새 태그 이름',
2637
2675
  'prompt.fromHead': '현재 HEAD',
2638
2676
  'error.dismiss': '닫기',
2677
+ 'error.pushRejected': '푸시가 거부되었습니다: 원격 브랜치에 로컬에 없는 커밋이 있습니다. 먼저 동기화(풀 후 푸시)하거나 풀(병합)한 뒤 다시 푸시하세요.',
2678
+ 'error.pullDiverged': '로컬과 원격에 각각 새 커밋이 있어 fast-forward할 수 없습니다. 풀(병합) 또는 풀(리베이스)을 사용하세요.',
2679
+ 'error.pullConflict': '풀 중 충돌이 발생했습니다. "변경"에서 충돌 파일을 해결하고 커밋한 뒤 계속하세요.',
2680
+ 'error.pullDirty': '풀을 하면 로컬의 커밋되지 않은 변경이 덮어써집니다. 먼저 커밋하거나 스태시하세요.',
2639
2681
  'status.loading': '읽는 중...',
2640
2682
  'status.busy': '처리 중...',
2641
2683
  'counts.staged': '스테이지됨 {n}',
@@ -2885,6 +2927,8 @@ window.__ModuleLoader__.load({
2885
2927
  'settings.placement.tabHint': 'Ausgeschaltet bleibt das Panel über rechte Seitenleiste und unteres Panel erreichbar.',
2886
2928
  'settings.placement.immersive': 'Immersiver Modus (volle Höhe, ausgeblendetes Eingabefeld)',
2887
2929
  'settings.placement.immersiveHint': 'Eingeschaltet füllt der Git-Tab die gesamte Gesprächshöhe und das Eingabefeld weicht zurück; Freigaben und Rückfragen bleiben sichtbar.',
2930
+ 'settings.diff.inline': 'Diffs im Panel anzeigen',
2931
+ 'settings.diff.inlineHint': 'Ein: Diffs öffnen im Git-Panel, mit geteilter Ansicht und Zeilenumbruch. Aus: Die Diff-Vorschau der Seitenleiste übernimmt.',
2888
2932
  'preview.openFile': 'In der Dateivorschau öffnen',
2889
2933
  'lf.added': '+{count}',
2890
2934
  'lf.deleted': '-{count}',
@@ -3002,6 +3046,10 @@ window.__ModuleLoader__.load({
3002
3046
  'prompt.newTag': 'Name des neuen Tags',
3003
3047
  'prompt.fromHead': 'aktuelles HEAD',
3004
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.',
3005
3053
  'status.loading': 'Wird gelesen...',
3006
3054
  'status.busy': 'Arbeitet...',
3007
3055
  'counts.staged': 'Vorgemerkt {n}',
@@ -3251,6 +3299,8 @@ window.__ModuleLoader__.load({
3251
3299
  'settings.placement.tabHint': 'Désactivé, le panneau reste accessible depuis la barre latérale droite et le panneau inférieur.',
3252
3300
  'settings.placement.immersive': 'Mode immersif (pleine hauteur, zone de saisie masquée)',
3253
3301
  'settings.placement.immersiveHint': 'Activé, l’onglet Git occupe toute la hauteur de la conversation et la zone de saisie s’efface ; approbations et questions restent visibles.',
3302
+ 'settings.diff.inline': 'Aperçu des diffs dans le panneau',
3303
+ 'settings.diff.inlineHint': 'Activé, les diffs s’ouvrent dans le panneau Git, avec vue scindée et retour à la ligne. Désactivé, l’aperçu diff de la barre latérale prend le relais.',
3254
3304
  'preview.openFile': 'Ouvrir dans l’aperçu du fichier',
3255
3305
  'lf.added': '+{count}',
3256
3306
  'lf.deleted': '-{count}',
@@ -3368,6 +3418,10 @@ window.__ModuleLoader__.load({
3368
3418
  'prompt.newTag': 'Nom de la nouvelle étiquette',
3369
3419
  'prompt.fromHead': 'HEAD courant',
3370
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.',
3371
3425
  'status.loading': 'Lecture...',
3372
3426
  'status.busy': 'En cours...',
3373
3427
  'counts.staged': 'Indexé {n}',
@@ -3617,6 +3671,8 @@ window.__ModuleLoader__.load({
3617
3671
  'settings.placement.tabHint': 'В выключенном виде панель доступна из правой боковой панели и нижней панели.',
3618
3672
  'settings.placement.immersive': 'Полноэкранный режим (во всю высоту, без поля ввода)',
3619
3673
  'settings.placement.immersiveHint': 'Во включённом виде вкладка Git занимает всю высоту области диалога, поле ввода скрывается; подтверждения и вопросы остаются на виду.',
3674
+ 'settings.diff.inline': 'Просмотр различий в панели',
3675
+ 'settings.diff.inlineHint': 'Включено: различия открываются в панели Git с разделённым видом и переносом строк. Выключено: показ переходит к боковой панели.',
3620
3676
  'preview.openFile': 'Открыть в предпросмотре файла',
3621
3677
  'lf.added': '+{count}',
3622
3678
  'lf.deleted': '-{count}',
@@ -3734,6 +3790,10 @@ window.__ModuleLoader__.load({
3734
3790
  'prompt.newTag': 'Имя нового тега',
3735
3791
  'prompt.fromHead': 'текущий HEAD',
3736
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 перезапишет незакоммиченные локальные изменения. Сначала закоммитьте или спрячьте их.',
3737
3797
  'status.loading': 'Чтение...',
3738
3798
  'status.busy': 'Работа...',
3739
3799
  'counts.staged': 'В индексе {n}',
@@ -3983,6 +4043,8 @@ window.__ModuleLoader__.load({
3983
4043
  'settings.placement.tabHint': 'Desligado, o painel continua disponível na barra lateral direita e no painel inferior.',
3984
4044
  'settings.placement.immersive': 'Modo imersivo (altura total, campo de entrada oculto)',
3985
4045
  'settings.placement.immersiveHint': 'Ativado, a aba Git ocupa toda a altura da conversa e o campo de entrada fica de lado; aprovações e perguntas continuam visíveis.',
4046
+ 'settings.diff.inline': 'Pré-visualizar diferenças no painel',
4047
+ 'settings.diff.inlineHint': 'Ligado, as diferenças abrem no painel Git, com vista dividida e quebra de linha. Desligado, a pré-visualização da barra lateral assume.',
3986
4048
  'preview.openFile': 'Abrir na pré-visualização do arquivo',
3987
4049
  'lf.added': '+{count}',
3988
4050
  'lf.deleted': '-{count}',
@@ -4100,6 +4162,10 @@ window.__ModuleLoader__.load({
4100
4162
  'prompt.newTag': 'Nome da nova tag',
4101
4163
  'prompt.fromHead': 'HEAD atual',
4102
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.',
4103
4169
  'status.loading': 'Lendo...',
4104
4170
  'status.busy': 'Trabalhando...',
4105
4171
  'counts.staged': 'Preparado {n}',
@@ -4349,6 +4415,8 @@ window.__ModuleLoader__.load({
4349
4415
  'settings.placement.tabHint': 'Da spento, il pannello resta raggiungibile dalla barra laterale destra e dal pannello inferiore.',
4350
4416
  'settings.placement.immersive': 'Modalità immersiva (altezza piena, input nascosto)',
4351
4417
  'settings.placement.immersiveHint': 'Attiva, la scheda Git occupa tutta l’altezza della conversazione e la barra di input resta nascosta; approvazioni e domande restano visibili.',
4418
+ 'settings.diff.inline': 'Anteprima delle differenze nel pannello',
4419
+ 'settings.diff.inlineHint': 'Attivo: le differenze si aprono nel pannello Git, con vista affiancata e ritorno a capo. Disattivo: subentra l’anteprima della barra laterale.',
4352
4420
  'preview.openFile': 'Apri nell’anteprima del file',
4353
4421
  'lf.added': '+{count}',
4354
4422
  'lf.deleted': '-{count}',
@@ -4466,6 +4534,10 @@ window.__ModuleLoader__.load({
4466
4534
  'prompt.newTag': 'Nome del nuovo tag',
4467
4535
  'prompt.fromHead': 'HEAD corrente',
4468
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.',
4469
4541
  'status.loading': 'Lettura...',
4470
4542
  'status.busy': 'In corso...',
4471
4543
  'counts.staged': 'In stage {n}',
@@ -4715,6 +4787,8 @@ window.__ModuleLoader__.load({
4715
4787
  'settings.placement.tabHint': 'Uitgeschakeld blijft het paneel bereikbaar via de rechterzijbalk en het onderpaneel.',
4716
4788
  'settings.placement.immersive': 'Meeslepende modus (volledige hoogte, verborgen invoerveld)',
4717
4789
  'settings.placement.immersiveHint': 'Aan: het Git-tabblad vult de hele gespreksruimte en het invoerveld wijkt; goedkeuringen en vragen blijven zichtbaar.',
4790
+ 'settings.diff.inline': 'Diffs in het paneel bekijken',
4791
+ 'settings.diff.inlineHint': 'Aan: diffs openen in het Git-paneel, met gesplitste weergave en regelterugloop. Uit: de diff-voorvertoning in de zijbalk neemt het over.',
4718
4792
  'preview.openFile': 'Openen in de bestandsvoorbeeld',
4719
4793
  'lf.added': '+{count}',
4720
4794
  'lf.deleted': '-{count}',
@@ -4832,6 +4906,10 @@ window.__ModuleLoader__.load({
4832
4906
  'prompt.newTag': 'Naam van de nieuwe tag',
4833
4907
  'prompt.fromHead': 'huidige HEAD',
4834
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.',
4835
4913
  'status.loading': 'Lezen...',
4836
4914
  'status.busy': 'Bezig...',
4837
4915
  'counts.staged': 'Gestaged {n}',
@@ -5081,6 +5159,8 @@ window.__ModuleLoader__.load({
5081
5159
  'settings.placement.tabHint': 'Po wyłączeniu panel pozostaje dostępny z prawego paska bocznego i dolnego panelu.',
5082
5160
  'settings.placement.immersive': 'Tryb immersyjny (pełna wysokość, ukryte pole wpisywania)',
5083
5161
  'settings.placement.immersiveHint': 'Po włączeniu karta Git wypełnia całą wysokość rozmowy, a pole wpisywania się chowa; zatwierdzenia i pytania pozostają widoczne.',
5162
+ 'settings.diff.inline': 'Podgląd różnic w panelu',
5163
+ 'settings.diff.inlineHint': 'Włączone: różnice otwierają się w panelu Git, z widokiem dzielonym i zawijaniem wierszy. Wyłączone: podgląd różnic w pasku bocznym przejmuje.',
5084
5164
  'preview.openFile': 'Otwórz w podglądzie pliku',
5085
5165
  'lf.added': '+{count}',
5086
5166
  'lf.deleted': '-{count}',
@@ -5198,6 +5278,10 @@ window.__ModuleLoader__.load({
5198
5278
  'prompt.newTag': 'Nazwa nowego tagu',
5199
5279
  'prompt.fromHead': 'bieżący HEAD',
5200
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.',
5201
5285
  'status.loading': 'Wczytywanie...',
5202
5286
  'status.busy': 'Praca...',
5203
5287
  'counts.staged': 'W indeksie {n}',
@@ -5447,6 +5531,8 @@ window.__ModuleLoader__.load({
5447
5531
  'settings.placement.tabHint': 'Avstängd finns panelen kvar via höger sidopanel och nedre panelen.',
5448
5532
  'settings.placement.immersive': 'Uppslukande läge (full höjd, dolt inmatningsfält)',
5449
5533
  'settings.placement.immersiveHint': 'På: Git-fliken fyller hela konversationsytan och inmatningsfältet träder åt sidan; godkännanden och frågor visas fortfarande.',
5534
+ 'settings.diff.inline': 'Förhandsgranska diffar i panelen',
5535
+ 'settings.diff.inlineHint': 'På: diffar öppnas i Git-panelen med delad vy och radbrytning. Av: sidopanelens diff-förhandsgranskning tar över.',
5450
5536
  'preview.openFile': 'Öppna i filförhandsvisningen',
5451
5537
  'lf.added': '+{count}',
5452
5538
  'lf.deleted': '-{count}',
@@ -5564,6 +5650,10 @@ window.__ModuleLoader__.load({
5564
5650
  'prompt.newTag': 'Namn på ny tagg',
5565
5651
  'prompt.fromHead': 'aktuell HEAD',
5566
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.',
5567
5657
  'status.loading': 'Läser...',
5568
5658
  'status.busy': 'Arbetar...',
5569
5659
  'counts.staged': 'Klädda {n}',
@@ -5813,6 +5903,8 @@ window.__ModuleLoader__.load({
5813
5903
  'settings.placement.tabHint': 'Kapatıldığında panel sağ kenar çubuğundan ve alt panelden açılabilir.',
5814
5904
  'settings.placement.immersive': 'Odaklanma modu (tam yükseklik, gizli giriş alanı)',
5815
5905
  'settings.placement.immersiveHint': 'Açıkken Git sekmesi tüm konuşma alanını kaplar ve giriş alanı gizlenir; onaylar ve sorular yine de görünür.',
5906
+ 'settings.diff.inline': 'Farkları panelde önizle',
5907
+ 'settings.diff.inlineHint': 'Açık: farklar Git panelinde açılır; bölünmüş görünüm ve satır kaydırma kullanılabilir. Kapalı: kenar çubuğundaki fark önizlemesi devralır.',
5816
5908
  'preview.openFile': 'Dosya önizlemesinde aç',
5817
5909
  'lf.added': '+{count}',
5818
5910
  'lf.deleted': '-{count}',
@@ -5930,6 +6022,10 @@ window.__ModuleLoader__.load({
5930
6022
  'prompt.newTag': 'Yeni etiket adı',
5931
6023
  'prompt.fromHead': 'geçerli HEAD',
5932
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.',
5933
6029
  'status.loading': 'Okunuyor...',
5934
6030
  'status.busy': 'Çalışıyor...',
5935
6031
  'counts.staged': 'Hazırlanan {n}',
@@ -6179,6 +6275,8 @@ window.__ModuleLoader__.load({
6179
6275
  'settings.placement.tabHint': 'Dimatikan, panel tetap bisa dibuka dari bilah sisi kanan dan panel bawah.',
6180
6276
  'settings.placement.immersive': 'Mode imersif (tinggi penuh, kolom masukan tersembunyi)',
6181
6277
  'settings.placement.immersiveHint': 'Aktif: tab Git memenuhi seluruh tinggi area percakapan dan kolom masukan mengalah; persetujuan dan pertanyaan tetap tampil.',
6278
+ 'settings.diff.inline': 'Pratinjau diff di panel',
6279
+ 'settings.diff.inlineHint': 'Aktif: diff terbuka di panel Git, dengan tampilan terbagi dan pelipatan baris. Nonaktif: pratinjau diff bilah sisi mengambil alih.',
6182
6280
  'preview.openFile': 'Buka di pratinjau berkas',
6183
6281
  'lf.added': '+{count}',
6184
6282
  'lf.deleted': '-{count}',
@@ -6296,6 +6394,10 @@ window.__ModuleLoader__.load({
6296
6394
  'prompt.newTag': 'Nama tag baru',
6297
6395
  'prompt.fromHead': 'HEAD saat ini',
6298
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.',
6299
6401
  'status.loading': 'Membaca...',
6300
6402
  'status.busy': 'Bekerja...',
6301
6403
  'counts.staged': 'Ter-stage {n}',
@@ -6545,6 +6647,8 @@ window.__ModuleLoader__.load({
6545
6647
  'settings.placement.tabHint': 'Tắt rồi, bảng vẫn mở được từ thanh bên phải và bảng dưới.',
6546
6648
  'settings.placement.immersive': 'Chế độ đắm chìm (chiều cao đầy đủ, ẩn ô nhập)',
6547
6649
  'settings.placement.immersiveHint': 'Bật: thẻ Git chiếm toàn bộ chiều cao vùng hội thoại và ô nhập thu vào; phê duyệt và câu hỏi vẫn hiển thị bình thường.',
6650
+ 'settings.diff.inline': 'Xem trước khác biệt trong bảng',
6651
+ 'settings.diff.inlineHint': 'Bật: khác biệt mở trong bảng Git, có chế độ chia đôi và tự động xuống dòng. Tắt: phần xem trước ở thanh bên đảm nhận.',
6548
6652
  'preview.openFile': 'Mở trong xem trước tệp',
6549
6653
  'lf.added': '+{count}',
6550
6654
  'lf.deleted': '-{count}',
@@ -6662,6 +6766,10 @@ window.__ModuleLoader__.load({
6662
6766
  'prompt.newTag': 'Tên thẻ mới',
6663
6767
  'prompt.fromHead': 'HEAD hiện tại',
6664
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.',
6665
6773
  'status.loading': 'Đang đọc...',
6666
6774
  'status.busy': 'Đang xử lý...',
6667
6775
  'counts.staged': 'Đã stage {n}',
@@ -6911,6 +7019,8 @@ window.__ModuleLoader__.load({
6911
7019
  'settings.placement.tabHint': 'عند الإيقاف تبقى اللوحة متاحة من الشريط الجانبي الأيمن واللوحة السفلية.',
6912
7020
  'settings.placement.immersive': 'الوضع الغامر (ارتفاع كامل، إخفاء صندوق الكتابة)',
6913
7021
  'settings.placement.immersiveHint': 'عند التشغيل يملأ تبويب Git منطقة المحادثة بكامل ارتفاعها ويتراجع صندوق الكتابة؛ تظل الموافقات والأسئلة ظاهرة كالمعتاد.',
7022
+ 'settings.diff.inline': 'معاينة الفروق داخل اللوحة',
7023
+ 'settings.diff.inlineHint': 'عند التشغيل تفتح الفروق داخل لوحة Git مع العرض المنقسم والتفاف الأسطر. عند الإيقاف تتولاها معاينة الفروق في الشريط الجانبي.',
6914
7024
  'preview.openFile': 'افتح في معاينة الملف',
6915
7025
  'lf.added': '+{count}',
6916
7026
  'lf.deleted': '-{count}',
@@ -7028,6 +7138,10 @@ window.__ModuleLoader__.load({
7028
7138
  'prompt.newTag': 'اسم الوسم الجديد',
7029
7139
  'prompt.fromHead': 'HEAD الحالي',
7030
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 إلى الكتابة فوق تغييرات محلية غير مثبتة. ثبّتها أو خزّنها أولًا.',
7031
7145
  'status.loading': 'جارٍ القراءة...',
7032
7146
  'status.busy': 'جارٍ العمل...',
7033
7147
  'counts.staged': 'مُجهَّز {n}',
@@ -7277,6 +7391,8 @@ window.__ModuleLoader__.load({
7277
7391
  'settings.placement.tabHint': 'बंद करने पर भी पैनल दाएँ साइडबार और निचले पैनल से खुलता है।',
7278
7392
  'settings.placement.immersive': 'इमर्सिव मोड (पूरी ऊँचाई, छिपा इनपुट बॉक्स)',
7279
7393
  'settings.placement.immersiveHint': 'चालू होने पर Git टैब पूरे संवाद क्षेत्र की ऊँचाई घेर लेता है और इनपुट बॉक्स हट जाता है; अनुमोदन और प्रश्न फिर भी दिखते हैं।',
7394
+ 'settings.diff.inline': 'पैनल में अंतर देखें',
7395
+ 'settings.diff.inlineHint': 'चालू: अंतर Git पैनल में खुलते हैं, स्प्लिट व्यू और लाइन रैप के साथ। बंद: साइडबार का अंतर पूर्वावलोकन संभालता है।',
7280
7396
  'preview.openFile': 'फ़ाइल पूर्वावलोकन में खोलें',
7281
7397
  'lf.added': '+{count}',
7282
7398
  'lf.deleted': '-{count}',
@@ -7394,6 +7510,10 @@ window.__ModuleLoader__.load({
7394
7510
  'prompt.newTag': 'नए टैग का नाम',
7395
7511
  'prompt.fromHead': 'वर्तमान HEAD',
7396
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 करें।',
7397
7517
  'status.loading': 'पढ़ा जा रहा है...',
7398
7518
  'status.busy': 'कार्य जारी...',
7399
7519
  'counts.staged': 'स्टेज्ड {n}',
@@ -7643,6 +7763,8 @@ window.__ModuleLoader__.load({
7643
7763
  'settings.placement.tabHint': 'ปิดแล้วยังเปิดแผงจากแถบข้างขวาหรือแผงล่างได้',
7644
7764
  'settings.placement.immersive': 'โหมดดื่มด่ำ (เต็มความสูง, ซ่อนกล่องพิมพ์)',
7645
7765
  'settings.placement.immersiveHint': 'เมื่อเปิด แท็บ Git จะกินพื้นที่สูงทั้งหมดของบทสนทนาและกล่องพิมพ์จะถอยออก; การอนุมัติและคำถามยังแสดงตามปกติ',
7766
+ 'settings.diff.inline': 'ดูตัวอย่างความแตกต่างในแผง',
7767
+ 'settings.diff.inlineHint': 'เปิด: ความแตกต่างเปิดในแผง Git พร้อมมุมมองแยกและตัดบรรทัดอัตโนมัติ ปิด: ตัวอย่างความแตกต่างในแถบข้างจะรับหน้าที่',
7646
7768
  'preview.openFile': 'เปิดในตัวอย่างไฟล์',
7647
7769
  'lf.added': '+{count}',
7648
7770
  'lf.deleted': '-{count}',
@@ -7760,6 +7882,10 @@ window.__ModuleLoader__.load({
7760
7882
  'prompt.newTag': 'ชื่อแท็กใหม่',
7761
7883
  'prompt.fromHead': 'HEAD ปัจจุบัน',
7762
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 ก่อน',
7763
7889
  'status.loading': 'กำลังอ่าน...',
7764
7890
  'status.busy': 'กำลังทำงาน...',
7765
7891
  'counts.staged': 'สเตจแล้ว {n}',
@@ -9326,47 +9452,6 @@ window.__ModuleLoader__.load({
9326
9452
  return false
9327
9453
  }
9328
9454
 
9329
- /* One single-file unified diff (what the host `diff` method returns) → the
9330
- one DiffHunk DiffBlock renders, by replaying ` `/`-`/`+` lines back into
9331
- the two sides. No `@@` at all (binary patches, mode-only changes) or a
9332
- pathless header → null, and the caller keeps the hand-drawn rows. */
9333
- function patchToHunk(patch) {
9334
- const text = typeof patch === 'string' ? patch : ''
9335
- if (text === '' || text.indexOf('@@') < 0) return null
9336
- const lines = text.split('\n')
9337
- let path = null
9338
- const oldText = []
9339
- const newText = []
9340
- for (const line of lines) {
9341
- if (line.indexOf('+++ ') === 0) {
9342
- const name = line.slice(4).split('\t')[0]
9343
- if (name !== '/dev/null') path = name.replace(/^b\//, '')
9344
- continue
9345
- }
9346
- if (line.indexOf('--- ') === 0) {
9347
- if (path === null) {
9348
- const name = line.slice(4).split('\t')[0]
9349
- if (name !== '/dev/null') path = name.replace(/^a\//, '')
9350
- }
9351
- continue
9352
- }
9353
- if (line.indexOf('@@') === 0) continue
9354
- if (line.indexOf('+') === 0) { newText.push(line.slice(1)); continue }
9355
- if (line.indexOf('-') === 0) { oldText.push(line.slice(1)); continue }
9356
- if (line.indexOf('\\') === 0) continue
9357
- if (line.indexOf(' ') === 0) { oldText.push(line.slice(1)); newText.push(line.slice(1)); continue }
9358
- // diff --git / index / new file mode / rename from ...: metadata above
9359
- // the first hunk; anything else after it would be malformed.
9360
- continue
9361
- }
9362
- if (path === null || path === '') return null
9363
- // A patch without body lines (mode-only, bare metadata) replays into an
9364
- // empty hunk that DiffBlock draws as nothing at all — keep the hand-drawn
9365
- // rows for those, they at least show what happened.
9366
- if (oldText.length === 0 && newText.length === 0) return null
9367
- return { path: path, oldText: oldText.length === 0 ? null : oldText.join('\n'), newText: newText.join('\n') }
9368
- }
9369
-
9370
9455
  /* ============================== diff ============================== */
9371
9456
 
9372
9457
  function diffLines(patch) {
@@ -9403,13 +9488,13 @@ window.__ModuleLoader__.load({
9403
9488
 
9404
9489
  /* ---- diff selection core
9405
9490
  Pure line-number plumbing for the diff-pane context menu (v0.13.5).
9406
- The host DiffBlock draws rows WITHOUT line-number attributes — a row is
9407
- `<div class={line + kindClass}>{row.text}</div>` (DiffBlock.tsx:204) and
9408
- the `+`/`-`/two-space prefixes live in CSS `::before` content — so the
9409
- menu reconstructs numbers by replaying the ROW KINDS in document order
9410
- from the patch's first hunk header. The hand-drawn rows already carry
9411
- parsed numbers (diffLines above). Kept DOM-free between the markers so
9412
- tests/smoke.mjs can slice the block and drive the real implementation. */
9491
+ For a host DiffBlock body (kept as a forward-compat fallback) rows do
9492
+ not expose numbers, so the menu replays ROW KINDS in document order from
9493
+ the patch's first hunk header; hand-drawn rows carry parsed numbers
9494
+ (diffLines above). The review renderer used today (#22) reads its own
9495
+ printed numbers instead (reviewRowSpanOf, DOM side below). Kept DOM-free
9496
+ between the markers so tests/smoke.mjs can slice the block and drive the
9497
+ real implementation. */
9413
9498
 
9414
9499
  const DIFF_SNIPPET_MAX = 600
9415
9500
 
@@ -9555,13 +9640,13 @@ window.__ModuleLoader__.load({
9555
9640
 
9556
9641
  /* ---------- DOM side of the diff-pane context menu (browser only) ----------
9557
9642
  SSR never calls these: they run inside the pane's onContextMenu, after
9558
- the selection has been captured. The row element under a selection
9559
- endpoint is either a hand-drawn `.dig-diff-line`, or a DiffBlock row —
9560
- a div child of the body whose OWN parent carries the `data-diff`
9561
- marker. `data-diff` is a component attribute (DiffBlock.tsx:199), not a
9562
- hashed CSS-module class, so the lookup does not bet on build-time class
9563
- renaming; the row KIND comes from the `::before` content the kinds are
9564
- painted with, for the same reason. */
9643
+ the selection has been captured. A row under a selection endpoint is
9644
+ either a review row (data-diff-line on .dig-lf-line / .dig-lf-splitLine
9645
+ / .dig-lf-sideLine), a hand-drawn `.dig-diff-line`, or a host DiffBlock
9646
+ row (a div child of the body whose OWN parent carries `data-diff`). The
9647
+ review path reads the printed .dig-lf-number cells; the host path gets
9648
+ its KIND from the `::before` content the rows are painted with. Both
9649
+ rely on component attributes, not hashed CSS-module classes. */
9565
9650
 
9566
9651
  function savedSelectionOf() {
9567
9652
  try {
@@ -9582,6 +9667,15 @@ window.__ModuleLoader__.load({
9582
9667
  if (element.classList !== undefined && element.classList.contains('dig-diff-line') === true) {
9583
9668
  return { kind: 'drawn', row: element, body: element.parentElement }
9584
9669
  }
9670
+ /* The review renderer (LineDiff, used by BOTH the inline pane and the
9671
+ native preview since #22) stamps every row with data-diff-line and
9672
+ carries the real line numbers in .dig-lf-number cells. */
9673
+ if (element.hasAttribute !== undefined && element.hasAttribute('data-diff-line') === true
9674
+ && element.classList !== undefined
9675
+ && (element.classList.contains('dig-lf-line') === true || element.classList.contains('dig-lf-splitLine') === true || element.classList.contains('dig-lf-sideLine') === true)) {
9676
+ const body = element.closest === undefined ? null : element.closest('.dig-lf-body')
9677
+ if (body !== null) return { kind: 'review', row: element, body: body }
9678
+ }
9585
9679
  const parent = element.parentElement
9586
9680
  if (parent !== null && parent.getAttribute !== undefined && parent.hasAttribute('data-diff') === false) {
9587
9681
  const root = parent.parentElement
@@ -9612,6 +9706,49 @@ window.__ModuleLoader__.load({
9612
9706
  return { rows: rows, kinds: kinds }
9613
9707
  }
9614
9708
 
9709
+ /* The review renderer's rows carry their printed numbers already: read
9710
+ them straight off the selected rows (unified: old then new; split with
9711
+ wrap: the two cells; split without wrap: one side per column). This is
9712
+ simpler and more accurate than replaying kinds, and it follows the
9713
+ SPLIT view, where one row is two files' lines. */
9714
+ function reviewRowSpanOf(body, fromRow, toRow) {
9715
+ if (body === null || body === undefined) return null
9716
+ const rows = Array.prototype.slice.call(body.querySelectorAll('[data-diff-line]'))
9717
+ const fromIndex = rows.indexOf(fromRow)
9718
+ const toIndex = rows.indexOf(toRow)
9719
+ if (fromIndex < 0 || toIndex < 0) return null
9720
+ let newFrom = null
9721
+ let newTo = null
9722
+ let oldFrom = null
9723
+ let oldTo = null
9724
+ const read = (node) => {
9725
+ const text = node === undefined || node === null ? '' : String(node.textContent).trim()
9726
+ if (text === '') return null
9727
+ const value = Number.parseInt(text, 10)
9728
+ return Number.isFinite(value) ? value : null
9729
+ }
9730
+ for (let index = Math.min(fromIndex, toIndex); index <= Math.max(fromIndex, toIndex); index += 1) {
9731
+ const row = rows[index]
9732
+ const numbers = row.querySelectorAll('.dig-lf-number')
9733
+ let oldLine = null
9734
+ let newLine = null
9735
+ if (row.classList.contains('dig-lf-sideLine') === true) {
9736
+ const side = row.closest === undefined ? null : row.closest('[data-diff-side]')
9737
+ const value = read(numbers[0])
9738
+ if (side !== null && side.getAttribute('data-diff-side') === 'left') oldLine = value
9739
+ else newLine = value
9740
+ } else {
9741
+ oldLine = read(numbers[0])
9742
+ newLine = read(numbers[1])
9743
+ }
9744
+ if (oldLine !== null) { oldFrom = oldFrom === null ? oldLine : oldFrom; oldTo = oldLine }
9745
+ if (newLine !== null) { newFrom = newFrom === null ? newLine : newFrom; newTo = newLine }
9746
+ }
9747
+ if (newFrom !== null) return { start: newFrom, end: newTo === null ? newFrom : newTo, old: false }
9748
+ if (oldFrom !== null) return { start: oldFrom, end: oldTo === null ? oldFrom : oldTo, old: true }
9749
+ return null
9750
+ }
9751
+
9615
9752
  /* { kind: 'lines', span } for a selection inside ONE diff body, or
9616
9753
  { kind: 'snippet' } when the rows are there but the numbers cannot be
9617
9754
  reconstructed. null (rows missing entirely) means "not a diff
@@ -9623,7 +9760,9 @@ window.__ModuleLoader__.load({
9623
9760
  if (from === null || to === null) return null
9624
9761
  if (from.kind !== to.kind || from.body !== to.body) return { kind: 'snippet', text: saved.text }
9625
9762
  let span = null
9626
- if (from.kind === 'drawn') {
9763
+ if (from.kind === 'review') {
9764
+ span = reviewRowSpanOf(from.body, from.row, to.row)
9765
+ } else if (from.kind === 'drawn') {
9627
9766
  const rows = diffLines(patch)
9628
9767
  const all = from.body === null || from.body === undefined ? [] : Array.prototype.slice.call(from.body.querySelectorAll('.dig-diff-line'))
9629
9768
  const fromIndex = all.indexOf(from.row)
@@ -9645,55 +9784,19 @@ window.__ModuleLoader__.load({
9645
9784
  }
9646
9785
 
9647
9786
 
9648
- /* DiffBlock's own 16-line cap would REPLACE scrolling with a fold button;
9649
- this panel keeps the old contract (render everything, scroll inside the
9650
- .dig-diff-pane max-height), so the cap is pushed out of reach and the
9651
- fold labels below are placeholders that never render at this size. */
9652
- const DIFF_BLOCK_MAX_LINES = 1000000
9653
-
9654
- function diffLabelsOf(t) {
9655
- return {
9656
- codeLabel: t('diff.codeLabel'),
9657
- wrapLabel: t('diff.wrapLabel'),
9658
- unwrapLabel: t('diff.unwrapLabel'),
9659
- copy: t('diff.copy'),
9660
- copied: t('diff.copied'),
9661
- collapseAria: 'Collapse diff',
9662
- expandAria: (hidden) => 'Show ' + String(hidden) + ' more diff lines',
9663
- collapse: 'Show less',
9664
- expand: (hidden) => 'Show ' + String(hidden) + ' more',
9665
- }
9666
- }
9667
-
9787
+ /* The inline pane body and the native preview tab share ONE renderer:
9788
+ LineDiff is the ported official ReviewTab/FileDiff and carries the
9789
+ split / wrap toolbar (issue #22). The former host DiffBlock body only
9790
+ had wrap / copy, so turning the external hand-off off silently dropped
9791
+ the 分栏 switch the user had asked for. One renderer also keeps both
9792
+ diff surfaces visually identical. */
9668
9793
  function DiffBody(props) {
9669
- const rows = useMemo(() => diffLines(props.patch), [props.patch])
9670
- if (props.loading === true) return E('div', { className: 'dig-empty' }, props.t('diff.loading'))
9671
- if (props.binary === true) return E('div', { className: 'dig-empty' }, props.t('diff.binary'))
9672
- if (rows.length === 0) return E('div', { className: 'dig-empty' }, props.t('diff.empty'))
9673
- /* Host-rendered first (v0.14): the ui-primitives diff card brings the
9674
- shared toolbar (copy / wrap / language) and the product's diff colors.
9675
- A patch we cannot replay into a hunk (binary, mode-only) or a host
9676
- without the seed module falls back to the hand-drawn rows below. This
9677
- IS the official diff renderer (the platform seed table ships it, the
9678
- host's own surfaces render diffs through it) — the external preview
9679
- tab uses the same one, so every diff in the product reads alike. */
9680
- if (UIPrimitives !== null && typeof UIPrimitives.DiffBlock === 'function') {
9681
- const hunk = patchToHunk(props.patch)
9682
- if (hunk !== null) {
9683
- return E('div', { className: 'dig-diff-host' },
9684
- E(UIPrimitives.DiffBlock, {
9685
- diffs: [hunk],
9686
- labels: diffLabelsOf(props.t),
9687
- maxLines: DIFF_BLOCK_MAX_LINES,
9688
- className: 'dig-diff-card',
9689
- }))
9690
- }
9691
- }
9692
- return E('div', { className: 'dig-diff' }, rows.map((row, index) => E('div', { key: index, className: 'dig-diff-line dig-diff-' + row.kind },
9693
- E('span', { className: 'dig-diff-gutter' }, row.oldLine === null ? '' : String(row.oldLine)),
9694
- E('span', { className: 'dig-diff-gutter' }, row.newLine === null ? '' : String(row.newLine)),
9695
- E('span', { className: 'dig-diff-text' }, row.text === '' ? ' ' : row.text),
9696
- )))
9794
+ return E(LineDiff, {
9795
+ patch: props.patch,
9796
+ loading: props.loading,
9797
+ t: props.t,
9798
+ onOpenFile: props.onOpenFile,
9799
+ })
9697
9800
  }
9698
9801
 
9699
9802
  /* ---- external preview diff (v0.14.3): a faithful port of the review view ----
@@ -10891,6 +10994,43 @@ window.__ModuleLoader__.load({
10891
10994
  E('span', { className: 'dig-settings-glyph' }, E(Icon, { name: 'branch', size: 13 })),
10892
10995
  E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t('settings.composer.title'))),
10893
10996
  E('div', { className: 'dig-settings-subnote' }, t('settings.composer.hint')))
10997
+ })(),
10998
+ (() => {
10999
+ /* Inline diff preview (issue #22): ON keeps the panel's own diff pane
11000
+ reachable, including its split / wrap switches. OFF restores the
11001
+ external hand-off to dsh-better-sidebar or the native preview seat.
11002
+ Same optimistic-write contract as the three rows above. */
11003
+ const field = 'inlineDiff'
11004
+ const inputId = 'dig-placement-' + field
11005
+ const shown = placementShown(field)
11006
+ const toggle = (event) => {
11007
+ if (readOnly === true) { setNote(t('settings.rail.readonly')); return }
11008
+ const fromControl = event !== undefined && event !== null && event.target !== null
11009
+ && event.target !== undefined && typeof event.target.checked === 'boolean'
11010
+ ? event.target.checked
11011
+ : null
11012
+ const wanted = placementTarget(field, fromControl)
11013
+ intent.current[field] = wanted
11014
+ dirty.current.set(field, wanted)
11015
+ setPending((now) => Object.assign({}, now, { [field]: wanted }))
11016
+ setNote('')
11017
+ bump()
11018
+ scheduleFlush()
11019
+ }
11020
+ return E('div', { key: field },
11021
+ E('div', { className: 'dig-settings-row', 'data-placement-row': field },
11022
+ E('input', {
11023
+ id: inputId,
11024
+ type: 'checkbox',
11025
+ className: 'dig-settings-check',
11026
+ 'aria-labelledby': inputId + '-label',
11027
+ checked: shown,
11028
+ disabled: readOnly === true,
11029
+ onChange: toggle,
11030
+ }),
11031
+ E('span', { className: 'dig-settings-glyph' }, E(Icon, { name: 'diff', size: 13 })),
11032
+ E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t('settings.diff.inline'))),
11033
+ E('div', { className: 'dig-settings-subnote' }, t('settings.diff.inlineHint')))
10894
11034
  })()),
10895
11035
  /* Section 3 — the AI commit message (issue #6): model, reasoning and the
10896
11036
  prompt template. */
@@ -12999,6 +13139,9 @@ window.__ModuleLoader__.load({
12999
13139
  const [commits, setCommits] = useState([])
13000
13140
  const [hasMore, setHasMore] = useState(false)
13001
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)
13002
13145
  const [note, setNote] = useState(null)
13003
13146
  const [busy, setBusy] = useState(false)
13004
13147
  const [view, setView] = useState('history')
@@ -13342,10 +13485,13 @@ window.__ModuleLoader__.load({
13342
13485
  const guard = useCallback(async (work) => {
13343
13486
  setBusy(true)
13344
13487
  setError(null)
13488
+ setErrorCode(null)
13345
13489
  try {
13346
13490
  return await work()
13347
13491
  } catch (caught) {
13348
- setError(caught instanceof Error ? caught.message : String(caught))
13492
+ const failure = caught instanceof Error ? caught : new Error(String(caught))
13493
+ setError(failure.message)
13494
+ setErrorCode(typeof failure.code === 'string' ? failure.code : null)
13349
13495
  return undefined
13350
13496
  } finally {
13351
13497
  setBusy(false)
@@ -13390,7 +13536,7 @@ window.__ModuleLoader__.load({
13390
13536
  writePaintRepos(cwd, data)
13391
13537
  apply(data, readPaintRoot(cwd))
13392
13538
  } catch (caught) {
13393
- if (!cancelled) setError(caught instanceof Error ? caught.message : String(caught))
13539
+ if (!cancelled) { setErrorCode(null); setError(caught instanceof Error ? caught.message : String(caught)) }
13394
13540
  }
13395
13541
  })()
13396
13542
  return () => { cancelled = true }
@@ -13418,6 +13564,7 @@ window.__ModuleLoader__.load({
13418
13564
  const data = await request('gitLogs', {})
13419
13565
  setGitOutput(Array.isArray(data.entries) ? data.entries : [])
13420
13566
  } catch (caught) {
13567
+ setErrorCode(null)
13421
13568
  setError(caught instanceof Error ? caught.message : String(caught))
13422
13569
  }
13423
13570
  }, [request])
@@ -13643,7 +13790,11 @@ window.__ModuleLoader__.load({
13643
13790
 
13644
13791
  const run = useCallback(async (method, payload) => {
13645
13792
  const result = await guard(() => request(method, Object.assign({}, base, payload)))
13646
- if (result !== undefined) {
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') {
13647
13798
  await refresh()
13648
13799
  /* v0.21.2: the composer pill is a separate tree; without this it only
13649
13800
  learned about an in-panel checkout/commit at the next 30s poll. The
@@ -13704,6 +13855,7 @@ window.__ModuleLoader__.load({
13704
13855
  const data = await request('diff', Object.assign({}, base, seed))
13705
13856
  setPatch(data.patch)
13706
13857
  } catch (caught) {
13858
+ setErrorCode(null)
13707
13859
  setError(caught instanceof Error ? caught.message : String(caught))
13708
13860
  } finally {
13709
13861
  setPatchLoading(false)
@@ -13722,6 +13874,11 @@ window.__ModuleLoader__.load({
13722
13874
  repoRoot 并进它自己的 git 调用,多仓库工作区不会落错仓库。 */
13723
13875
  const previewDiff = useCallback((seed) => {
13724
13876
  if (repoRoot === null) return false
13877
+ /* Inline-first (issue #22): the switch defaults to ON, so the panel's
13878
+ own diff pane — and its split / wrap controls — is what a click
13879
+ opens. Turning it off hands the preview to the external chain
13880
+ below, which is the only behaviour older releases had. */
13881
+ if (inlineDiffEnabled() === true) return false
13725
13882
  const commitHash = seed.hash !== undefined && seed.hash !== null ? String(seed.hash) : null
13726
13883
  const ref = commitHash !== null
13727
13884
  ? { kind: 'commit', hash: commitHash.slice(0, 7), hashFull: commitHash, subject: seed.subject || '', worktree: repoRoot, repoRoot: repoRoot }
@@ -13801,6 +13958,7 @@ window.__ModuleLoader__.load({
13801
13958
  try {
13802
13959
  setDetail(await request('commitDetail', Object.assign({}, base, { hash: commit.hash })))
13803
13960
  } catch (caught) {
13961
+ setErrorCode(null)
13804
13962
  setError(caught instanceof Error ? caught.message : String(caught))
13805
13963
  }
13806
13964
  }, [base])
@@ -13973,6 +14131,7 @@ window.__ModuleLoader__.load({
13973
14131
  const createWorktree = async (draft) => {
13974
14132
  setBusy(true)
13975
14133
  setError(null)
14134
+ setErrorCode(null)
13976
14135
  try {
13977
14136
  await request('worktreeAdd', Object.assign({}, base, draft))
13978
14137
  setDialog(null)
@@ -13988,6 +14147,7 @@ window.__ModuleLoader__.load({
13988
14147
  const removeWorktree = async (tree, force) => {
13989
14148
  setBusy(true)
13990
14149
  setError(null)
14150
+ setErrorCode(null)
13991
14151
  try {
13992
14152
  await request('worktreeRemove', Object.assign({}, base, { path: tree.path, confirm: true, force: force === true }))
13993
14153
  setDialog(null)
@@ -14001,6 +14161,7 @@ window.__ModuleLoader__.load({
14001
14161
  return message
14002
14162
  }
14003
14163
  setDialog(null)
14164
+ setErrorCode(null)
14004
14165
  setError(message)
14005
14166
  return message
14006
14167
  } finally {
@@ -14010,6 +14171,7 @@ window.__ModuleLoader__.load({
14010
14171
  const pruneWorktrees = async () => {
14011
14172
  setBusy(true)
14012
14173
  setError(null)
14174
+ setErrorCode(null)
14013
14175
  try {
14014
14176
  const data = await request('worktreePrune', Object.assign({}, base, { confirm: true }))
14015
14177
  setDialog(null)
@@ -14018,6 +14180,7 @@ window.__ModuleLoader__.load({
14018
14180
  return data
14019
14181
  } catch (caught) {
14020
14182
  setDialog(null)
14183
+ setErrorCode(null)
14021
14184
  setError(caught instanceof Error ? caught.message : String(caught))
14022
14185
  return null
14023
14186
  } finally {
@@ -14198,7 +14361,26 @@ window.__ModuleLoader__.load({
14198
14361
  ].filter((item) => item !== null))
14199
14362
  return
14200
14363
  }
14201
- const updateItem = { id: 'update', icon: 'fetch', tone: 'cyan', label: t('action.update'), run: () => { void run('fetch', { prune: true }) } }
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
+ }
14202
14384
  const pushItem = { id: 'push', icon: 'push', tone: 'success', label: t('action.push'), disabled: blocked, reason: t('operation.hint'), run: () => setDialog({ kind: 'push' }) }
14203
14385
  const items = [
14204
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) } },
@@ -14601,9 +14783,29 @@ window.__ModuleLoader__.load({
14601
14783
  E('span', { className: 'dig-topbar-spacer' }),
14602
14784
  busy ? E('span', { className: 'dig-busy' }, t('status.busy')) : null)
14603
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
+ }
14604
14803
  const banner = error === null ? null : E('div', { className: 'dig-banner' },
14605
- E('span', { className: 'dig-banner-text' }, error),
14606
- E('button', { type: 'button', className: 'dig-link', onClick: () => setError(null) }, t('error.dismiss')))
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')))
14607
14809
 
14608
14810
  const noteBanner = note === null ? null : E('div', { className: 'dig-note' },
14609
14811
  E('span', { className: 'dig-banner-text' }, note),
@@ -14795,7 +14997,7 @@ window.__ModuleLoader__.load({
14795
14997
  ] },
14796
14998
  null,
14797
14999
  /* —— top-level sync (VS Code header row) —— */
14798
- { id: 'mm-pull', icon: 'pull', tone: 'accent', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'ff-only' }) } },
15000
+ { id: 'mm-pull', icon: 'pull', tone: 'accent', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'merge' }) } },
14799
15001
  { id: 'mm-push', icon: 'push', tone: 'success', label: t('toolbar.push'), disabled: remoteReady !== true || blocked, reason, run: () => setDialog({ kind: 'push' }) },
14800
15002
  { id: 'mm-clone', icon: 'plus', tone: 'secondary', label: t('more.clone'), run: () => setDialog({ kind: 'clone' }) },
14801
15003
  { id: 'mm-checkout', icon: 'checkout', tone: 'accent', label: t('action.checkout') + '...', disabled: otherBranches.length === 0 || blocked, reason, run: (ev) => pickBranchMenu(ev, 'checkout') },
@@ -14824,12 +15026,12 @@ window.__ModuleLoader__.load({
14824
15026
  ] },
14825
15027
  /* —— SCM ▸ Pull, Push ▸ —— */
14826
15028
  { id: 'sub-pullpush', icon: 'pull', tone: 'accent', label: t('more.pullpushGroup'), items: [
14827
- { id: 'pp-sync', label: t('more.sync'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void (async () => { await run('pull', { mode: 'ff-only' }); await run('push', { confirm: true }) })() } },
15029
+ { 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 }) })() } },
14828
15030
  { 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 }) })() } },
14829
15031
  null,
14830
- { id: 'pp-pull', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'ff-only' }) } },
15032
+ { id: 'pp-pull', label: t('toolbar.pull'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'merge' }) } },
14831
15033
  { id: 'pp-pull-rebase', label: t('more.pullRebase'), disabled: remoteReady !== true || tracked !== true || blocked, reason, run: () => { void run('pull', { mode: 'rebase' }) } },
14832
- { id: 'pp-pull-from', label: t('more.pullFrom'), disabled: remoteNames.length === 0, reason, run: pickRemote((name) => { void run('pull', { mode: 'ff-only', remote: name }) }) },
15034
+ { id: 'pp-pull-from', label: t('more.pullFrom'), disabled: remoteNames.length === 0, reason, run: pickRemote((name) => { void run('pull', { mode: 'merge', remote: name }) }) },
14833
15035
  null,
14834
15036
  { id: 'pp-push', label: t('toolbar.push'), disabled: remoteReady !== true || blocked, reason, run: () => setDialog({ kind: 'push' }) },
14835
15037
  { id: 'pp-push-force', label: t('more.pushForce'), danger: true, disabled: remoteReady !== true || blocked, reason, run: () => setDialog({ kind: 'pushForce' }) },
@@ -14956,7 +15158,7 @@ window.__ModuleLoader__.load({
14956
15158
  const pullPickedBranches = async (picked) => {
14957
15159
  for (const item of picked) {
14958
15160
  if (item.kind !== 'local') continue
14959
- if (item.entry.head === true) { await run('pull', { mode: 'ff-only' }); continue }
15161
+ if (item.entry.head === true) { await run('pull', { mode: 'merge' }); continue }
14960
15162
  const upstream = item.entry.upstream
14961
15163
  if (upstream === null || upstream === undefined || upstream === '') continue
14962
15164
  const slash = upstream.indexOf('/')
@@ -15015,7 +15217,7 @@ window.__ModuleLoader__.load({
15015
15217
  }
15016
15218
  else if (spec.id === 'pull') {
15017
15219
  if (railPickedLocals.length > 0) { entry.label = fill(t('toolbar.pullPicked'), { n: railPickedLocals.length }); entry.disabled = blocked; entry.run = () => { void pullPickedBranches(railPickedLocals) } }
15018
- else { entry.disabled = remoteReady !== true || tracked !== true || blocked; entry.run = () => { void run('pull', { mode: 'ff-only' }) } }
15220
+ else { entry.disabled = remoteReady !== true || tracked !== true || blocked; entry.run = () => { void run('pull', { mode: 'merge' }) } }
15019
15221
  }
15020
15222
  else if (spec.id === 'push') {
15021
15223
  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 })) }) }
@@ -16355,6 +16557,18 @@ window.__ModuleLoader__.load({
16355
16557
  return value !== false
16356
16558
  }
16357
16559
 
16560
+ /* Inline-first diff preview (issue #22). Default ON: the panel's own diff
16561
+ pane owns the split / wrap switches, so installing dsh-better-sidebar or
16562
+ opening the native preview seat must not silently take that capability
16563
+ away. The settings card flips this field; false restores the external
16564
+ hand-off chain in previewDiff. */
16565
+ function inlineDiffEnabled() {
16566
+ const ready = railFormReady()
16567
+ if (ready === null) return true
16568
+ const value = ready.value !== null && typeof ready.value === 'object' ? ready.value.inlineDiff : undefined
16569
+ return value !== false
16570
+ }
16571
+
16358
16572
  const COMPOSER_POLL_MS = 30000
16359
16573
  const COMPOSER_ERROR_TTL_MS = 6000
16360
16574
  /* Line counts re-run at most this often without a real change: the two
@@ -17201,9 +17415,9 @@ window.__ModuleLoader__.load({
17201
17415
  diff — whatever the store holds — fetched fresh from the host route on
17202
17416
  every seed. The seed carries cwd + repoRoot so a multi-root workspace
17203
17417
  lands on the right repository (the route requires cwd; repoRoot skips
17204
- the rev-parse). Rendered through the SAME DiffBody the inline pane uses,
17205
- so host DiffBlock reuse, the hand-drawn fallback and the binary/empty
17206
- states are all identical. */
17418
+ the rev-parse). Rendered through the SAME LineDiff the inline pane's
17419
+ DiffBody uses, so the header tool set (split / wrap / open file) and
17420
+ the binary/empty states are identical on both surfaces. */
17207
17421
  function NativePreviewPanel(props) {
17208
17422
  const t = props.t
17209
17423
  const [seed, setSeed] = useState(() => previewStore.seed)