dsh-ide-git 0.19.13 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-ide-git",
3
- "version": "0.19.13",
3
+ "version": "0.21.0",
4
4
  "main": "./src/index.js",
5
5
  "description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ide-git",
3
- "version": "0.19.13",
3
+ "version": "0.21.0",
4
4
  "description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
5
5
  "type": "module",
6
6
  "repository": {
package/src/client.js CHANGED
@@ -164,6 +164,9 @@ window.__ModuleLoader__.load({
164
164
  'action.fullscreen': '全屏',
165
165
  'action.exitFullscreen': '退出全屏',
166
166
  'action.newBranchFrom': '从该分支新建分支...',
167
+ 'action.setUpstream': '设置上游…',
168
+ 'action.unsetUpstream': '取消上游',
169
+ 'action.createLocalBranch': '创建本地分支…',
167
170
  'action.newBranchHere': '在此新建分支...',
168
171
  'action.rename': '重命名...',
169
172
  'action.delete': '删除',
@@ -235,6 +238,8 @@ window.__ModuleLoader__.load({
235
238
  'more.fetchAll': '从所有远程抓取',
236
239
  'more.deleteRemoteBranch': '删除远程分支...',
237
240
  'more.publish': '发布分支...',
241
+ 'more.setUpstreamHead': '设置当前分支上游…',
242
+ 'more.unsetUpstreamHead': '取消当前分支上游',
238
243
  'more.addRemote': '添加远程...',
239
244
  'more.removeRemote': '删除远程...',
240
245
  'more.stashTracked': '贮藏(仅已跟踪)',
@@ -297,6 +302,12 @@ window.__ModuleLoader__.load({
297
302
  'composer.changesHint': '未提交变更数',
298
303
  'composer.fileHint': '新增 {a} · 删除 {d} 个文件',
299
304
  'composer.linesHint': '+{ins} −{del} 行',
305
+ 'modelRoute.pick': '选择模型',
306
+ 'modelRoute.trigger': 'AI 提交信息模型',
307
+ 'modelRoute.modelRow': '模型',
308
+ 'modelRoute.effortRow': '思考',
309
+ 'modelRoute.effortDefault': '跟随默认',
310
+ 'modelRoute.empty': '暂无可用模型',
300
311
  'composer.behindHint': '远程新提交(可拉取)',
301
312
  'composer.aheadHint': '本地待推送提交',
302
313
  'composer.inWorktree': '已被其他工作树签出',
@@ -392,6 +403,7 @@ window.__ModuleLoader__.load({
392
403
  'confirm.cancel': '取消',
393
404
  'confirm.ok': '确定',
394
405
  'prompt.newBranch': '新分支名(将创建并签出)',
406
+ 'prompt.createTracking': '从远程分支创建本地分支',
395
407
  'prompt.renameBranch': '重命名分支',
396
408
  'prompt.newTag': '新标签名',
397
409
  'prompt.fromHead': '当前 HEAD',
@@ -494,6 +506,9 @@ window.__ModuleLoader__.load({
494
506
  'action.fullscreen': 'Full screen',
495
507
  'action.exitFullscreen': 'Exit full screen',
496
508
  'action.newBranchFrom': 'New branch from here...',
509
+ 'action.setUpstream': 'Set Upstream…',
510
+ 'action.unsetUpstream': 'Unset Upstream',
511
+ 'action.createLocalBranch': 'Create Local Branch…',
497
512
  'action.newBranchHere': 'New branch here...',
498
513
  'action.rename': 'Rename...',
499
514
  'action.delete': 'Delete',
@@ -565,6 +580,8 @@ window.__ModuleLoader__.load({
565
580
  'more.fetchAll': 'Fetch from All Remotes',
566
581
  'more.deleteRemoteBranch': 'Delete Remote Branch...',
567
582
  'more.publish': 'Publish Branch...',
583
+ 'more.setUpstreamHead': 'Set Upstream for Current Branch…',
584
+ 'more.unsetUpstreamHead': 'Unset Upstream of Current Branch',
568
585
  'more.addRemote': 'Add Remote...',
569
586
  'more.removeRemote': 'Remove Remote...',
570
587
  'more.stashTracked': 'Stash (Tracked Only)',
@@ -627,6 +644,12 @@ window.__ModuleLoader__.load({
627
644
  'composer.changesHint': 'Uncommitted changes',
628
645
  'composer.fileHint': '{a} added · {d} deleted files',
629
646
  'composer.linesHint': '+{ins} −{del} lines',
647
+ 'modelRoute.pick': 'Pick a model',
648
+ 'modelRoute.trigger': 'AI commit message model',
649
+ 'modelRoute.modelRow': 'Model',
650
+ 'modelRoute.effortRow': 'Reasoning',
651
+ 'modelRoute.effortDefault': 'Follow default',
652
+ 'modelRoute.empty': 'No models available',
630
653
  'composer.behindHint': 'New upstream commits (pullable)',
631
654
  'composer.aheadHint': 'Local commits waiting to push',
632
655
  'composer.inWorktree': 'Checked out in another worktree',
@@ -722,6 +745,7 @@ window.__ModuleLoader__.load({
722
745
  'confirm.cancel': 'Cancel',
723
746
  'confirm.ok': 'OK',
724
747
  'prompt.newBranch': 'New branch name (created and checked out)',
748
+ 'prompt.createTracking': 'Create a local branch from the remote one',
725
749
  'prompt.renameBranch': 'Rename branch',
726
750
  'prompt.newTag': 'New tag name',
727
751
  'prompt.fromHead': 'current HEAD',
@@ -832,6 +856,9 @@ window.__ModuleLoader__.load({
832
856
  'action.fullscreen': '全螢幕',
833
857
  'action.exitFullscreen': '結束全螢幕',
834
858
  'action.newBranchFrom': '由此新建分支...',
859
+ 'action.setUpstream': '設定上游…',
860
+ 'action.unsetUpstream': '取消上游',
861
+ 'action.createLocalBranch': '建立本地分支…',
835
862
  'action.newBranchHere': '喺呢度新建分支...',
836
863
  'action.rename': '重新命名...',
837
864
  'action.delete': '刪除',
@@ -903,6 +930,8 @@ window.__ModuleLoader__.load({
903
930
  'more.fetchAll': '從所有遠端抓取',
904
931
  'more.deleteRemoteBranch': '刪除遠端分支...',
905
932
  'more.publish': '發布分支...',
933
+ 'more.setUpstreamHead': '設定目前分支上游…',
934
+ 'more.unsetUpstreamHead': '取消目前分支上游',
906
935
  'more.addRemote': '新增遠端...',
907
936
  'more.removeRemote': '刪除遠端...',
908
937
  'more.stashTracked': '儲存(僅已追蹤)',
@@ -965,6 +994,12 @@ window.__ModuleLoader__.load({
965
994
  'composer.changesHint': '未提交變更數',
966
995
  'composer.fileHint': '新增 {a} · 刪除 {d} 個檔案',
967
996
  'composer.linesHint': '+{ins} −{del} 行',
997
+ 'modelRoute.pick': '選擇模型',
998
+ 'modelRoute.trigger': 'AI 提交訊息模型',
999
+ 'modelRoute.modelRow': '模型',
1000
+ 'modelRoute.effortRow': '思考',
1001
+ 'modelRoute.effortDefault': '跟隨預設',
1002
+ 'modelRoute.empty': '暫無可用模型',
968
1003
  'composer.behindHint': '遠端新提交(可以拉取)',
969
1004
  'composer.aheadHint': '本地等待推送提交',
970
1005
  'composer.inWorktree': '已經喺其他工作樹簽出',
@@ -1060,6 +1095,7 @@ window.__ModuleLoader__.load({
1060
1095
  'confirm.cancel': '取消',
1061
1096
  'confirm.ok': '確定',
1062
1097
  'prompt.newBranch': '新分支名(建立並簽出)',
1098
+ 'prompt.createTracking': '從遠端分支建立本地分支',
1063
1099
  'prompt.renameBranch': '重新命名分支',
1064
1100
  'prompt.newTag': '新標籤名',
1065
1101
  'prompt.fromHead': '當前 HEAD',
@@ -1162,6 +1198,9 @@ window.__ModuleLoader__.load({
1162
1198
  'action.fullscreen': '全螢幕',
1163
1199
  'action.exitFullscreen': '結束全螢幕',
1164
1200
  'action.newBranchFrom': '由此新建分支...',
1201
+ 'action.setUpstream': '設定上游…',
1202
+ 'action.unsetUpstream': '取消上游',
1203
+ 'action.createLocalBranch': '建立本地分支…',
1165
1204
  'action.newBranchHere': '在此新建分支...',
1166
1205
  'action.rename': '重新命名...',
1167
1206
  'action.delete': '刪除',
@@ -1233,6 +1272,8 @@ window.__ModuleLoader__.load({
1233
1272
  'more.fetchAll': '從所有遠端擷取',
1234
1273
  'more.deleteRemoteBranch': '刪除遠端分支...',
1235
1274
  'more.publish': '發佈分支...',
1275
+ 'more.setUpstreamHead': '設定目前分支上游…',
1276
+ 'more.unsetUpstreamHead': '取消目前分支上游',
1236
1277
  'more.addRemote': '新增遠端...',
1237
1278
  'more.removeRemote': '移除遠端...',
1238
1279
  'more.stashTracked': '擱置(僅已追蹤)',
@@ -1295,6 +1336,12 @@ window.__ModuleLoader__.load({
1295
1336
  'composer.changesHint': '未提交變更數',
1296
1337
  'composer.fileHint': '新增 {a} · 刪除 {d} 個檔案',
1297
1338
  'composer.linesHint': '+{ins} −{del} 行',
1339
+ 'modelRoute.pick': '選擇模型',
1340
+ 'modelRoute.trigger': 'AI 提交訊息模型',
1341
+ 'modelRoute.modelRow': '模型',
1342
+ 'modelRoute.effortRow': '思考',
1343
+ 'modelRoute.effortDefault': '跟隨預設',
1344
+ 'modelRoute.empty': '暫無可用模型',
1298
1345
  'composer.behindHint': '遠端新提交(可拉取)',
1299
1346
  'composer.aheadHint': '本地待推送提交',
1300
1347
  'composer.inWorktree': '已被其他工作樹簽出',
@@ -1390,6 +1437,7 @@ window.__ModuleLoader__.load({
1390
1437
  'confirm.cancel': '取消',
1391
1438
  'confirm.ok': '確定',
1392
1439
  'prompt.newBranch': '新分支名稱(建立並簽出)',
1440
+ 'prompt.createTracking': '從遠端分支建立本地分支',
1393
1441
  'prompt.renameBranch': '重新命名分支',
1394
1442
  'prompt.newTag': '新標籤名稱',
1395
1443
  'prompt.fromHead': '目前 HEAD',
@@ -1492,6 +1540,9 @@ window.__ModuleLoader__.load({
1492
1540
  'action.fullscreen': '全螢幕',
1493
1541
  'action.exitFullscreen': '結束全螢幕',
1494
1542
  'action.newBranchFrom': '由此新建分支...',
1543
+ 'action.setUpstream': '設定上游…',
1544
+ 'action.unsetUpstream': '取消上游',
1545
+ 'action.createLocalBranch': '建立本地分支…',
1495
1546
  'action.newBranchHere': '喺呢度新建分支...',
1496
1547
  'action.rename': '重新命名...',
1497
1548
  'action.delete': '刪除',
@@ -1563,6 +1614,8 @@ window.__ModuleLoader__.load({
1563
1614
  'more.fetchAll': '從所有遠端抓取',
1564
1615
  'more.deleteRemoteBranch': '刪除遠端分支...',
1565
1616
  'more.publish': '發布分支...',
1617
+ 'more.setUpstreamHead': '設定目前分支上游…',
1618
+ 'more.unsetUpstreamHead': '取消目前分支上游',
1566
1619
  'more.addRemote': '新增遠端...',
1567
1620
  'more.removeRemote': '刪除遠端...',
1568
1621
  'more.stashTracked': '儲存(僅已追蹤)',
@@ -1625,6 +1678,12 @@ window.__ModuleLoader__.load({
1625
1678
  'composer.changesHint': '未提交變更數',
1626
1679
  'composer.fileHint': '新增 {a} · 刪除 {d} 個檔案',
1627
1680
  'composer.linesHint': '+{ins} −{del} 行',
1681
+ 'modelRoute.pick': '選擇模型',
1682
+ 'modelRoute.trigger': 'AI 提交訊息模型',
1683
+ 'modelRoute.modelRow': '模型',
1684
+ 'modelRoute.effortRow': '思考',
1685
+ 'modelRoute.effortDefault': '跟隨預設',
1686
+ 'modelRoute.empty': '暫無可用模型',
1628
1687
  'composer.behindHint': '遠端新提交(可以拉取)',
1629
1688
  'composer.aheadHint': '本地等待推送提交',
1630
1689
  'composer.inWorktree': '已經喺其他工作樹簽出',
@@ -1720,6 +1779,7 @@ window.__ModuleLoader__.load({
1720
1779
  'confirm.cancel': '取消',
1721
1780
  'confirm.ok': '確定',
1722
1781
  'prompt.newBranch': '新分支名(建立並簽出)',
1782
+ 'prompt.createTracking': '從遠端分支建立本地分支',
1723
1783
  'prompt.renameBranch': '重新命名分支',
1724
1784
  'prompt.newTag': '新標籤名',
1725
1785
  'prompt.fromHead': '當前 HEAD',
@@ -1822,6 +1882,9 @@ window.__ModuleLoader__.load({
1822
1882
  'action.fullscreen': '全画面',
1823
1883
  'action.exitFullscreen': '全画面を終了',
1824
1884
  'action.newBranchFrom': 'ここから新しいブランチ...',
1885
+ 'action.setUpstream': '上流を設定…',
1886
+ 'action.unsetUpstream': '上流を解除',
1887
+ 'action.createLocalBranch': 'ローカルブランチを作成…',
1825
1888
  'action.newBranchHere': 'ここに新しいブランチ...',
1826
1889
  'action.rename': '名前を変更...',
1827
1890
  'action.delete': '削除',
@@ -1893,6 +1956,8 @@ window.__ModuleLoader__.load({
1893
1956
  'more.fetchAll': '全リモートからフェッチ',
1894
1957
  'more.deleteRemoteBranch': 'リモートブランチを削除...',
1895
1958
  'more.publish': 'ブランチを公開...',
1959
+ 'more.setUpstreamHead': '現在のブランチの上流を設定…',
1960
+ 'more.unsetUpstreamHead': '現在のブランチの上流を解除',
1896
1961
  'more.addRemote': 'リモートを追加...',
1897
1962
  'more.removeRemote': 'リモートを削除...',
1898
1963
  'more.stashTracked': 'スタッシュ(追跡済みのみ)',
@@ -1955,6 +2020,12 @@ window.__ModuleLoader__.load({
1955
2020
  'composer.changesHint': '未コミットの変更数',
1956
2021
  'composer.fileHint': '追加 {a} · 削除 {d} ファイル',
1957
2022
  'composer.linesHint': '+{ins} −{del} 行',
2023
+ 'modelRoute.pick': 'モデルを選択',
2024
+ 'modelRoute.trigger': 'AI コミットメッセージのモデル',
2025
+ 'modelRoute.modelRow': 'モデル',
2026
+ 'modelRoute.effortRow': '思考',
2027
+ 'modelRoute.effortDefault': 'デフォルトに従う',
2028
+ 'modelRoute.empty': '利用可能なモデルがありません',
1958
2029
  'composer.behindHint': 'リモートの新規コミット(取得可能)',
1959
2030
  'composer.aheadHint': 'プッシュ待ちのローカルコミット',
1960
2031
  'composer.inWorktree': '別のワークツリーでチェックアウト中',
@@ -2050,6 +2121,7 @@ window.__ModuleLoader__.load({
2050
2121
  'confirm.cancel': 'キャンセル',
2051
2122
  'confirm.ok': 'OK',
2052
2123
  'prompt.newBranch': '新しいブランチ名(作成してチェックアウト)',
2124
+ 'prompt.createTracking': 'リモートブランチからローカルブランチを作成',
2053
2125
  'prompt.renameBranch': 'ブランチ名を変更',
2054
2126
  'prompt.newTag': '新しいタグ名',
2055
2127
  'prompt.fromHead': '現在の HEAD',
@@ -2152,6 +2224,9 @@ window.__ModuleLoader__.load({
2152
2224
  'action.fullscreen': '전체 화면',
2153
2225
  'action.exitFullscreen': '전체 화면 종료',
2154
2226
  'action.newBranchFrom': '여기서 새 브랜치...',
2227
+ 'action.setUpstream': '업스트림 설정…',
2228
+ 'action.unsetUpstream': '업스트림 해제',
2229
+ 'action.createLocalBranch': '로컬 브랜치 만들기…',
2155
2230
  'action.newBranchHere': '여기에 새 브랜치...',
2156
2231
  'action.rename': '이름 바꾸기...',
2157
2232
  'action.delete': '삭제',
@@ -2223,6 +2298,8 @@ window.__ModuleLoader__.load({
2223
2298
  'more.fetchAll': '모든 원격에서 페치',
2224
2299
  'more.deleteRemoteBranch': '원격 브랜치 삭제...',
2225
2300
  'more.publish': '브랜치 게시...',
2301
+ 'more.setUpstreamHead': '현재 브랜치 업스트림 설정…',
2302
+ 'more.unsetUpstreamHead': '현재 브랜치 업스트림 해제',
2226
2303
  'more.addRemote': '원격 추가...',
2227
2304
  'more.removeRemote': '원격 제거...',
2228
2305
  'more.stashTracked': '스태시(추적된 파일만)',
@@ -2285,6 +2362,12 @@ window.__ModuleLoader__.load({
2285
2362
  'composer.changesHint': '커밋되지 않은 변경 수',
2286
2363
  'composer.fileHint': '추가 {a} · 삭제 {d}개 파일',
2287
2364
  'composer.linesHint': '+{ins} −{del}줄',
2365
+ 'modelRoute.pick': '모델 선택',
2366
+ 'modelRoute.trigger': 'AI 커밋 메시지 모델',
2367
+ 'modelRoute.modelRow': '모델',
2368
+ 'modelRoute.effortRow': '추론',
2369
+ 'modelRoute.effortDefault': '기본값 따르기',
2370
+ 'modelRoute.empty': '사용 가능한 모델이 없습니다',
2288
2371
  'composer.behindHint': '원격 새 커밋(가져오기 가능)',
2289
2372
  'composer.aheadHint': '푸시 대기 중인 로컬 커밋',
2290
2373
  'composer.inWorktree': '다른 작업 트리에서 체크아웃됨',
@@ -2380,6 +2463,7 @@ window.__ModuleLoader__.load({
2380
2463
  'confirm.cancel': '취소',
2381
2464
  'confirm.ok': '확인',
2382
2465
  'prompt.newBranch': '새 브랜치 이름(생성 후 체크아웃)',
2466
+ 'prompt.createTracking': '원격 브랜치에서 로컬 브랜치 만들기',
2383
2467
  'prompt.renameBranch': '브랜치 이름 바꾸기',
2384
2468
  'prompt.newTag': '새 태그 이름',
2385
2469
  'prompt.fromHead': '현재 HEAD',
@@ -2482,6 +2566,9 @@ window.__ModuleLoader__.load({
2482
2566
  'action.fullscreen': 'Vollbild',
2483
2567
  'action.exitFullscreen': 'Vollbild beenden',
2484
2568
  'action.newBranchFrom': 'Neuer Branch von hier...',
2569
+ 'action.setUpstream': 'Upstream festlegen …',
2570
+ 'action.unsetUpstream': 'Upstream entfernen',
2571
+ 'action.createLocalBranch': 'Lokalen Branch erstellen …',
2485
2572
  'action.newBranchHere': 'Neuer Branch hier...',
2486
2573
  'action.rename': 'Umbenennen...',
2487
2574
  'action.delete': 'Löschen',
@@ -2553,6 +2640,8 @@ window.__ModuleLoader__.load({
2553
2640
  'more.fetchAll': 'Von allen Remotes holen',
2554
2641
  'more.deleteRemoteBranch': 'Remote-Branch löschen...',
2555
2642
  'more.publish': 'Branch veröffentlichen...',
2643
+ 'more.setUpstreamHead': 'Upstream des aktuellen Branch festlegen …',
2644
+ 'more.unsetUpstreamHead': 'Upstream des aktuellen Branch entfernen',
2556
2645
  'more.addRemote': 'Remote hinzufügen...',
2557
2646
  'more.removeRemote': 'Remote entfernen...',
2558
2647
  'more.stashTracked': 'Stash (nur getrackt)',
@@ -2615,6 +2704,12 @@ window.__ModuleLoader__.load({
2615
2704
  'composer.changesHint': 'Uncommittete Änderungen',
2616
2705
  'composer.fileHint': '{a} hinzugefügt · {d} gelöscht',
2617
2706
  'composer.linesHint': '+{ins} −{del} Zeilen',
2707
+ 'modelRoute.pick': 'Modell wählen',
2708
+ 'modelRoute.trigger': 'Modell für KI-Commits',
2709
+ 'modelRoute.modelRow': 'Modell',
2710
+ 'modelRoute.effortRow': 'Denken',
2711
+ 'modelRoute.effortDefault': 'Standard folgen',
2712
+ 'modelRoute.empty': 'Keine Modelle verfügbar',
2618
2713
  'composer.behindHint': 'Neue Upstream-Commits (abrufbar)',
2619
2714
  'composer.aheadHint': 'Lokale Commits zum Pushen',
2620
2715
  'composer.inWorktree': 'In anderem Worktree ausgecheckt',
@@ -2710,6 +2805,7 @@ window.__ModuleLoader__.load({
2710
2805
  'confirm.cancel': 'Abbrechen',
2711
2806
  'confirm.ok': 'OK',
2712
2807
  'prompt.newBranch': 'Name des neuen Branch (wird erstellt und ausgecheckt)',
2808
+ 'prompt.createTracking': 'Lokalen Branch aus dem Remote-Branch erstellen',
2713
2809
  'prompt.renameBranch': 'Branch umbenennen',
2714
2810
  'prompt.newTag': 'Name des neuen Tags',
2715
2811
  'prompt.fromHead': 'aktuelles HEAD',
@@ -2812,6 +2908,9 @@ window.__ModuleLoader__.load({
2812
2908
  'action.fullscreen': 'Plein écran',
2813
2909
  'action.exitFullscreen': 'Quitter le plein écran',
2814
2910
  'action.newBranchFrom': 'Nouvelle branche depuis ici...',
2911
+ 'action.setUpstream': 'Définir l\'upstream…',
2912
+ 'action.unsetUpstream': 'Retirer l\'upstream',
2913
+ 'action.createLocalBranch': 'Créer une branche locale…',
2815
2914
  'action.newBranchHere': 'Nouvelle branche ici...',
2816
2915
  'action.rename': 'Renommer...',
2817
2916
  'action.delete': 'Supprimer',
@@ -2883,6 +2982,8 @@ window.__ModuleLoader__.load({
2883
2982
  'more.fetchAll': 'Récupérer de tous les distants',
2884
2983
  'more.deleteRemoteBranch': 'Supprimer la branche distante...',
2885
2984
  'more.publish': 'Publier la branche...',
2985
+ 'more.setUpstreamHead': 'Définir l\'upstream de la branche courante…',
2986
+ 'more.unsetUpstreamHead': 'Retirer l\'upstream de la branche courante',
2886
2987
  'more.addRemote': 'Ajouter un distant...',
2887
2988
  'more.removeRemote': 'Supprimer le distant...',
2888
2989
  'more.stashTracked': 'Remisage (suivis seulement)',
@@ -2945,6 +3046,12 @@ window.__ModuleLoader__.load({
2945
3046
  'composer.changesHint': 'Modifications non commitées',
2946
3047
  'composer.fileHint': '{a} ajoutés · {d} supprimés',
2947
3048
  'composer.linesHint': '+{ins} −{del} lignes',
3049
+ 'modelRoute.pick': 'Choisir un modèle',
3050
+ 'modelRoute.trigger': 'Modèle des messages de commit IA',
3051
+ 'modelRoute.modelRow': 'Modèle',
3052
+ 'modelRoute.effortRow': 'Raisonnement',
3053
+ 'modelRoute.effortDefault': 'Suivre la valeur par défaut',
3054
+ 'modelRoute.empty': 'Aucun modèle disponible',
2948
3055
  'composer.behindHint': 'Nouveaux commits amont (récupérables)',
2949
3056
  'composer.aheadHint': 'Commits locaux en attente d’envoi',
2950
3057
  'composer.inWorktree': 'Extrait dans un autre arbre de travail',
@@ -3040,6 +3147,7 @@ window.__ModuleLoader__.load({
3040
3147
  'confirm.cancel': 'Annuler',
3041
3148
  'confirm.ok': 'OK',
3042
3149
  'prompt.newBranch': 'Nom de la nouvelle branche (créée et extraite)',
3150
+ 'prompt.createTracking': 'Créer une branche locale depuis la branche distante',
3043
3151
  'prompt.renameBranch': 'Renommer la branche',
3044
3152
  'prompt.newTag': 'Nom de la nouvelle étiquette',
3045
3153
  'prompt.fromHead': 'HEAD courant',
@@ -3142,6 +3250,9 @@ window.__ModuleLoader__.load({
3142
3250
  'action.fullscreen': 'Полный экран',
3143
3251
  'action.exitFullscreen': 'Выйти из полного экрана',
3144
3252
  'action.newBranchFrom': 'Новая ветка отсюда...',
3253
+ 'action.setUpstream': 'Задать upstream…',
3254
+ 'action.unsetUpstream': 'Убрать upstream',
3255
+ 'action.createLocalBranch': 'Создать локальную ветку…',
3145
3256
  'action.newBranchHere': 'Новая ветка здесь...',
3146
3257
  'action.rename': 'Переименовать...',
3147
3258
  'action.delete': 'Удалить',
@@ -3213,6 +3324,8 @@ window.__ModuleLoader__.load({
3213
3324
  'more.fetchAll': 'Изъять со всех серверов',
3214
3325
  'more.deleteRemoteBranch': 'Удалить ветвь на сервере...',
3215
3326
  'more.publish': 'Опубликовать ветвь...',
3327
+ 'more.setUpstreamHead': 'Задать upstream текущей ветки…',
3328
+ 'more.unsetUpstreamHead': 'Убрать upstream текущей ветки',
3216
3329
  'more.addRemote': 'Добавить сервер...',
3217
3330
  'more.removeRemote': 'Удалить сервер...',
3218
3331
  'more.stashTracked': 'Отложить (только отслеживаемое)',
@@ -3275,6 +3388,12 @@ window.__ModuleLoader__.load({
3275
3388
  'composer.changesHint': 'Незакоммиченные изменения',
3276
3389
  'composer.fileHint': 'добавлено: {a} · удалено: {d}',
3277
3390
  'composer.linesHint': '+{ins} −{del} строк',
3391
+ 'modelRoute.pick': 'Выбрать модель',
3392
+ 'modelRoute.trigger': 'Модель сообщений коммитов ИИ',
3393
+ 'modelRoute.modelRow': 'Модель',
3394
+ 'modelRoute.effortRow': 'Рассуждение',
3395
+ 'modelRoute.effortDefault': 'По умолчанию',
3396
+ 'modelRoute.empty': 'Нет доступных моделей',
3278
3397
  'composer.behindHint': 'Новые коммиты upstream (можно забрать)',
3279
3398
  'composer.aheadHint': 'Локальные коммиты, ожидающие отправки',
3280
3399
  'composer.inWorktree': 'Выверен в другом worktree',
@@ -3370,6 +3489,7 @@ window.__ModuleLoader__.load({
3370
3489
  'confirm.cancel': 'Отмена',
3371
3490
  'confirm.ok': 'OK',
3372
3491
  'prompt.newBranch': 'Имя новой ветки (будет создана и выбрана)',
3492
+ 'prompt.createTracking': 'Создать локальную ветку из удалённой',
3373
3493
  'prompt.renameBranch': 'Переименовать ветку',
3374
3494
  'prompt.newTag': 'Имя нового тега',
3375
3495
  'prompt.fromHead': 'текущий HEAD',
@@ -3472,6 +3592,9 @@ window.__ModuleLoader__.load({
3472
3592
  'action.fullscreen': 'Tela cheia',
3473
3593
  'action.exitFullscreen': 'Sair da tela cheia',
3474
3594
  'action.newBranchFrom': 'Nova branch a partir daqui...',
3595
+ 'action.setUpstream': 'Definir upstream…',
3596
+ 'action.unsetUpstream': 'Remover upstream',
3597
+ 'action.createLocalBranch': 'Criar branch local…',
3475
3598
  'action.newBranchHere': 'Nova branch aqui...',
3476
3599
  'action.rename': 'Renomear...',
3477
3600
  'action.delete': 'Excluir',
@@ -3543,6 +3666,8 @@ window.__ModuleLoader__.load({
3543
3666
  'more.fetchAll': 'Fetch de todos os remotos',
3544
3667
  'more.deleteRemoteBranch': 'Eliminar ramo remoto...',
3545
3668
  'more.publish': 'Publicar ramificação...',
3669
+ 'more.setUpstreamHead': 'Definir upstream da branch atual…',
3670
+ 'more.unsetUpstreamHead': 'Remover upstream da branch atual',
3546
3671
  'more.addRemote': 'Adicionar remoto...',
3547
3672
  'more.removeRemote': 'Remover remoto...',
3548
3673
  'more.stashTracked': 'Stash (apenas rastreados)',
@@ -3605,6 +3730,12 @@ window.__ModuleLoader__.load({
3605
3730
  'composer.changesHint': 'Alterações não commitadas',
3606
3731
  'composer.fileHint': '{a} adicionados · {d} removidos',
3607
3732
  'composer.linesHint': '+{ins} −{del} linhas',
3733
+ 'modelRoute.pick': 'Escolher modelo',
3734
+ 'modelRoute.trigger': 'Modelo da mensagem de commit por IA',
3735
+ 'modelRoute.modelRow': 'Modelo',
3736
+ 'modelRoute.effortRow': 'Raciocínio',
3737
+ 'modelRoute.effortDefault': 'Seguir o padrão',
3738
+ 'modelRoute.empty': 'Nenhum modelo disponível',
3608
3739
  'composer.behindHint': 'Novos commits remotos (para pull)',
3609
3740
  'composer.aheadHint': 'Commits locais aguardando push',
3610
3741
  'composer.inWorktree': 'Em checkout em outra worktree',
@@ -3700,6 +3831,7 @@ window.__ModuleLoader__.load({
3700
3831
  'confirm.cancel': 'Cancelar',
3701
3832
  'confirm.ok': 'OK',
3702
3833
  'prompt.newBranch': 'Nome da nova branch (criada e com checkout)',
3834
+ 'prompt.createTracking': 'Criar branch local a partir da remota',
3703
3835
  'prompt.renameBranch': 'Renomear branch',
3704
3836
  'prompt.newTag': 'Nome da nova tag',
3705
3837
  'prompt.fromHead': 'HEAD atual',
@@ -3802,6 +3934,9 @@ window.__ModuleLoader__.load({
3802
3934
  'action.fullscreen': 'Schermo intero',
3803
3935
  'action.exitFullscreen': 'Esci da schermo intero',
3804
3936
  'action.newBranchFrom': 'Nuovo branch da qui...',
3937
+ 'action.setUpstream': 'Imposta upstream…',
3938
+ 'action.unsetUpstream': 'Rimuovi upstream',
3939
+ 'action.createLocalBranch': 'Crea branch locale…',
3805
3940
  'action.newBranchHere': 'Nuovo branch qui...',
3806
3941
  'action.rename': 'Rinomina...',
3807
3942
  'action.delete': 'Elimina',
@@ -3873,6 +4008,8 @@ window.__ModuleLoader__.load({
3873
4008
  'more.fetchAll': 'Fetch da tutti i remoti',
3874
4009
  'more.deleteRemoteBranch': 'Elimina branch remoto...',
3875
4010
  'more.publish': 'Pubblica branch...',
4011
+ 'more.setUpstreamHead': 'Imposta upstream del branch corrente…',
4012
+ 'more.unsetUpstreamHead': 'Rimuovi upstream del branch corrente',
3876
4013
  'more.addRemote': 'Aggiungi remoto...',
3877
4014
  'more.removeRemote': 'Rimuovi remoto...',
3878
4015
  'more.stashTracked': 'Stash (solo tracciati)',
@@ -3935,6 +4072,12 @@ window.__ModuleLoader__.load({
3935
4072
  'composer.changesHint': 'Modifiche non ancora committate',
3936
4073
  'composer.fileHint': '{a} aggiunti · {d} eliminati',
3937
4074
  'composer.linesHint': '+{ins} −{del} righe',
4075
+ 'modelRoute.pick': 'Scegli modello',
4076
+ 'modelRoute.trigger': 'Modello dei messaggi di commit IA',
4077
+ 'modelRoute.modelRow': 'Modello',
4078
+ 'modelRoute.effortRow': 'Ragionamento',
4079
+ 'modelRoute.effortDefault': 'Segui il predefinito',
4080
+ 'modelRoute.empty': 'Nessun modello disponibile',
3938
4081
  'composer.behindHint': 'Nuovi commit upstream (scaricabili)',
3939
4082
  'composer.aheadHint': 'Commit locali in attesa di push',
3940
4083
  'composer.inWorktree': 'Estratto in un altro worktree',
@@ -4030,6 +4173,7 @@ window.__ModuleLoader__.load({
4030
4173
  'confirm.cancel': 'Annulla',
4031
4174
  'confirm.ok': 'OK',
4032
4175
  'prompt.newBranch': 'Nome del nuovo branch (creato e messo in checkout)',
4176
+ 'prompt.createTracking': 'Crea un branch locale dal branch remoto',
4033
4177
  'prompt.renameBranch': 'Rinomina il branch',
4034
4178
  'prompt.newTag': 'Nome del nuovo tag',
4035
4179
  'prompt.fromHead': 'HEAD corrente',
@@ -4132,6 +4276,9 @@ window.__ModuleLoader__.load({
4132
4276
  'action.fullscreen': 'Volledig scherm',
4133
4277
  'action.exitFullscreen': 'Volledig scherm sluiten',
4134
4278
  'action.newBranchFrom': 'Nieuwe branch vanaf hier...',
4279
+ 'action.setUpstream': 'Upstream instellen…',
4280
+ 'action.unsetUpstream': 'Upstream verwijderen',
4281
+ 'action.createLocalBranch': 'Lokale branch maken…',
4135
4282
  'action.newBranchHere': 'Nieuwe branch hier...',
4136
4283
  'action.rename': 'Hernoemen...',
4137
4284
  'action.delete': 'Verwijderen',
@@ -4203,6 +4350,8 @@ window.__ModuleLoader__.load({
4203
4350
  'more.fetchAll': 'Van alle remotes halen',
4204
4351
  'more.deleteRemoteBranch': 'Remote branch verwijderen...',
4205
4352
  'more.publish': 'Branch publiceren...',
4353
+ 'more.setUpstreamHead': 'Upstream instellen voor huidige branch…',
4354
+ 'more.unsetUpstreamHead': 'Upstream van huidige branch verwijderen',
4206
4355
  'more.addRemote': 'Remote toevoegen...',
4207
4356
  'more.removeRemote': 'Remote verwijderen...',
4208
4357
  'more.stashTracked': 'Stash (alleen gevolgd)',
@@ -4265,6 +4414,12 @@ window.__ModuleLoader__.load({
4265
4414
  'composer.changesHint': 'Ongecommitte wijzigingen',
4266
4415
  'composer.fileHint': '{a} toegevoegd · {d} verwijderd',
4267
4416
  'composer.linesHint': '+{ins} −{del} regels',
4417
+ 'modelRoute.pick': 'Kies een model',
4418
+ 'modelRoute.trigger': 'Model voor AI-commitberichten',
4419
+ 'modelRoute.modelRow': 'Model',
4420
+ 'modelRoute.effortRow': 'Redeneren',
4421
+ 'modelRoute.effortDefault': 'Standaard volgen',
4422
+ 'modelRoute.empty': 'Geen modellen beschikbaar',
4268
4423
  'composer.behindHint': 'Nieuwe upstream-commits (op te halen)',
4269
4424
  'composer.aheadHint': 'Lokale commits die wachten op push',
4270
4425
  'composer.inWorktree': 'Uitgecheckt in een andere worktree',
@@ -4360,6 +4515,7 @@ window.__ModuleLoader__.load({
4360
4515
  'confirm.cancel': 'Annuleren',
4361
4516
  'confirm.ok': 'OK',
4362
4517
  'prompt.newBranch': 'Naam van de nieuwe branch (wordt aangemaakt en uitgecheckt)',
4518
+ 'prompt.createTracking': 'Lokale branch maken vanaf de remote branch',
4363
4519
  'prompt.renameBranch': 'Branch hernoemen',
4364
4520
  'prompt.newTag': 'Naam van de nieuwe tag',
4365
4521
  'prompt.fromHead': 'huidige HEAD',
@@ -4462,6 +4618,9 @@ window.__ModuleLoader__.load({
4462
4618
  'action.fullscreen': 'Pełny ekran',
4463
4619
  'action.exitFullscreen': 'Wyjdź z pełnego ekranu',
4464
4620
  'action.newBranchFrom': 'Nowa gałąź stąd...',
4621
+ 'action.setUpstream': 'Ustaw upstream…',
4622
+ 'action.unsetUpstream': 'Usuń upstream',
4623
+ 'action.createLocalBranch': 'Utwórz gałąź lokalną…',
4465
4624
  'action.newBranchHere': 'Nowa gałąź tutaj...',
4466
4625
  'action.rename': 'Zmień nazwę...',
4467
4626
  'action.delete': 'Usuń',
@@ -4533,6 +4692,8 @@ window.__ModuleLoader__.load({
4533
4692
  'more.fetchAll': 'Pobierz ze wszystkich zdalnych',
4534
4693
  'more.deleteRemoteBranch': 'Usuń zdalną gałąź...',
4535
4694
  'more.publish': 'Opublikuj gałąź...',
4695
+ 'more.setUpstreamHead': 'Ustaw upstream bieżącej gałęzi…',
4696
+ 'more.unsetUpstreamHead': 'Usuń upstream bieżącej gałęzi',
4536
4697
  'more.addRemote': 'Dodaj zdalne...',
4537
4698
  'more.removeRemote': 'Usuń zdalne...',
4538
4699
  'more.stashTracked': 'Schowaj (tylko śledzone)',
@@ -4595,6 +4756,12 @@ window.__ModuleLoader__.load({
4595
4756
  'composer.changesHint': 'Niezatwierdzone zmiany',
4596
4757
  'composer.fileHint': 'dodane: {a} · usunięte: {d}',
4597
4758
  'composer.linesHint': '+{ins} −{del} linii',
4759
+ 'modelRoute.pick': 'Wybierz model',
4760
+ 'modelRoute.trigger': 'Model wiadomości commitów AI',
4761
+ 'modelRoute.modelRow': 'Model',
4762
+ 'modelRoute.effortRow': 'Rozumowanie',
4763
+ 'modelRoute.effortDefault': 'Domyślne',
4764
+ 'modelRoute.empty': 'Brak dostępnych modeli',
4598
4765
  'composer.behindHint': 'Nowe commity upstream (do pobrania)',
4599
4766
  'composer.aheadHint': 'Lokalne commity oczekujące na wysłanie',
4600
4767
  'composer.inWorktree': 'Wybrane w innym drzewie roboczym',
@@ -4690,6 +4857,7 @@ window.__ModuleLoader__.load({
4690
4857
  'confirm.cancel': 'Anuluj',
4691
4858
  'confirm.ok': 'OK',
4692
4859
  'prompt.newBranch': 'Nazwa nowej gałęzi (zostanie utworzona i wybrana)',
4860
+ 'prompt.createTracking': 'Utwórz gałąź lokalną z gałęzi zdalnej',
4693
4861
  'prompt.renameBranch': 'Zmień nazwę gałęzi',
4694
4862
  'prompt.newTag': 'Nazwa nowego tagu',
4695
4863
  'prompt.fromHead': 'bieżący HEAD',
@@ -4792,6 +4960,9 @@ window.__ModuleLoader__.load({
4792
4960
  'action.fullscreen': 'Helskärm',
4793
4961
  'action.exitFullscreen': 'Avsluta helskärm',
4794
4962
  'action.newBranchFrom': 'Ny gren härifrån...',
4963
+ 'action.setUpstream': 'Ange upstream…',
4964
+ 'action.unsetUpstream': 'Ta bort upstream',
4965
+ 'action.createLocalBranch': 'Skapa lokal gren…',
4795
4966
  'action.newBranchHere': 'Ny gren här...',
4796
4967
  'action.rename': 'Byt namn...',
4797
4968
  'action.delete': 'Ta bort',
@@ -4863,6 +5034,8 @@ window.__ModuleLoader__.load({
4863
5034
  'more.fetchAll': 'Hämta från alla fjärrar',
4864
5035
  'more.deleteRemoteBranch': 'Ta bort fjärrgren...',
4865
5036
  'more.publish': 'Publicera gren...',
5037
+ 'more.setUpstreamHead': 'Ange upstream för aktuell gren…',
5038
+ 'more.unsetUpstreamHead': 'Ta bort upstream för aktuell gren',
4866
5039
  'more.addRemote': 'Lägg till fjärr...',
4867
5040
  'more.removeRemote': 'Ta bort fjärr...',
4868
5041
  'more.stashTracked': 'Stash (endast spårade)',
@@ -4925,6 +5098,12 @@ window.__ModuleLoader__.load({
4925
5098
  'composer.changesHint': 'Ocommittade ändringar',
4926
5099
  'composer.fileHint': '{a} tillagda · {d} borttagna',
4927
5100
  'composer.linesHint': '+{ins} −{del} rader',
5101
+ 'modelRoute.pick': 'Välj modell',
5102
+ 'modelRoute.trigger': 'Modell för AI-commit-meddelanden',
5103
+ 'modelRoute.modelRow': 'Modell',
5104
+ 'modelRoute.effortRow': 'Resonemang',
5105
+ 'modelRoute.effortDefault': 'Följ standard',
5106
+ 'modelRoute.empty': 'Inga modeller tillgängliga',
4928
5107
  'composer.behindHint': 'Nya upstream-commits (hämtbara)',
4929
5108
  'composer.aheadHint': 'Lokala commits som väntar på push',
4930
5109
  'composer.inWorktree': 'Utcheckad i ett annat arbetsträd',
@@ -5020,6 +5199,7 @@ window.__ModuleLoader__.load({
5020
5199
  'confirm.cancel': 'Avbryt',
5021
5200
  'confirm.ok': 'OK',
5022
5201
  'prompt.newBranch': 'Namn på ny gren (skapas och checkas ut)',
5202
+ 'prompt.createTracking': 'Skapa lokal gren från fjärrgrenen',
5023
5203
  'prompt.renameBranch': 'Byt namn på grenen',
5024
5204
  'prompt.newTag': 'Namn på ny tagg',
5025
5205
  'prompt.fromHead': 'aktuell HEAD',
@@ -5122,6 +5302,9 @@ window.__ModuleLoader__.load({
5122
5302
  'action.fullscreen': 'Tam ekran',
5123
5303
  'action.exitFullscreen': 'Tam ekrandan çık',
5124
5304
  'action.newBranchFrom': 'Buradan yeni dal...',
5305
+ 'action.setUpstream': 'Upstream ayarla…',
5306
+ 'action.unsetUpstream': 'Upstream kaldır',
5307
+ 'action.createLocalBranch': 'Yerel dal oluştur…',
5125
5308
  'action.newBranchHere': 'Burada yeni dal...',
5126
5309
  'action.rename': 'Yeniden adlandır...',
5127
5310
  'action.delete': 'Sil',
@@ -5193,6 +5376,8 @@ window.__ModuleLoader__.load({
5193
5376
  'more.fetchAll': 'Tüm uzaklardan al',
5194
5377
  'more.deleteRemoteBranch': 'Uzak dalı sil...',
5195
5378
  'more.publish': 'Dalı yayınla...',
5379
+ 'more.setUpstreamHead': 'Geçerli dalın upstream\'ini ayarla…',
5380
+ 'more.unsetUpstreamHead': 'Geçerli dalın upstream\'ini kaldır',
5196
5381
  'more.addRemote': 'Uzak ekle...',
5197
5382
  'more.removeRemote': 'Uzağı kaldır...',
5198
5383
  'more.stashTracked': 'Stash (yalnızca izlenen)',
@@ -5255,6 +5440,12 @@ window.__ModuleLoader__.load({
5255
5440
  'composer.changesHint': 'Commitlenmemiş değişiklikler',
5256
5441
  'composer.fileHint': '{a} eklendi · {d} silindi',
5257
5442
  'composer.linesHint': '+{ins} −{del} satır',
5443
+ 'modelRoute.pick': 'Model seç',
5444
+ 'modelRoute.trigger': 'Yapay zekâ commit mesajı modeli',
5445
+ 'modelRoute.modelRow': 'Model',
5446
+ 'modelRoute.effortRow': 'Akıl yürütme',
5447
+ 'modelRoute.effortDefault': 'Varsayılanı izle',
5448
+ 'modelRoute.empty': 'Kullanılabilir model yok',
5258
5449
  'composer.behindHint': 'Yeni upstream commitleri (çekilebilir)',
5259
5450
  'composer.aheadHint': 'Push bekleyen yerel commitler',
5260
5451
  'composer.inWorktree': 'Başka bir worktree’da checkout yapılmış',
@@ -5350,6 +5541,7 @@ window.__ModuleLoader__.load({
5350
5541
  'confirm.cancel': 'İptal',
5351
5542
  'confirm.ok': 'Tamam',
5352
5543
  'prompt.newBranch': 'Yeni dal adı (oluşturulur ve çıkarılır)',
5544
+ 'prompt.createTracking': 'Uzak daldan yerel dal oluştur',
5353
5545
  'prompt.renameBranch': 'Dalı yeniden adlandır',
5354
5546
  'prompt.newTag': 'Yeni etiket adı',
5355
5547
  'prompt.fromHead': 'geçerli HEAD',
@@ -5452,6 +5644,9 @@ window.__ModuleLoader__.load({
5452
5644
  'action.fullscreen': 'Layar penuh',
5453
5645
  'action.exitFullscreen': 'Keluar dari layar penuh',
5454
5646
  'action.newBranchFrom': 'Branch baru dari sini...',
5647
+ 'action.setUpstream': 'Atur upstream…',
5648
+ 'action.unsetUpstream': 'Hapus upstream',
5649
+ 'action.createLocalBranch': 'Buat branch lokal…',
5455
5650
  'action.newBranchHere': 'Branch baru di sini...',
5456
5651
  'action.rename': 'Ganti nama...',
5457
5652
  'action.delete': 'Hapus',
@@ -5523,6 +5718,8 @@ window.__ModuleLoader__.load({
5523
5718
  'more.fetchAll': 'Fetch dari semua remote',
5524
5719
  'more.deleteRemoteBranch': 'Hapus cabang remote...',
5525
5720
  'more.publish': 'Publikasikan cabang...',
5721
+ 'more.setUpstreamHead': 'Atur upstream cabang saat ini…',
5722
+ 'more.unsetUpstreamHead': 'Hapus upstream cabang saat ini',
5526
5723
  'more.addRemote': 'Tambah remote...',
5527
5724
  'more.removeRemote': 'Hapus remote...',
5528
5725
  'more.stashTracked': 'Stash (hanya yang dilacak)',
@@ -5585,6 +5782,12 @@ window.__ModuleLoader__.load({
5585
5782
  'composer.changesHint': 'Perubahan yang belum di-commit',
5586
5783
  'composer.fileHint': '{a} ditambahkan · {d} dihapus',
5587
5784
  'composer.linesHint': '+{ins} −{del} baris',
5785
+ 'modelRoute.pick': 'Pilih model',
5786
+ 'modelRoute.trigger': 'Model pesan commit AI',
5787
+ 'modelRoute.modelRow': 'Model',
5788
+ 'modelRoute.effortRow': 'Penalaran',
5789
+ 'modelRoute.effortDefault': 'Ikuti bawaan',
5790
+ 'modelRoute.empty': 'Tidak ada model yang tersedia',
5588
5791
  'composer.behindHint': 'Commit upstream baru (bisa ditarik)',
5589
5792
  'composer.aheadHint': 'Commit lokal menunggu push',
5590
5793
  'composer.inWorktree': 'Sudah di-checkout di worktree lain',
@@ -5680,6 +5883,7 @@ window.__ModuleLoader__.load({
5680
5883
  'confirm.cancel': 'Batal',
5681
5884
  'confirm.ok': 'OK',
5682
5885
  'prompt.newBranch': 'Nama branch baru (dibuat dan di-checkout)',
5886
+ 'prompt.createTracking': 'Buat branch lokal dari branch remote',
5683
5887
  'prompt.renameBranch': 'Ganti nama branch',
5684
5888
  'prompt.newTag': 'Nama tag baru',
5685
5889
  'prompt.fromHead': 'HEAD saat ini',
@@ -5782,6 +5986,9 @@ window.__ModuleLoader__.load({
5782
5986
  'action.fullscreen': 'Toàn màn hình',
5783
5987
  'action.exitFullscreen': 'Thoát toàn màn hình',
5784
5988
  'action.newBranchFrom': 'Nhánh mới từ đây...',
5989
+ 'action.setUpstream': 'Đặt upstream…',
5990
+ 'action.unsetUpstream': 'Bỏ upstream',
5991
+ 'action.createLocalBranch': 'Tạo nhánh cục bộ…',
5785
5992
  'action.newBranchHere': 'Nhánh mới tại đây...',
5786
5993
  'action.rename': 'Đổi tên...',
5787
5994
  'action.delete': 'Xoá',
@@ -5853,6 +6060,8 @@ window.__ModuleLoader__.load({
5853
6060
  'more.fetchAll': 'Fetch từ mọi remote',
5854
6061
  'more.deleteRemoteBranch': 'Xóa nhánh remote...',
5855
6062
  'more.publish': 'Công bố nhánh...',
6063
+ 'more.setUpstreamHead': 'Đặt upstream cho nhánh hiện tại…',
6064
+ 'more.unsetUpstreamHead': 'Bỏ upstream của nhánh hiện tại',
5856
6065
  'more.addRemote': 'Thêm remote...',
5857
6066
  'more.removeRemote': 'Xóa remote...',
5858
6067
  'more.stashTracked': 'Stash (chỉ theo dõi)',
@@ -5915,6 +6124,12 @@ window.__ModuleLoader__.load({
5915
6124
  'composer.changesHint': 'Thay đổi chưa commit',
5916
6125
  'composer.fileHint': 'thêm {a} · xóa {d} tệp',
5917
6126
  'composer.linesHint': '+{ins} −{del} dòng',
6127
+ 'modelRoute.pick': 'Chọn mô hình',
6128
+ 'modelRoute.trigger': 'Mô hình tin nhắn commit AI',
6129
+ 'modelRoute.modelRow': 'Mô hình',
6130
+ 'modelRoute.effortRow': 'Suy luận',
6131
+ 'modelRoute.effortDefault': 'Theo mặc định',
6132
+ 'modelRoute.empty': 'Không có mô hình nào',
5918
6133
  'composer.behindHint': 'Commit mới từ xa (có thể kéo về)',
5919
6134
  'composer.aheadHint': 'Commit cục bộ chờ đẩy lên',
5920
6135
  'composer.inWorktree': 'Đã được checkout ở worktree khác',
@@ -6010,6 +6225,7 @@ window.__ModuleLoader__.load({
6010
6225
  'confirm.cancel': 'Huỷ',
6011
6226
  'confirm.ok': 'OK',
6012
6227
  'prompt.newBranch': 'Tên nhánh mới (được tạo và checkout)',
6228
+ 'prompt.createTracking': 'Tạo nhánh cục bộ từ nhánh từ xa',
6013
6229
  'prompt.renameBranch': 'Đổi tên nhánh',
6014
6230
  'prompt.newTag': 'Tên thẻ mới',
6015
6231
  'prompt.fromHead': 'HEAD hiện tại',
@@ -6112,6 +6328,9 @@ window.__ModuleLoader__.load({
6112
6328
  'action.fullscreen': 'ملء الشاشة',
6113
6329
  'action.exitFullscreen': 'إنهاء ملء الشاشة',
6114
6330
  'action.newBranchFrom': 'فرع جديد من هنا...',
6331
+ 'action.setUpstream': 'تعيين upstream…',
6332
+ 'action.unsetUpstream': 'إزالة upstream',
6333
+ 'action.createLocalBranch': 'إنشاء فرع محلي…',
6115
6334
  'action.newBranchHere': 'فرع جديد هنا...',
6116
6335
  'action.rename': 'إعادة التسمية...',
6117
6336
  'action.delete': 'حذف',
@@ -6183,6 +6402,8 @@ window.__ModuleLoader__.load({
6183
6402
  'more.fetchAll': 'جلب من كل الأجهزة البعيدة',
6184
6403
  'more.deleteRemoteBranch': 'حذف فرع بعيد...',
6185
6404
  'more.publish': 'نشر الفرع...',
6405
+ 'more.setUpstreamHead': 'تعيين upstream للفرع الحالي…',
6406
+ 'more.unsetUpstreamHead': 'إزالة upstream للفرع الحالي',
6186
6407
  'more.addRemote': 'إضافة جهاز بعيد...',
6187
6408
  'more.removeRemote': 'إزالة جهاز بعيد...',
6188
6409
  'more.stashTracked': 'خبئ (المتتبع فقط)',
@@ -6245,6 +6466,12 @@ window.__ModuleLoader__.load({
6245
6466
  'composer.changesHint': 'تغييرات غير ملتزَم بها',
6246
6467
  'composer.fileHint': 'مضاف {a} · محذوف {d}',
6247
6468
  'composer.linesHint': '+{ins} −{del} أسطر',
6469
+ 'modelRoute.pick': 'اختر نموذجًا',
6470
+ 'modelRoute.trigger': 'نموذج رسائل الالتزام بالذكاء الاصطناعي',
6471
+ 'modelRoute.modelRow': 'النموذج',
6472
+ 'modelRoute.effortRow': 'الاستدلال',
6473
+ 'modelRoute.effortDefault': 'اتبع الافتراضي',
6474
+ 'modelRoute.empty': 'لا توجد نماذج متاحة',
6248
6475
  'composer.behindHint': 'التزامات جديدة في المستودع البعيد (قابلة للسحب)',
6249
6476
  'composer.aheadHint': 'التزامات محلية بانتظار الدفع',
6250
6477
  'composer.inWorktree': 'مسحوب في شجرة عمل أخرى',
@@ -6340,6 +6567,7 @@ window.__ModuleLoader__.load({
6340
6567
  'confirm.cancel': 'إلغاء',
6341
6568
  'confirm.ok': 'موافق',
6342
6569
  'prompt.newBranch': 'اسم الفرع الجديد (يُنشأ ويُبدَّل إليه)',
6570
+ 'prompt.createTracking': 'إنشاء فرع محلي من الفرع البعيد',
6343
6571
  'prompt.renameBranch': 'إعادة تسمية الفرع',
6344
6572
  'prompt.newTag': 'اسم الوسم الجديد',
6345
6573
  'prompt.fromHead': 'HEAD الحالي',
@@ -6442,6 +6670,9 @@ window.__ModuleLoader__.load({
6442
6670
  'action.fullscreen': 'पूर्ण स्क्रीन',
6443
6671
  'action.exitFullscreen': 'पूर्ण स्क्रीन से बाहर',
6444
6672
  'action.newBranchFrom': 'यहाँ से नई शाखा...',
6673
+ 'action.setUpstream': 'अपस्ट्रीम सेट करें…',
6674
+ 'action.unsetUpstream': 'अपस्ट्रीम हटाएँ',
6675
+ 'action.createLocalBranch': 'लोकल शाखा बनाएँ…',
6445
6676
  'action.newBranchHere': 'यहाँ नई शाखा...',
6446
6677
  'action.rename': 'नाम बदलें...',
6447
6678
  'action.delete': 'हटाएँ',
@@ -6513,6 +6744,8 @@ window.__ModuleLoader__.load({
6513
6744
  'more.fetchAll': 'सभी रिमोट से फ़ेच',
6514
6745
  'more.deleteRemoteBranch': 'रिमोट ब्रांच हटाएँ...',
6515
6746
  'more.publish': 'ब्रांच प्रकाशित करें...',
6747
+ 'more.setUpstreamHead': 'वर्तमान शाखा के लिए अपस्ट्रीम सेट करें…',
6748
+ 'more.unsetUpstreamHead': 'वर्तमान शाखा का अपस्ट्रीम हटाएँ',
6516
6749
  'more.addRemote': 'रिमोट जोड़ें...',
6517
6750
  'more.removeRemote': 'रिमोट हटाएँ...',
6518
6751
  'more.stashTracked': 'स्टैश (केवल ट्रैक की गई)',
@@ -6575,6 +6808,12 @@ window.__ModuleLoader__.load({
6575
6808
  'composer.changesHint': 'बिना कमिट बदलाव',
6576
6809
  'composer.fileHint': '{a} जोड़ी गईं · {d} हटाई गईं',
6577
6810
  'composer.linesHint': '+{ins} −{del} पंक्तियाँ',
6811
+ 'modelRoute.pick': 'मॉडल चुनें',
6812
+ 'modelRoute.trigger': 'AI कमिट संदेश मॉडल',
6813
+ 'modelRoute.modelRow': 'मॉडल',
6814
+ 'modelRoute.effortRow': 'तर्क',
6815
+ 'modelRoute.effortDefault': 'डिफ़ॉल्ट का पालन करें',
6816
+ 'modelRoute.empty': 'कोई मॉडल उपलब्ध नहीं',
6578
6817
  'composer.behindHint': 'अपस्ट्रीम के नए कमिट (खींचने योग्य)',
6579
6818
  'composer.aheadHint': 'पुश के लिए प्रतीक्षारत स्थानीय कमिट',
6580
6819
  'composer.inWorktree': 'दूसरे वर्कट्री में चेकआउट',
@@ -6670,6 +6909,7 @@ window.__ModuleLoader__.load({
6670
6909
  'confirm.cancel': 'रद्द करें',
6671
6910
  'confirm.ok': 'ठीक',
6672
6911
  'prompt.newBranch': 'नई शाखा का नाम (बनाई और चेकआउट की जाएगी)',
6912
+ 'prompt.createTracking': 'रिमोट शाखा से लोकल शाखा बनाएँ',
6673
6913
  'prompt.renameBranch': 'शाखा का नाम बदलें',
6674
6914
  'prompt.newTag': 'नए टैग का नाम',
6675
6915
  'prompt.fromHead': 'वर्तमान HEAD',
@@ -6772,6 +7012,9 @@ window.__ModuleLoader__.load({
6772
7012
  'action.fullscreen': 'เต็มหน้าจอ',
6773
7013
  'action.exitFullscreen': 'ออกจากเต็มหน้าจอ',
6774
7014
  'action.newBranchFrom': 'แบรนช์ใหม่จากที่นี่...',
7015
+ 'action.setUpstream': 'ตั้งค่า upstream…',
7016
+ 'action.unsetUpstream': 'ยกเลิก upstream',
7017
+ 'action.createLocalBranch': 'สร้างแบรนช์ในเครื่อง…',
6775
7018
  'action.newBranchHere': 'แบรนช์ใหม่ที่นี่...',
6776
7019
  'action.rename': 'เปลี่ยนชื่อ...',
6777
7020
  'action.delete': 'ลบ',
@@ -6843,6 +7086,8 @@ window.__ModuleLoader__.load({
6843
7086
  'more.fetchAll': 'ฟีตช์จากทุกรีโมต',
6844
7087
  'more.deleteRemoteBranch': 'ลบบรานช์รีโมต...',
6845
7088
  'more.publish': 'เผยแพร่บรานช์...',
7089
+ 'more.setUpstreamHead': 'ตั้งค่า upstream ของแบรนช์ปัจจุบัน…',
7090
+ 'more.unsetUpstreamHead': 'ยกเลิก upstream ของแบรนช์ปัจจุบัน',
6846
7091
  'more.addRemote': 'เพิ่มรีโมต...',
6847
7092
  'more.removeRemote': 'ลบรีโมต...',
6848
7093
  'more.stashTracked': 'สแตช(เฉพาะไฟล์ที่ติดตาม)',
@@ -6905,6 +7150,12 @@ window.__ModuleLoader__.load({
6905
7150
  'composer.changesHint': 'การเปลี่ยนแปลงที่ยังไม่คอมมิต',
6906
7151
  'composer.fileHint': 'เพิ่ม {a} · ลบ {d} ไฟล์',
6907
7152
  'composer.linesHint': '+{ins} −{del} บรรทัด',
7153
+ 'modelRoute.pick': 'เลือกโมเดล',
7154
+ 'modelRoute.trigger': 'โมเดลข้อความคอมมิต AI',
7155
+ 'modelRoute.modelRow': 'โมเดล',
7156
+ 'modelRoute.effortRow': 'การใช้เหตุผล',
7157
+ 'modelRoute.effortDefault': 'ตามค่าเริ่มต้น',
7158
+ 'modelRoute.empty': 'ไม่มีโมเดลที่ใช้ได้',
6908
7159
  'composer.behindHint': 'คอมมิตใหม่จากรีโมต (ดึงได้)',
6909
7160
  'composer.aheadHint': 'คอมมิตในเครื่องรอพุช',
6910
7161
  'composer.inWorktree': 'ถูกเช็คเอาต์ในเวิร์กทรีอื่น',
@@ -7000,6 +7251,7 @@ window.__ModuleLoader__.load({
7000
7251
  'confirm.cancel': 'ยกเลิก',
7001
7252
  'confirm.ok': 'ตกลง',
7002
7253
  'prompt.newBranch': 'ชื่อแบรนช์ใหม่ (สร้างและเช็กเอาต์ให้)',
7254
+ 'prompt.createTracking': 'สร้างแบรนช์ในเครื่องจากแบรนช์รีโมต',
7003
7255
  'prompt.renameBranch': 'เปลี่ยนชื่อแบรนช์',
7004
7256
  'prompt.newTag': 'ชื่อแท็กใหม่',
7005
7257
  'prompt.fromHead': 'HEAD ปัจจุบัน',
@@ -10541,6 +10793,10 @@ window.__ModuleLoader__.load({
10541
10793
  },
10542
10794
  E('span', { className: 'dig-row-icon dig-tone-' + tone + (starred === true ? ' dig-star-filled' : '') }, E(Icon, { name: glyph, size: 12 })),
10543
10795
  E('span', { className: 'dig-row-label' }, props.label === undefined ? entry.name : props.label),
10796
+ // Inline tracking target (issue #18): the title already said "name →
10797
+ // upstream", but nobody hovers a tree to learn a binding. Local rows
10798
+ // show it as muted small text; remote rows ARE the upstream.
10799
+ kind !== 'remote' && kind !== 'tag' && entry.upstream !== null && entry.upstream !== undefined ? E('span', { className: 'dig-row-upstream' }, '→ ' + entry.upstream) : null,
10544
10800
  // A bare 'W' told nobody anything (reported right after the section counts):
10545
10801
  // it means this branch is checked out in ANOTHER working tree, which is why
10546
10802
  // git refuses to check it out here. An icon plus a spelled-out tooltip says so.
@@ -10930,6 +11186,9 @@ window.__ModuleLoader__.load({
10930
11186
  function ChangesPanel(props) {
10931
11187
  const t = props.t
10932
11188
  const summary = props.summary
11189
+ /* The panel-side model picker (issue #19) rides the official model
11190
+ directories, soft-injected into a module ref at apply time — see
11191
+ ModelRoutePicker for why a bare ctx property read cannot work. */
10933
11192
  const [message, setMessage] = useState('')
10934
11193
  const [amend, setAmend] = useState(false)
10935
11194
  const [collapsed, setCollapsed] = useState(() => {
@@ -11228,6 +11487,7 @@ window.__ModuleLoader__.load({
11228
11487
  title: t('changes.aiQuota'),
11229
11488
  onClick: () => { if (message.trim() === '') void aiRun(); else setAiAsk(true) },
11230
11489
  }, aiBusy === true ? t('changes.aiBusy') : t('changes.aiWrite')),
11490
+ E(ModelRoutePicker, { sessionId: props.sessionId, t: t }),
11231
11491
  E('button', {
11232
11492
  type: 'button', className: 'dig-btn dig-btn-primary dig-btn-small',
11233
11493
  disabled: message.trim() === '' || props.busy === true,
@@ -11249,6 +11509,7 @@ window.__ModuleLoader__.load({
11249
11509
  disabled: aiBusy === true || props.busy === true,
11250
11510
  onClick: () => { if (message.trim() === '') void aiRun(); else setAiAsk(true) },
11251
11511
  }, aiBusy === true ? t('changes.aiBusy') : t('changes.aiWrite')),
11512
+ E(ModelRoutePicker, { sessionId: props.sessionId, t: t }),
11252
11513
  E('span', { className: 'dig-commit-ai-hint' }, t('changes.aiQuota')),
11253
11514
  aiUndo === null ? null : E('button', {
11254
11515
  type: 'button', className: 'dig-link',
@@ -13184,7 +13445,25 @@ window.__ModuleLoader__.load({
13184
13445
  { id: 'copy', icon: 'tag', tone: 'secondary', label: t('action.copyName'), run: () => copyValues([entry.name]) },
13185
13446
  null,
13186
13447
  { id: 'favorite', icon: 'star', tone: 'warn', label: t('action.favorite'), active: favorites.indexOf(entry.name) >= 0, run: () => toggleFavoriteBranch(entry.name) },
13187
- { id: 'newBranch', icon: 'plus', tone: 'success', label: t('action.newBranchFrom'), run: () => setDialog({ kind: 'newBranch', from: entry.name }) },
13448
+ /* Upstream management (issue #18). The remote-picker submenu is
13449
+ built INSIDE run() — branches.remote is read at click time, not
13450
+ render time, so branchMenu's useCallback deps stay untouched
13451
+ (remoteBranches is defined AFTER this callback; a render-time
13452
+ reference would be the classic hooks TDZ trap). */
13453
+ isLocal ? { id: 'setUpstream', icon: 'fetch', tone: 'cyan', label: t('action.setUpstream'), disabled: branches === null || branches.remote.length === 0, reason: reason, run: (event) => openMenuAt(event, branches.remote.map((remoteEntry) => ({
13454
+ id: 'upstream:' + remoteEntry.name,
13455
+ icon: 'fetch', tone: 'cyan',
13456
+ label: remoteEntry.name,
13457
+ active: entry.upstream === remoteEntry.name,
13458
+ run: () => { void run('branch-upstream', { branch: entry.name, upstream: remoteEntry.name }) },
13459
+ }))) } : null,
13460
+ isLocal && entry.upstream !== null && entry.upstream !== undefined ? { id: 'unsetUpstream', icon: 'undo', tone: 'secondary', label: t('action.unsetUpstream'), run: () => { void run('branch-upstream', { branch: entry.name, unset: true }) } } : null,
13461
+ /* Remote rows get the tracking form: the local branch is created
13462
+ FROM the remote ref with --track in one step (issue #18 item 5),
13463
+ replacing the plain "new branch from" which could not bind. */
13464
+ entry.remote === true
13465
+ ? { id: 'createTracking', icon: 'branchCreate', tone: 'success', label: t('action.createLocalBranch'), run: () => setDialog({ kind: 'createTracking', from: entry.name, name: entry.name.slice(entry.name.indexOf('/') + 1) }) }
13466
+ : { id: 'newBranch', icon: 'plus', tone: 'success', label: t('action.newBranchFrom'), run: () => setDialog({ kind: 'newBranch', from: entry.name }) },
13188
13467
  isLocal ? { id: 'rename', icon: 'file', tone: 'primary', label: t('action.rename'), run: () => setDialog({ kind: 'renameBranch', from: entry.name }) } : null,
13189
13468
  isLocal ? { id: 'delete', icon: 'trash', tone: 'danger', label: t('action.delete'), danger: true, disabled: entry.head === true, reason: reason, run: () => setDialog({ kind: 'deleteBranch', name: entry.name }) } : null,
13190
13469
  entry.tag === true
@@ -13360,6 +13639,9 @@ window.__ModuleLoader__.load({
13360
13639
  const submitDialog = useCallback(async (state, value) => {
13361
13640
  setDialog(null)
13362
13641
  if (state.kind === 'newBranch') { await run('checkout', { branch: value, create: true, startPoint: state.from }); return }
13642
+ /* From a remote ref (issue #18): --track binds the upstream in the
13643
+ same checkout -b, so "create" and "set upstream" are one motion. */
13644
+ if (state.kind === 'createTracking') { await run('checkout', { branch: value, create: true, startPoint: state.from, track: true }); return }
13363
13645
  if (state.kind === 'renameBranch') { await run('branchRename', { from: state.from, to: value }); return }
13364
13646
  if (state.kind === 'newTag') { await run('tagCreate', { name: value, hash: state.hash }); return }
13365
13647
  if (state.kind === 'deleteTag') { await run('tagDelete', { name: value }); return }
@@ -13784,6 +14066,17 @@ window.__ModuleLoader__.load({
13784
14066
  { id: 'br-delete-remote', label: t('more.deleteRemoteBranch'), danger: true, disabled: remoteBranches.length === 0 || blocked, reason, run: (ev) => pickRemoteBranchMenu(ev) },
13785
14067
  null,
13786
14068
  { id: 'br-publish', label: t('more.publish'), disabled: remoteReady !== true || tracked === true || blocked, reason, run: () => { void run('push', { setUpstream: true, confirm: true }) } },
14069
+ /* Upstream management for the CURRENT branch (issue #18): same
14070
+ verbs as the branch-row menu, reachable without selecting a
14071
+ branch first. Submenu built inside run() like rm-remove. */
14072
+ { id: 'br-upstream', label: t('more.setUpstreamHead'), icon: 'fetch', tone: 'cyan', disabled: headBranch === '' || remoteBranches.length === 0 || blocked, reason, run: (ev) => openMenuAt(ev, remoteBranches.map((remoteEntry) => ({
14073
+ id: 'head-upstream:' + remoteEntry.name,
14074
+ icon: 'fetch', tone: 'cyan',
14075
+ label: remoteEntry.name,
14076
+ active: summary.upstream === remoteEntry.name,
14077
+ run: () => { void run('branch-upstream', { branch: headBranch, upstream: remoteEntry.name }) },
14078
+ }))) },
14079
+ { id: 'br-unset-upstream', label: t('more.unsetUpstreamHead'), icon: 'undo', tone: 'secondary', disabled: headBranch === '' || tracked !== true || blocked, reason, run: () => { void run('branch-upstream', { branch: headBranch, unset: true }) } },
13787
14080
  ] },
13788
14081
  /* —— SCM ▸ Remote ▸ —— */
13789
14082
  { id: 'sub-remote', icon: 'fetch', tone: 'violet', label: t('more.remoteGroup'), items: [
@@ -14175,13 +14468,13 @@ window.__ModuleLoader__.load({
14175
14468
  onClose: () => setRailSettings(false),
14176
14469
  }))
14177
14470
  }
14178
- if (dialog !== null && (dialog.kind === 'newBranch' || dialog.kind === 'renameBranch' || dialog.kind === 'newTag' || dialog.kind === 'deleteTag' || dialog.kind === 'commitMsg')) {
14179
- const titles = { newBranch: t('prompt.newBranch'), renameBranch: t('prompt.renameBranch'), newTag: t('prompt.newTag'), deleteTag: t('prompt.deleteTag'), commitMsg: t('prompt.commitMessage') }
14471
+ if (dialog !== null && (dialog.kind === 'newBranch' || dialog.kind === 'createTracking' || dialog.kind === 'renameBranch' || dialog.kind === 'newTag' || dialog.kind === 'deleteTag' || dialog.kind === 'commitMsg')) {
14472
+ const titles = { newBranch: t('prompt.newBranch'), createTracking: t('prompt.createTracking'), renameBranch: t('prompt.renameBranch'), newTag: t('prompt.newTag'), deleteTag: t('prompt.deleteTag'), commitMsg: t('prompt.commitMessage') }
14180
14473
  overlays.push(E(PromptDialog, {
14181
14474
  key: 'prompt',
14182
14475
  title: titles[dialog.kind],
14183
14476
  placeholder: dialog.from === undefined ? t('prompt.fromHead') : dialog.from,
14184
- initialValue: dialog.kind === 'renameBranch' ? dialog.from : '',
14477
+ initialValue: dialog.kind === 'renameBranch' ? dialog.from : (dialog.kind === 'createTracking' ? dialog.name : ''),
14185
14478
  okLabel: t('confirm.ok'), cancelLabel: t('confirm.cancel'),
14186
14479
  onCancel: () => setDialog(null),
14187
14480
  onSubmit: (value) => { void submitDialog(dialog, value) },
@@ -14710,6 +15003,9 @@ window.__ModuleLoader__.load({
14710
15003
  '.dig-row-head .dig-row-label{font-weight:600}',
14711
15004
  '.dig-row-icon{display:inline-flex;flex:none;opacity:.9}',
14712
15005
  '.dig-row-label{overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;font-weight:600}',
15006
+ // Inline tracking target (issue #18): flex:none so it never starves the
15007
+ // label, capped and ellipsised so a long origin/… cannot push the badges.
15008
+ '.dig-row-upstream{flex:none;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary))}',
14713
15009
  '.dig-row-sub{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;max-width:45%;overflow:hidden;text-overflow:ellipsis}',
14714
15010
  '.dig-row-dir{margin-left:auto;opacity:.75}',
14715
15011
  '.dig-badge{font-size:10px;padding:0 5px;border-radius:999px;corner-shape:round;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);flex:none}',
@@ -15035,6 +15331,22 @@ window.__ModuleLoader__.load({
15035
15331
  '.dig-cs-item-note{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}',
15036
15332
  '.dig-cs-item-counts{display:inline-flex;gap:5px;margin-left:auto;flex:none;font-variant-numeric:tabular-nums;font-size:11px}',
15037
15333
  '.dig-cs-static{color:var(--dsw-alias-label-tertiary);cursor:default}',
15334
+ /* ---- ModelRoutePicker (issue #19) ---- */
15335
+ '.dig-mrp-root{position:relative;display:inline-flex;flex:none;min-width:0}',
15336
+ '.dig-mrp-trigger{display:inline-flex;align-items:center;gap:4px;min-width:0;max-width:220px;font-size:11px;line-height:16px}',
15337
+ '.dig-mrp-trigger[data-open=true]{color:var(--dsw-alias-brand-primary)}',
15338
+ '.dig-mrp-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
15339
+ '.dig-mrp-effort{flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:10px}',
15340
+ '.dig-mrp-menu{max-width:300px}',
15341
+ '.dig-mrp-row .dig-cs-item-name{display:none}',
15342
+ '.dig-mrp-row-label{flex:none}',
15343
+ '.dig-mrp-row-value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-left:auto;color:var(--dsw-alias-label-tertiary)}',
15344
+ '.dig-mrp-row-arrow{flex:none;display:inline-flex;color:var(--dsw-alias-label-tertiary)}',
15345
+ '.dig-mrp-back{color:var(--dsw-alias-label-tertiary)}',
15346
+ '.dig-mrp-group{padding:6px 10px 2px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:14px}',
15347
+ '.dig-mrp-empty{padding:10px;color:var(--dsw-alias-label-tertiary);font-size:12px}',
15348
+ '.dig-mrp-error{padding:6px 10px;margin-bottom:2px;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}',
15349
+ 'body[data-ds-dark-theme] .dig-mrp-error{background:rgba(248,113,113,.16)}',
15038
15350
  ].concat(LANE_TRACE_RULES).join('\n')
15039
15351
 
15040
15352
  /* ============================== plugin ============================== */
@@ -15264,6 +15576,179 @@ window.__ModuleLoader__.load({
15264
15576
  E('span', { className: 'dig-cs-chunk', 'aria-hidden': 'true' }, text)))
15265
15577
  }
15266
15578
 
15579
+ /* ---- ModelRoutePicker (issue #19) ------------------------------------
15580
+ The panel-side model + reasoning picker, bound to the SAME session model
15581
+ route the composer's own selector writes: the host's ui-model-selection
15582
+ plugin exposes the modelDirectories resolver, and directoryFor(sessionId)
15583
+ hands out the session-scoped directory whose select() IS
15584
+ sessions.selectModel — so this picker, the composer pill and the /model
15585
+ command can never disagree. The VIEW is hand-drawn (ModelSelect is not
15586
+ exported from the official package); the DATA and the selection semantics
15587
+ are official end to end. The resolver arrives through a soft inject into
15588
+ a module-level ref (a bare `ctx.modelDirectories` read throws "without
15589
+ inject" on a real host — cordis gates EVERY property read behind an
15590
+ inject declaration); until it lands, and on any older host, the picker
15591
+ hides itself instead of throwing, and the AI-write button still works
15592
+ through the host's served-models fallback. */
15593
+ let modelDirectoriesRef = null
15594
+ const modelDirectorySubs = new Set()
15595
+ function ModelRoutePicker(props) {
15596
+ const t = props.t
15597
+ const sessionId = typeof props.sessionId === 'string' && props.sessionId !== '' ? props.sessionId : 'default'
15598
+ const [injectVersion, setInjectVersion] = useState(0)
15599
+ const [directory, setDirectory] = useState(null)
15600
+ const [state, setState] = useState(null)
15601
+ const [open, setOpen] = useState(false)
15602
+ const [pane, setPane] = useState('root')
15603
+ const rootRef = useRef(null)
15604
+ /* The soft inject may land AFTER first paint (apply runs once, this slot
15605
+ mounts per session view): re-render when the module ref refills. */
15606
+ useEffect(() => {
15607
+ const bump = () => setInjectVersion((v) => v + 1)
15608
+ modelDirectorySubs.add(bump)
15609
+ return () => modelDirectorySubs.delete(bump)
15610
+ }, [])
15611
+ const resolver = modelDirectoriesRef
15612
+ const ok = resolver !== undefined && resolver !== null && typeof resolver.directoryFor === 'function'
15613
+ /* Resolve the session-scoped directory once per session; subscribe AFTER
15614
+ taking the snapshot so a change that lands between the two is not lost. */
15615
+ useEffect(() => {
15616
+ if (ok !== true) return undefined
15617
+ let dir = null
15618
+ try { dir = resolver.directoryFor(sessionId) } catch (error) { void error }
15619
+ if (dir === null || dir === undefined) return undefined
15620
+ setState(dir.store.getSnapshot())
15621
+ setDirectory(dir)
15622
+ /* Snapshot and subscription live on dir.store (uSES-safe); load()/
15623
+ select() hang directly off the directory. */
15624
+ return dir.store.subscribe(() => setState(dir.store.getSnapshot()))
15625
+ }, [ok, resolver, sessionId, injectVersion])
15626
+ /* The catalog loads on mount; failures surface through the store and the
15627
+ menu shows them. A rejected load must not unmount or disable anything. */
15628
+ useEffect(() => {
15629
+ if (directory === null) return undefined
15630
+ directory.load().catch(() => {})
15631
+ return undefined
15632
+ }, [directory])
15633
+ /* Outside close. pointerdown + capture: the change list captures
15634
+ pointerdown for the rubber band, so a plain mousedown listener can
15635
+ miss the very click that opened another menu (v0.19.2 lesson). */
15636
+ useEffect(() => {
15637
+ if (open !== true) return undefined
15638
+ const close = (event) => {
15639
+ const root = rootRef.current
15640
+ if (root !== null && root.contains(event.target) === true) return
15641
+ setOpen(false)
15642
+ setPane('root')
15643
+ }
15644
+ document.addEventListener('pointerdown', close, true)
15645
+ return () => document.removeEventListener('pointerdown', close, true)
15646
+ }, [open])
15647
+ if (ok !== true || directory === null) return null
15648
+ const s = state === null ? directory.getSnapshot() : state
15649
+ const current = s === null || s.current === undefined ? null : s.current
15650
+ const busy = s !== null && s.pending !== null && s.pending !== undefined
15651
+ const groups = s === null || s.groups === undefined ? [] : s.groups
15652
+ /* The current route's reasoning table, looked up through the SAME
15653
+ directory groups the menu renders (official effectiveEffort rule). */
15654
+ let routeGroup = null
15655
+ let routeModel = null
15656
+ if (current !== null) {
15657
+ routeGroup = groups.filter((row) => row.id === current.provider)[0] || null
15658
+ routeModel = routeGroup === null ? null : routeGroup.models.filter((row) => row.id === current.model)[0] || null
15659
+ }
15660
+ const reasoning = routeModel === null || routeModel.reasoning === undefined ? undefined : routeModel.reasoning
15661
+ const effectiveEffort = current === null ? undefined : current.reasoningEffort !== undefined ? current.reasoningEffort : reasoning === undefined ? undefined : reasoning.defaultEffort
15662
+ const effortName = (effort) => {
15663
+ if (reasoning !== undefined) {
15664
+ const hit = reasoning.efforts.filter((row) => row.id === effort)[0]
15665
+ if (hit !== undefined) return hit.name
15666
+ }
15667
+ return effort === undefined ? t('modelRoute.effortDefault') : effort
15668
+ }
15669
+ const effortLabel = current === null ? '' : reasoning === undefined
15670
+ ? (current.reasoningEffort === undefined ? '' : current.reasoningEffort)
15671
+ : (effectiveEffort === undefined ? t('modelRoute.effortDefault') : effortName(effectiveEffort))
15672
+ const modelName = current === null ? '' : routeModel === null ? current.model : routeModel.name
15673
+ const effortChoices = reasoning === undefined ? [] : ([
15674
+ ...(reasoning.defaultEffort === undefined ? [{ key: 'provider-default', effort: undefined, label: t('modelRoute.effortDefault') }] : []),
15675
+ ...reasoning.efforts.map((row) => ({ key: row.id, effort: row.id, label: row.name })),
15676
+ ])
15677
+ const closeMenu = () => { setOpen(false); setPane('root') }
15678
+ const choose = (selection) => {
15679
+ void directory.select(selection).then((result) => {
15680
+ if (result !== undefined && result !== null && result.ok !== true) return
15681
+ closeMenu()
15682
+ }).catch(() => {})
15683
+ }
15684
+ const triggerTitle = t('modelRoute.trigger') + (current === null ? '' : ' · ' + current.provider + '/' + current.model + (effortLabel === '' ? '' : ' · ' + effortLabel))
15685
+ return E('div', { className: 'dig-mrp-root', ref: rootRef },
15686
+ E('button', {
15687
+ type: 'button',
15688
+ className: 'dig-btn dig-btn-small dig-mrp-trigger',
15689
+ 'data-open': open === true ? 'true' : undefined,
15690
+ title: triggerTitle,
15691
+ onClick: () => { setOpen(!open); setPane('root') },
15692
+ },
15693
+ E(Icon, { name: 'settings', size: 11 }),
15694
+ E('span', { className: 'dig-mrp-label' }, current === null ? t('modelRoute.pick') : modelName),
15695
+ effortLabel === '' ? null : E('span', { className: 'dig-mrp-effort' }, effortLabel),
15696
+ E(Icon, { name: 'chevron', size: 9 })),
15697
+ open !== true ? null : E('div', { className: 'dig-cs-menu dig-mrp-menu', role: 'menu' },
15698
+ s !== null && typeof s.error === 'string' && s.error !== ''
15699
+ ? E('div', { className: 'dig-mrp-error', role: 'status' }, s.error)
15700
+ : null,
15701
+ pane === 'root'
15702
+ ? [
15703
+ E('button', {
15704
+ type: 'button', key: 'model', className: 'dig-cs-item dig-mrp-row', disabled: busy === true,
15705
+ onClick: () => setPane('model'),
15706
+ },
15707
+ E('span', { className: 'dig-mrp-row-label' }, t('modelRoute.modelRow')),
15708
+ E('span', { className: 'dig-mrp-row-value' }, current === null ? t('modelRoute.pick') : modelName),
15709
+ E('span', { className: 'dig-mrp-row-arrow' }, E(Icon, { name: 'chevron', size: 9 }))),
15710
+ E('button', {
15711
+ type: 'button', key: 'effort', className: 'dig-cs-item dig-mrp-row', disabled: busy === true || current === null || effortChoices.length === 0,
15712
+ onClick: () => setPane('effort'),
15713
+ },
15714
+ E('span', { className: 'dig-mrp-row-label' }, t('modelRoute.effortRow')),
15715
+ E('span', { className: 'dig-mrp-row-value' }, effortChoices.length === 0 ? '' : (effortLabel === '' ? t('modelRoute.effortDefault') : effortLabel)),
15716
+ E('span', { className: 'dig-mrp-row-arrow' }, E(Icon, { name: 'chevron', size: 9 }))),
15717
+ ]
15718
+ : [
15719
+ E('button', {
15720
+ type: 'button', key: 'back', className: 'dig-cs-item dig-mrp-back', disabled: busy === true,
15721
+ onClick: () => setPane('root'),
15722
+ }, '‹ ' + (pane === 'model' ? t('modelRoute.modelRow') : t('modelRoute.effortRow'))),
15723
+ ],
15724
+ pane === 'model' && groups.length === 0
15725
+ ? E('div', { className: 'dig-mrp-empty' }, t('modelRoute.empty'))
15726
+ : null,
15727
+ pane === 'model' && groups.map((group) => E(Fragment, { key: group.id },
15728
+ E('div', { className: 'dig-mrp-group' }, group.name === undefined || group.name === null || group.name === '' ? group.id : group.name),
15729
+ group.models.map((model) => {
15730
+ const selected = current !== null && current.provider === group.id && current.model === model.id
15731
+ return E('button', {
15732
+ type: 'button', key: model.id, className: 'dig-cs-item', 'data-current': selected === true ? 'true' : undefined,
15733
+ disabled: busy === true,
15734
+ title: group.id + '/' + model.id,
15735
+ onClick: () => choose({ provider: group.id, model: model.id }),
15736
+ },
15737
+ E('span', { className: 'dig-cs-item-name' }, model.name === undefined || model.name === null ? model.id : model.name),
15738
+ E('span', { className: 'dig-cs-item-note' }, selected === true ? E(Icon, { name: 'check', size: 11 }) : null))
15739
+ }))),
15740
+ pane === 'effort' && current !== null && effortChoices.map((level) => {
15741
+ const selected = effectiveEffort === level.effort
15742
+ return E('button', {
15743
+ type: 'button', key: level.key, className: 'dig-cs-item', 'data-current': selected === true ? 'true' : undefined,
15744
+ disabled: busy === true,
15745
+ onClick: () => choose({ provider: current.provider, model: current.model, reasoningEffort: level.effort }),
15746
+ },
15747
+ E('span', { className: 'dig-cs-item-name' }, level.label),
15748
+ E('span', { className: 'dig-cs-item-note' }, selected === true ? E(Icon, { name: 'check', size: 11 }) : null))
15749
+ })))
15750
+ }
15751
+
15267
15752
  function ComposerGitStatus(props) {
15268
15753
  const ctx = props.ctx
15269
15754
  const t = props.t
@@ -16165,6 +16650,20 @@ window.__ModuleLoader__.load({
16165
16650
  }, 'dsh-ide-git: session-switch prewarm')
16166
16651
  })
16167
16652
 
16653
+ /* ---- panel model picker: the official directories resolver ---- */
16654
+ /* ui-model-selection exposes the resolver as a CONTEXT PROPERTY, and
16655
+ cordis refuses bare property reads ("cannot get property … without
16656
+ inject") — the only legal consumer path is a declared inject. Soft:
16657
+ an older host without the service never fires this callback, the ref
16658
+ stays null and the picker hides itself (the AI-write button keeps its
16659
+ served-models fallback). Subscribers re-render the mounted pickers. */
16660
+ ctx.inject(['modelDirectories'], (mdCtx) => {
16661
+ const md = mdCtx.get('modelDirectories')
16662
+ if (md === undefined || md === null || typeof md.directoryFor !== 'function') return
16663
+ modelDirectoriesRef = md
16664
+ for (const notify of modelDirectorySubs) { try { notify() } catch (error) { void error } }
16665
+ })
16666
+
16168
16667
  /* ---- settings: the row Config is the rail's source of truth ---- */
16169
16668
  /* dsh >= 0.1.7 serves a plugin's row Config to the browser through
16170
16669
  ctx.configForms; from then on the settings card and the rail read that one
package/src/index.js CHANGED
@@ -470,7 +470,7 @@ function withRepoLock(key, work) {
470
470
  }
471
471
 
472
472
  const WRITE_METHODS = new Set([
473
- 'stage', 'unstage', 'discard', 'commit', 'checkout', 'branchCreate', 'branchRename',
473
+ 'stage', 'unstage', 'discard', 'commit', 'checkout', 'branchCreate', 'branchRename', 'branch-upstream',
474
474
  'branchDelete', 'merge', 'rebase', 'cherryPick', 'revert', 'reset', 'fetch', 'pull',
475
475
  'push', 'stashPush', 'stashApply', 'stashDrop', 'stashClear', 'tagCreate', 'tagDelete', 'tagDeleteRemote', 'pushTags', 'clone', 'remoteAdd', 'remoteRemove', 'branchDeleteRemote', 'undoApply',
476
476
  ])
@@ -1003,6 +1003,9 @@ async function checkout(payload) {
1003
1003
  const name = requireRef(payload.branch, 'branch')
1004
1004
  const start = typeof payload.startPoint === 'string' && payload.startPoint.trim() !== '' ? requireRef(payload.startPoint, 'startPoint') : undefined
1005
1005
  const args = ['checkout', '-b', name]
1006
+ /* track (issue #18): "create a local branch from a remote one" must bind
1007
+ the upstream in the same breath — checkout -b alone never does. */
1008
+ if (payload.track === true && start !== undefined) args.push('--track')
1006
1009
  if (start !== undefined) args.push(start)
1007
1010
  await git(root, args)
1008
1011
  return { branch: name, created: true }
@@ -1022,6 +1025,25 @@ async function branchCreate(payload) {
1022
1025
  return { branch: name }
1023
1026
  }
1024
1027
 
1028
+ /* Upstream management (issue #18): rebind or drop a branch's tracking remote.
1029
+ git refuses --set-upstream-to with a ref it cannot resolve, so a typo dies
1030
+ here instead of silently writing branch.<name>.merge. */
1031
+ async function branchUpstream(payload) {
1032
+ const cwd = cwdOf(payload)
1033
+ const root = await repoRootOf(cwd, payload)
1034
+ const branch = requireRef(payload.branch, 'branch')
1035
+ if (payload.unset === true) {
1036
+ /* --unset-upstream errors on a branch that has none; the client only
1037
+ offers the verb when the row shows an upstream, and git's own message
1038
+ is the right one to surface if they raced. */
1039
+ await git(root, ['branch', '--unset-upstream', branch])
1040
+ return { branch, upstream: null }
1041
+ }
1042
+ const upstream = requireRef(payload.upstream, 'upstream')
1043
+ await git(root, ['branch', '--set-upstream-to=' + upstream, branch])
1044
+ return { branch, upstream }
1045
+ }
1046
+
1025
1047
  async function branchRename(payload) {
1026
1048
  const cwd = cwdOf(payload)
1027
1049
  const root = await repoRootOf(cwd, payload)
@@ -1766,6 +1788,28 @@ function failureTextOf(failure) {
1766
1788
  return String(failure.code === undefined ? failure : failure.code)
1767
1789
  }
1768
1790
 
1791
+ /** The no-route fallback: the first provider this host serves that lists at
1792
+ least one model, and that provider's first model. Issue #19 — a fresh
1793
+ session (no model picked yet) used to fail the panel button with 'no-route'
1794
+ before the user ever saw a message; a commit message is too small a task to
1795
+ lecture anyone about routing. Errors per provider stay silent on purpose:
1796
+ a provider that fails its catalog simply is not a fallback candidate. */
1797
+ async function fallbackRouteOf() {
1798
+ const llm = commitServices.llm
1799
+ for (const row of llm.listProviders()) {
1800
+ try {
1801
+ const models = await llm.listModels(row.id)
1802
+ const model = models[0]
1803
+ if (model !== undefined && model !== null && typeof model.id === 'string' && model.id !== '') {
1804
+ return { provider: row.id, model: model.id }
1805
+ }
1806
+ } catch (error) {
1807
+ void error
1808
+ }
1809
+ }
1810
+ return null
1811
+ }
1812
+
1769
1813
  /** Route the call exactly like the session does when it talks to the model. */
1770
1814
  async function commitRouteOf(payload) {
1771
1815
  const wanted = typeof payload.model === 'string' ? payload.model.trim() : ''
@@ -1779,7 +1823,13 @@ async function commitRouteOf(payload) {
1779
1823
  try { header = session.requestHeader() } catch (error) { header = null; void error }
1780
1824
  const config = header === null || header === undefined || header.config === undefined || header.config === null ? {} : header.config
1781
1825
  if (typeof config.provider !== 'string' || typeof config.model !== 'string') {
1782
- throw new PanelError('no-route', 'this session has no model route yet; pick a model in the session first', 502)
1826
+ /* Issue #19: 'no-route' is the failure every blank session hit. The
1827
+ served fallback below keeps the button useful on a brand-new session;
1828
+ the panel's model picker (this release) is the user's visible way to
1829
+ steer it, and it drives the SAME session route once a model is picked. */
1830
+ const fallback = await fallbackRouteOf()
1831
+ if (fallback !== null) return { provider: fallback.provider, model: fallback.model, source: 'fallback' }
1832
+ throw new PanelError('no-models', 'no model route and no served models to fall back to; pick a model in the session first', 502)
1783
1833
  }
1784
1834
  return { provider: config.provider, model: config.model, source: 'session' }
1785
1835
  }
@@ -1999,6 +2049,7 @@ const METHODS = {
1999
2049
  checkout,
2000
2050
  branchCreate,
2001
2051
  branchRename,
2052
+ 'branch-upstream': branchUpstream,
2002
2053
  branchDelete,
2003
2054
  merge,
2004
2055
  rebase,