dsh-ide-git 0.10.0 → 0.12.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 +2 -2
- package/package.json +3 -3
- package/src/client.js +263 -39
- package/src/index.js +110 -4
package/dsh.plugin.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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": {
|
|
7
|
-
"dsh": ">=0.1.2
|
|
7
|
+
"dsh": ">=0.1.6-alpha.2"
|
|
8
8
|
},
|
|
9
9
|
"contributes": {
|
|
10
10
|
"tools": [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ide-git",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18",
|
|
22
|
-
"dsh": ">=0.1.2
|
|
22
|
+
"dsh": ">=0.1.6-alpha.2"
|
|
23
23
|
},
|
|
24
24
|
"dsh": {
|
|
25
25
|
"bundle": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"license": "MIT",
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
69
|
-
"@deepseek-ai/dsh": ">=0.1.2
|
|
69
|
+
"@deepseek-ai/dsh": ">=0.1.6-alpha.2",
|
|
70
70
|
"dsh-better-sidebar": ">=0.12.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependenciesMeta": {
|
package/src/client.js
CHANGED
|
@@ -167,8 +167,11 @@ window.__ModuleLoader__.load({
|
|
|
167
167
|
'settings.commit.title': 'AI 提交信息',
|
|
168
168
|
'settings.commit.model': '使用的模型',
|
|
169
169
|
'settings.commit.modelHint': '留空 = 跟随当前会话(provider/model)',
|
|
170
|
+
'settings.commit.modelFollow': '跟随当前会话',
|
|
170
171
|
'settings.commit.reasoning': '思考强度',
|
|
171
|
-
'settings.commit.reasoningHint': '留空 =
|
|
172
|
+
'settings.commit.reasoningHint': '留空 = 不思考',
|
|
173
|
+
'settings.commit.reasoningDefault': '不思考(默认)',
|
|
174
|
+
'settings.commit.reasoningFollowHint': '跟随会话时固定不思考;选择固定模型后可再选思考强度',
|
|
172
175
|
'settings.commit.prompt': '补充提示词',
|
|
173
176
|
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,不超过 72 字符',
|
|
174
177
|
'settings.commit.tooLong': '超过 {n} 个字符的部分不会保存',
|
|
@@ -352,8 +355,11 @@ window.__ModuleLoader__.load({
|
|
|
352
355
|
'settings.commit.title': 'AI commit message',
|
|
353
356
|
'settings.commit.model': 'Model',
|
|
354
357
|
'settings.commit.modelHint': 'Empty = follow this session (provider/model)',
|
|
358
|
+
'settings.commit.modelFollow': 'Follow the current session',
|
|
355
359
|
'settings.commit.reasoning': 'Reasoning effort',
|
|
356
|
-
'settings.commit.reasoningHint': 'Empty =
|
|
360
|
+
'settings.commit.reasoningHint': 'Empty = no reasoning',
|
|
361
|
+
'settings.commit.reasoningDefault': 'No reasoning (default)',
|
|
362
|
+
'settings.commit.reasoningFollowHint': 'Following the session always uses no reasoning; pick a fixed model to choose an effort',
|
|
357
363
|
'settings.commit.prompt': 'Extra instructions',
|
|
358
364
|
'settings.commit.promptHint': 'e.g. Conventional Commits, Chinese subject, at most 72 characters',
|
|
359
365
|
'settings.commit.tooLong': 'Only the first {n} characters are saved',
|
|
@@ -545,8 +551,11 @@ window.__ModuleLoader__.load({
|
|
|
545
551
|
'settings.commit.title': 'AI 提交訊息',
|
|
546
552
|
'settings.commit.model': '使用嘅模型',
|
|
547
553
|
'settings.commit.modelHint': '留空 = 跟隨當前對話(provider/model)',
|
|
554
|
+
'settings.commit.modelFollow': '跟隨當前對話',
|
|
548
555
|
'settings.commit.reasoning': '思考強度',
|
|
549
|
-
'settings.commit.reasoningHint': '留空 =
|
|
556
|
+
'settings.commit.reasoningHint': '留空 = 不思考',
|
|
557
|
+
'settings.commit.reasoningDefault': '不思考(預設)',
|
|
558
|
+
'settings.commit.reasoningFollowHint': '跟隨對話時固定不思考;選擇固定模型後可再選思考強度',
|
|
550
559
|
'settings.commit.prompt': '補充提示詞',
|
|
551
560
|
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,唔超過 72 字',
|
|
552
561
|
'settings.commit.tooLong': '超過 {n} 個字嘅部分唔會儲存',
|
|
@@ -730,8 +739,11 @@ window.__ModuleLoader__.load({
|
|
|
730
739
|
'settings.commit.title': 'AI 提交訊息',
|
|
731
740
|
'settings.commit.model': '使用的模型',
|
|
732
741
|
'settings.commit.modelHint': '留空 = 跟隨目前工作階段(provider/model)',
|
|
742
|
+
'settings.commit.modelFollow': '跟隨目前工作階段',
|
|
733
743
|
'settings.commit.reasoning': '思考強度',
|
|
734
|
-
'settings.commit.reasoningHint': '留空 =
|
|
744
|
+
'settings.commit.reasoningHint': '留空 = 不思考',
|
|
745
|
+
'settings.commit.reasoningDefault': '不思考(預設)',
|
|
746
|
+
'settings.commit.reasoningFollowHint': '跟隨工作階段時固定不思考;選擇固定模型後可再選思考強度',
|
|
735
747
|
'settings.commit.prompt': '補充提示詞',
|
|
736
748
|
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,不超過 72 字元',
|
|
737
749
|
'settings.commit.tooLong': '超過 {n} 個字元的部分不會儲存',
|
|
@@ -915,8 +927,11 @@ window.__ModuleLoader__.load({
|
|
|
915
927
|
'settings.commit.title': 'AI 提交訊息',
|
|
916
928
|
'settings.commit.model': '使用的模型',
|
|
917
929
|
'settings.commit.modelHint': '留空 = 跟隨目前工作階段(provider/model)',
|
|
930
|
+
'settings.commit.modelFollow': '跟隨目前工作階段',
|
|
918
931
|
'settings.commit.reasoning': '思考強度',
|
|
919
|
-
'settings.commit.reasoningHint': '留空 =
|
|
932
|
+
'settings.commit.reasoningHint': '留空 = 不思考',
|
|
933
|
+
'settings.commit.reasoningDefault': '不思考(預設)',
|
|
934
|
+
'settings.commit.reasoningFollowHint': '跟隨工作階段時固定不思考;選擇固定模型後可再選思考強度',
|
|
920
935
|
'settings.commit.prompt': '補充提示詞',
|
|
921
936
|
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,不超過 72 字元',
|
|
922
937
|
'settings.commit.tooLong': '超過 {n} 個字元的部分不會儲存',
|
|
@@ -1100,8 +1115,11 @@ window.__ModuleLoader__.load({
|
|
|
1100
1115
|
'settings.commit.title': 'AI コミットメッセージ',
|
|
1101
1116
|
'settings.commit.model': '使用するモデル',
|
|
1102
1117
|
'settings.commit.modelHint': '空欄 = 現在のセッションに従う(provider/model)',
|
|
1118
|
+
'settings.commit.modelFollow': '現在のセッションに従う',
|
|
1103
1119
|
'settings.commit.reasoning': '思考の強度',
|
|
1104
|
-
'settings.commit.reasoningHint': '空欄 =
|
|
1120
|
+
'settings.commit.reasoningHint': '空欄 = 思考なし',
|
|
1121
|
+
'settings.commit.reasoningDefault': '思考なし(既定)',
|
|
1122
|
+
'settings.commit.reasoningFollowHint': 'セッション追従では常に思考なし。固定モデルを選ぶと強度を選べます',
|
|
1105
1123
|
'settings.commit.prompt': '追加の指示',
|
|
1106
1124
|
'settings.commit.promptHint': '例:Conventional Commits、subject は日本語、72 文字以内',
|
|
1107
1125
|
'settings.commit.tooLong': '{n} 文字を超える部分は保存されません',
|
|
@@ -1285,8 +1303,11 @@ window.__ModuleLoader__.load({
|
|
|
1285
1303
|
'settings.commit.title': 'AI 커밋 메시지',
|
|
1286
1304
|
'settings.commit.model': '사용할 모델',
|
|
1287
1305
|
'settings.commit.modelHint': '비우면 현재 세션을 따릅니다(provider/model)',
|
|
1306
|
+
'settings.commit.modelFollow': '현재 세션 따르기',
|
|
1288
1307
|
'settings.commit.reasoning': '추론 강도',
|
|
1289
|
-
'settings.commit.reasoningHint': '
|
|
1308
|
+
'settings.commit.reasoningHint': '비워 두면 추론 없음',
|
|
1309
|
+
'settings.commit.reasoningDefault': '추론 없음(기본값)',
|
|
1310
|
+
'settings.commit.reasoningFollowHint': '세션을 따를 때는 항상 추론을 건너뜁니다. 고정 모델을 선택하면 강도를 고를 수 있습니다',
|
|
1290
1311
|
'settings.commit.prompt': '추가 지시',
|
|
1291
1312
|
'settings.commit.promptHint': '예: Conventional Commits, 제목은 한국어, 72자 이내',
|
|
1292
1313
|
'settings.commit.tooLong': '{n}자를 넘는 부분은 저장되지 않습니다',
|
|
@@ -1470,8 +1491,11 @@ window.__ModuleLoader__.load({
|
|
|
1470
1491
|
'settings.commit.title': 'KI-Commit-Nachricht',
|
|
1471
1492
|
'settings.commit.model': 'Modell',
|
|
1472
1493
|
'settings.commit.modelHint': 'Leer = dieser Sitzung folgen (provider/model)',
|
|
1494
|
+
'settings.commit.modelFollow': 'Dieser Sitzung folgen',
|
|
1473
1495
|
'settings.commit.reasoning': 'Denkaufwand',
|
|
1474
|
-
'settings.commit.reasoningHint': 'Leer =
|
|
1496
|
+
'settings.commit.reasoningHint': 'Leer = kein Denken',
|
|
1497
|
+
'settings.commit.reasoningDefault': 'Kein Denken (Standard)',
|
|
1498
|
+
'settings.commit.reasoningFollowHint': 'Beim Folgen der Sitzung wird nie gedacht; wähle ein festes Modell, um eine Stufe zu wählen',
|
|
1475
1499
|
'settings.commit.prompt': 'Zusätzliche Anweisungen',
|
|
1476
1500
|
'settings.commit.promptHint': 'z. B. Conventional Commits, Betreff auf Deutsch, höchstens 72 Zeichen',
|
|
1477
1501
|
'settings.commit.tooLong': 'Nur die ersten {n} Zeichen werden gespeichert',
|
|
@@ -1655,8 +1679,11 @@ window.__ModuleLoader__.load({
|
|
|
1655
1679
|
'settings.commit.title': 'Message de commit par IA',
|
|
1656
1680
|
'settings.commit.model': 'Modèle',
|
|
1657
1681
|
'settings.commit.modelHint': 'Vide = suivre cette session (provider/model)',
|
|
1682
|
+
'settings.commit.modelFollow': 'Suivre cette session',
|
|
1658
1683
|
'settings.commit.reasoning': 'Effort de raisonnement',
|
|
1659
|
-
'settings.commit.reasoningHint': 'Vide =
|
|
1684
|
+
'settings.commit.reasoningHint': 'Vide = sans raisonnement',
|
|
1685
|
+
'settings.commit.reasoningDefault': 'Sans raisonnement (par défaut)',
|
|
1686
|
+
'settings.commit.reasoningFollowHint': 'En suivant la session, jamais de raisonnement ; choisissez un modèle fixe pour en choisir l\'intensité',
|
|
1660
1687
|
'settings.commit.prompt': 'Instructions supplémentaires',
|
|
1661
1688
|
'settings.commit.promptHint': 'ex. Conventional Commits, sujet en français, 72 caractères maximum',
|
|
1662
1689
|
'settings.commit.tooLong': 'Seuls les {n} premiers caractères sont enregistrés',
|
|
@@ -1840,8 +1867,11 @@ window.__ModuleLoader__.load({
|
|
|
1840
1867
|
'settings.commit.title': 'Сообщение коммита от ИИ',
|
|
1841
1868
|
'settings.commit.model': 'Модель',
|
|
1842
1869
|
'settings.commit.modelHint': 'Пусто = как в этой сессии (provider/model)',
|
|
1870
|
+
'settings.commit.modelFollow': 'Как в этой сессии',
|
|
1843
1871
|
'settings.commit.reasoning': 'Усилие рассуждения',
|
|
1844
|
-
'settings.commit.reasoningHint': 'Пусто =
|
|
1872
|
+
'settings.commit.reasoningHint': 'Пусто = без размышлений',
|
|
1873
|
+
'settings.commit.reasoningDefault': 'Без размышлений (по умолчанию)',
|
|
1874
|
+
'settings.commit.reasoningFollowHint': 'При следовании за сессией размышления всегда выключены; выберите фиксированную модель, чтобы выбрать уровень',
|
|
1845
1875
|
'settings.commit.prompt': 'Дополнительные указания',
|
|
1846
1876
|
'settings.commit.promptHint': 'напр. Conventional Commits, тема по-русски, до 72 символов',
|
|
1847
1877
|
'settings.commit.tooLong': 'Сохраняются только первые {n} символов',
|
|
@@ -2025,8 +2055,11 @@ window.__ModuleLoader__.load({
|
|
|
2025
2055
|
'settings.commit.title': 'Mensagem de commit por IA',
|
|
2026
2056
|
'settings.commit.model': 'Modelo',
|
|
2027
2057
|
'settings.commit.modelHint': 'Vazio = seguir esta sessão (provider/model)',
|
|
2058
|
+
'settings.commit.modelFollow': 'Seguir esta sessão',
|
|
2028
2059
|
'settings.commit.reasoning': 'Esforço de raciocínio',
|
|
2029
|
-
'settings.commit.reasoningHint': 'Vazio =
|
|
2060
|
+
'settings.commit.reasoningHint': 'Vazio = sem raciocínio',
|
|
2061
|
+
'settings.commit.reasoningDefault': 'Sem raciocínio (padrão)',
|
|
2062
|
+
'settings.commit.reasoningFollowHint': 'Ao seguir a sessão, o raciocínio fica sempre desativado; escolha um modelo fixo para escolher o nível',
|
|
2030
2063
|
'settings.commit.prompt': 'Instruções extras',
|
|
2031
2064
|
'settings.commit.promptHint': 'ex.: Conventional Commits, assunto em português, até 72 caracteres',
|
|
2032
2065
|
'settings.commit.tooLong': 'Só os primeiros {n} caracteres são salvos',
|
|
@@ -2210,8 +2243,11 @@ window.__ModuleLoader__.load({
|
|
|
2210
2243
|
'settings.commit.title': 'Messaggio di commit con IA',
|
|
2211
2244
|
'settings.commit.model': 'Modello',
|
|
2212
2245
|
'settings.commit.modelHint': 'Vuoto = segui questa sessione (provider/model)',
|
|
2246
|
+
'settings.commit.modelFollow': 'Segui questa sessione',
|
|
2213
2247
|
'settings.commit.reasoning': 'Sforzo di ragionamento',
|
|
2214
|
-
'settings.commit.reasoningHint': 'Vuoto =
|
|
2248
|
+
'settings.commit.reasoningHint': 'Vuoto = senza ragionamento',
|
|
2249
|
+
'settings.commit.reasoningDefault': 'Senza ragionamento (predefinito)',
|
|
2250
|
+
'settings.commit.reasoningFollowHint': 'Seguendo la sessione il ragionamento resta sempre spento; scegli un modello fisso per sceglierne il livello',
|
|
2215
2251
|
'settings.commit.prompt': 'Istruzioni aggiuntive',
|
|
2216
2252
|
'settings.commit.promptHint': 'es. Conventional Commits, oggetto in italiano, max 72 caratteri',
|
|
2217
2253
|
'settings.commit.tooLong': 'Solo i primi {n} caratteri vengono salvati',
|
|
@@ -2395,8 +2431,11 @@ window.__ModuleLoader__.load({
|
|
|
2395
2431
|
'settings.commit.title': 'AI-commitbericht',
|
|
2396
2432
|
'settings.commit.model': 'Model',
|
|
2397
2433
|
'settings.commit.modelHint': 'Leeg = deze sessie volgen (provider/model)',
|
|
2434
|
+
'settings.commit.modelFollow': 'Deze sessie volgen',
|
|
2398
2435
|
'settings.commit.reasoning': 'Redeneerinspanning',
|
|
2399
|
-
'settings.commit.reasoningHint': 'Leeg =
|
|
2436
|
+
'settings.commit.reasoningHint': 'Leeg = geen redeneren',
|
|
2437
|
+
'settings.commit.reasoningDefault': 'Geen redeneren (standaard)',
|
|
2438
|
+
'settings.commit.reasoningFollowHint': 'Bij het volgen van de sessie wordt nooit geredeneerd; kies een vast model om een niveau te kiezen',
|
|
2400
2439
|
'settings.commit.prompt': 'Extra instructies',
|
|
2401
2440
|
'settings.commit.promptHint': 'bv. Conventional Commits, onderwerp in het Nederlands, max. 72 tekens',
|
|
2402
2441
|
'settings.commit.tooLong': 'Alleen de eerste {n} tekens worden opgeslagen',
|
|
@@ -2580,8 +2619,11 @@ window.__ModuleLoader__.load({
|
|
|
2580
2619
|
'settings.commit.title': 'Wiadomość commita od AI',
|
|
2581
2620
|
'settings.commit.model': 'Model',
|
|
2582
2621
|
'settings.commit.modelHint': 'Puste = zgodnie z tą sesją (provider/model)',
|
|
2622
|
+
'settings.commit.modelFollow': 'Zgodnie z tą sesją',
|
|
2583
2623
|
'settings.commit.reasoning': 'Poziom rozumowania',
|
|
2584
|
-
'settings.commit.reasoningHint': 'Puste =
|
|
2624
|
+
'settings.commit.reasoningHint': 'Puste = bez rozumowania',
|
|
2625
|
+
'settings.commit.reasoningDefault': 'Bez rozumowania (domyślnie)',
|
|
2626
|
+
'settings.commit.reasoningFollowHint': 'Podążanie za sesją zawsze pomija rozumowanie; wybierz stały model, aby wybrać poziom',
|
|
2585
2627
|
'settings.commit.prompt': 'Dodatkowe instrukcje',
|
|
2586
2628
|
'settings.commit.promptHint': 'np. Conventional Commits, temat po polsku, maks. 72 znaki',
|
|
2587
2629
|
'settings.commit.tooLong': 'Zapisywane jest tylko pierwszych {n} znaków',
|
|
@@ -2765,8 +2807,11 @@ window.__ModuleLoader__.load({
|
|
|
2765
2807
|
'settings.commit.title': 'AI-commitmeddelande',
|
|
2766
2808
|
'settings.commit.model': 'Modell',
|
|
2767
2809
|
'settings.commit.modelHint': 'Tomt = följ denna session (provider/model)',
|
|
2810
|
+
'settings.commit.modelFollow': 'Följ denna session',
|
|
2768
2811
|
'settings.commit.reasoning': 'Resonemangsinsats',
|
|
2769
|
-
'settings.commit.reasoningHint': 'Tomt =
|
|
2812
|
+
'settings.commit.reasoningHint': 'Tomt = inget resonemang',
|
|
2813
|
+
'settings.commit.reasoningDefault': 'Inget resonemang (standard)',
|
|
2814
|
+
'settings.commit.reasoningFollowHint': 'När sessionen följs används aldrig resonemang; välj en fast modell för att välja en nivå',
|
|
2770
2815
|
'settings.commit.prompt': 'Extra instruktioner',
|
|
2771
2816
|
'settings.commit.promptHint': 't.ex. Conventional Commits, ämne på svenska, högst 72 tecken',
|
|
2772
2817
|
'settings.commit.tooLong': 'Endast de första {n} tecknen sparas',
|
|
@@ -2950,8 +2995,11 @@ window.__ModuleLoader__.load({
|
|
|
2950
2995
|
'settings.commit.title': 'AI commit mesajı',
|
|
2951
2996
|
'settings.commit.model': 'Model',
|
|
2952
2997
|
'settings.commit.modelHint': 'Boş = bu oturumu izle (provider/model)',
|
|
2998
|
+
'settings.commit.modelFollow': 'Bu oturumu izle',
|
|
2953
2999
|
'settings.commit.reasoning': 'Düşünme yoğunluğu',
|
|
2954
|
-
'settings.commit.reasoningHint': 'Boş =
|
|
3000
|
+
'settings.commit.reasoningHint': 'Boş = akıl yürütme yok',
|
|
3001
|
+
'settings.commit.reasoningDefault': 'Akıl yürütme yok (varsayılan)',
|
|
3002
|
+
'settings.commit.reasoningFollowHint': 'Oturumu izlerken akıl yürütme her zaman kapalıdır; bir düzey seçmek için sabit bir model seçin',
|
|
2955
3003
|
'settings.commit.prompt': 'Ek yönergeler',
|
|
2956
3004
|
'settings.commit.promptHint': 'örn. Conventional Commits, konu Türkçe, en fazla 72 karakter',
|
|
2957
3005
|
'settings.commit.tooLong': 'Yalnızca ilk {n} karakter kaydedilir',
|
|
@@ -3135,8 +3183,11 @@ window.__ModuleLoader__.load({
|
|
|
3135
3183
|
'settings.commit.title': 'Pesan commit AI',
|
|
3136
3184
|
'settings.commit.model': 'Model',
|
|
3137
3185
|
'settings.commit.modelHint': 'Kosong = ikuti sesi ini (provider/model)',
|
|
3186
|
+
'settings.commit.modelFollow': 'Ikuti sesi ini',
|
|
3138
3187
|
'settings.commit.reasoning': 'Intensitas penalaran',
|
|
3139
|
-
'settings.commit.reasoningHint': 'Kosong =
|
|
3188
|
+
'settings.commit.reasoningHint': 'Kosong = tanpa penalaran',
|
|
3189
|
+
'settings.commit.reasoningDefault': 'Tanpa penalaran (bawaan)',
|
|
3190
|
+
'settings.commit.reasoningFollowHint': 'Saat mengikuti sesi, penalaran selalu nonaktif; pilih model tetap untuk memilih tingkat',
|
|
3140
3191
|
'settings.commit.prompt': 'Instruksi tambahan',
|
|
3141
3192
|
'settings.commit.promptHint': 'mis. Conventional Commits, subjek bahasa Indonesia, maks 72 karakter',
|
|
3142
3193
|
'settings.commit.tooLong': 'Hanya {n} karakter pertama yang disimpan',
|
|
@@ -3320,8 +3371,11 @@ window.__ModuleLoader__.load({
|
|
|
3320
3371
|
'settings.commit.title': 'Thông điệp commit do AI viết',
|
|
3321
3372
|
'settings.commit.model': 'Mô hình',
|
|
3322
3373
|
'settings.commit.modelHint': 'Trống = theo phiên này (provider/model)',
|
|
3374
|
+
'settings.commit.modelFollow': 'Theo phiên này',
|
|
3323
3375
|
'settings.commit.reasoning': 'Mức suy luận',
|
|
3324
|
-
'settings.commit.reasoningHint': '
|
|
3376
|
+
'settings.commit.reasoningHint': 'Để trống = không suy luận',
|
|
3377
|
+
'settings.commit.reasoningDefault': 'Không suy luận (mặc định)',
|
|
3378
|
+
'settings.commit.reasoningFollowHint': 'Khi theo phiên, suy luận luôn tắt; chọn một mô hình cố định để chọn mức',
|
|
3325
3379
|
'settings.commit.prompt': 'Chỉ dẫn bổ sung',
|
|
3326
3380
|
'settings.commit.promptHint': 'ví dụ Conventional Commits, tiêu đề tiếng Việt, tối đa 72 ký tự',
|
|
3327
3381
|
'settings.commit.tooLong': 'Chỉ {n} ký tự đầu được lưu',
|
|
@@ -3505,8 +3559,11 @@ window.__ModuleLoader__.load({
|
|
|
3505
3559
|
'settings.commit.title': 'رسالة إيداع بالذكاء الاصطناعي',
|
|
3506
3560
|
'settings.commit.model': 'النموذج',
|
|
3507
3561
|
'settings.commit.modelHint': 'فارغ = اتبع هذه الجلسة (provider/model)',
|
|
3562
|
+
'settings.commit.modelFollow': 'اتبع هذه الجلسة',
|
|
3508
3563
|
'settings.commit.reasoning': 'مستوى التفكير',
|
|
3509
|
-
'settings.commit.reasoningHint': 'فارغ =
|
|
3564
|
+
'settings.commit.reasoningHint': 'فارغ = بدون استدلال',
|
|
3565
|
+
'settings.commit.reasoningDefault': 'بدون استدلال (افتراضي)',
|
|
3566
|
+
'settings.commit.reasoningFollowHint': 'عند اتباع الجلسة يكون الاستدلال معطلًا دائمًا؛ اختر نموذجًا ثابتًا لاختيار المستوى',
|
|
3510
3567
|
'settings.commit.prompt': 'تعليمات إضافية',
|
|
3511
3568
|
'settings.commit.promptHint': 'مثال: Conventional Commits، والعنوان بالعربية، وبحد أقصى 72 حرفًا',
|
|
3512
3569
|
'settings.commit.tooLong': 'يُحفظ أول {n} حرفًا فقط',
|
|
@@ -3690,8 +3747,11 @@ window.__ModuleLoader__.load({
|
|
|
3690
3747
|
'settings.commit.title': 'AI कमिट संदेश',
|
|
3691
3748
|
'settings.commit.model': 'मॉडल',
|
|
3692
3749
|
'settings.commit.modelHint': 'खाली = इस सत्र का अनुसरण करें (provider/model)',
|
|
3750
|
+
'settings.commit.modelFollow': 'इस सत्र का पालन करें',
|
|
3693
3751
|
'settings.commit.reasoning': 'तर्क की तीव्रता',
|
|
3694
|
-
'settings.commit.reasoningHint': 'खाली =
|
|
3752
|
+
'settings.commit.reasoningHint': 'खाली = कोई तर्क नहीं',
|
|
3753
|
+
'settings.commit.reasoningDefault': 'कोई तर्क नहीं (डिफ़ॉल्ट)',
|
|
3754
|
+
'settings.commit.reasoningFollowHint': 'सत्र का पालन करते समय तर्क हमेशा बंद रहता है; स्तर चुनने के लिए एक निश्चित मॉडल चुनें',
|
|
3695
3755
|
'settings.commit.prompt': 'अतिरिक्त निर्देश',
|
|
3696
3756
|
'settings.commit.promptHint': 'जैसे Conventional Commits, विषय हिंदी में, अधिकतम 72 अक्षर',
|
|
3697
3757
|
'settings.commit.tooLong': 'केवल पहले {n} अक्षर सहेजे जाते हैं',
|
|
@@ -3875,8 +3935,11 @@ window.__ModuleLoader__.load({
|
|
|
3875
3935
|
'settings.commit.title': 'ข้อความคอมมิตโดย AI',
|
|
3876
3936
|
'settings.commit.model': 'โมเดล',
|
|
3877
3937
|
'settings.commit.modelHint': 'ว่าง = ตามเซสชันนี้ (provider/model)',
|
|
3938
|
+
'settings.commit.modelFollow': 'ตามเซสชันนี้',
|
|
3878
3939
|
'settings.commit.reasoning': 'ระดับการใช้เหตุผล',
|
|
3879
|
-
'settings.commit.reasoningHint': '
|
|
3940
|
+
'settings.commit.reasoningHint': 'เว้นว่าง = ไม่ใช้เหตุผล',
|
|
3941
|
+
'settings.commit.reasoningDefault': 'ไม่ใช้เหตุผล (ค่าเริ่มต้น)',
|
|
3942
|
+
'settings.commit.reasoningFollowHint': 'เมื่อตามเซสชันจะไม่ใช้เหตุผลเสมอ เลือกโมเดลคงที่เพื่อเลือกระดับ',
|
|
3880
3943
|
'settings.commit.prompt': 'คำสั่งเพิ่มเติม',
|
|
3881
3944
|
'settings.commit.promptHint': 'เช่น Conventional Commits หัวข้อเป็นภาษาไทย ไม่เกิน 72 ตัวอักษร',
|
|
3882
3945
|
'settings.commit.tooLong': 'บันทึกเพียง {n} ตัวอักษรแรก',
|
|
@@ -4045,6 +4108,48 @@ window.__ModuleLoader__.load({
|
|
|
4045
4108
|
|
|
4046
4109
|
/* ============================== storage ============================== */
|
|
4047
4110
|
|
|
4111
|
+
/* ---- ai commit pickers (issue #7): the model catalog and per-model
|
|
4112
|
+
reasoning efforts, served by the host's commit-models / commit-efforts
|
|
4113
|
+
routes. Cached for a short window so the settings card can mount twice
|
|
4114
|
+
without asking twice; a failed or empty lookup degrades the row to its
|
|
4115
|
+
plain text field (the hand-typed value keeps working: routing accepts
|
|
4116
|
+
unlisted ids), never to an error. ---- */
|
|
4117
|
+
const PICKER_TTL_MS = 30_000
|
|
4118
|
+
let commitCatalogCache = null
|
|
4119
|
+
let commitCatalogFlight = null
|
|
4120
|
+
const commitEffortsCache = new Map()
|
|
4121
|
+
|
|
4122
|
+
function pickerProvidersOf(catalog) {
|
|
4123
|
+
if (catalog === null || typeof catalog !== 'object' || !Array.isArray(catalog.providers)) return null
|
|
4124
|
+
return catalog.providers
|
|
4125
|
+
}
|
|
4126
|
+
|
|
4127
|
+
async function fetchCommitCatalog() {
|
|
4128
|
+
if (commitCatalogCache !== null && Date.now() - commitCatalogCache.at < PICKER_TTL_MS) {
|
|
4129
|
+
return pickerProvidersOf(commitCatalogCache.value)
|
|
4130
|
+
}
|
|
4131
|
+
if (commitCatalogFlight !== null) return commitCatalogFlight
|
|
4132
|
+
commitCatalogFlight = request('commit-models', {})
|
|
4133
|
+
.then(
|
|
4134
|
+
(value) => {
|
|
4135
|
+
commitCatalogCache = { at: Date.now(), value: value }
|
|
4136
|
+
return pickerProvidersOf(value)
|
|
4137
|
+
},
|
|
4138
|
+
() => null,
|
|
4139
|
+
)
|
|
4140
|
+
commitCatalogFlight.then(() => { commitCatalogFlight = null }, () => { commitCatalogFlight = null })
|
|
4141
|
+
return commitCatalogFlight
|
|
4142
|
+
return commitCatalogFlight
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
async function fetchCommitEfforts(model) {
|
|
4146
|
+
const hit = commitEffortsCache.get(model)
|
|
4147
|
+
if (hit !== undefined && Date.now() - hit.at < PICKER_TTL_MS) return hit.value
|
|
4148
|
+
const value = await request('commit-efforts', { model: model })
|
|
4149
|
+
commitEffortsCache.set(model, { at: Date.now(), value: value })
|
|
4150
|
+
return value
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4048
4153
|
function readRememberedRepo(sessionId) {
|
|
4049
4154
|
try {
|
|
4050
4155
|
const raw = window.localStorage.getItem(REPO_KEY)
|
|
@@ -4986,25 +5091,31 @@ window.__ModuleLoader__.load({
|
|
|
4986
5091
|
list is drag-sortable and arrow-sortable (the arrows also work with a
|
|
4987
5092
|
keyboard); visibility is a per-action toggle, not a delete, so a hidden
|
|
4988
5093
|
action keeps its position. */
|
|
4989
|
-
/* The plugin's settings card. It
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
5094
|
+
/* The plugin's settings card. It lives on the plugin's detail page (slot
|
|
5095
|
+
`plugins.bundle.config`, keyed by the PACKAGE name), and it is the only
|
|
5096
|
+
editor when the host serves the row Config — the in-panel editor is not
|
|
5097
|
+
reachable there. The legacy Settings → Plugins seat went away with the
|
|
5098
|
+
raised host floor.
|
|
4994
5099
|
|
|
4995
5100
|
A host without that service still renders the card (the seat exists), so it
|
|
4996
5101
|
says so instead of pretending to have settings. */
|
|
4997
5102
|
function RailSettingsCard(props) {
|
|
4998
5103
|
const t = props.t
|
|
4999
5104
|
const [tick, setTick] = useState(0)
|
|
5000
|
-
/* Collapse state of the card shell (the li card on the
|
|
5001
|
-
|
|
5105
|
+
/* Collapse state of the card shell (the li card on the list seat). The
|
|
5106
|
+
plugin-page seat (props.view === 'page') renders flat. */
|
|
5002
5107
|
const [openCard, setOpenCard] = useState(false)
|
|
5003
5108
|
const [pending, setPending] = useState({})
|
|
5004
5109
|
const [note, setNote] = useState('')
|
|
5005
5110
|
/* The three AI-commit settings are free text, so they are drafted locally
|
|
5006
5111
|
and written on blur / Enter — one write per edit, never one per keystroke. */
|
|
5007
5112
|
const [textDraft, setTextDraft] = useState({})
|
|
5113
|
+
/* Picker data (issue #7): the model catalog once per mount, and the
|
|
5114
|
+
reasoning efforts of whichever model the settings currently name.
|
|
5115
|
+
null = not answered yet; an empty array = answered with nothing. Both
|
|
5116
|
+
degrade the row to its plain text field, never to an error — a
|
|
5117
|
+
hand-typed value keeps working because routing accepts unlisted ids. */
|
|
5118
|
+
const [pickers, setPickers] = useState({ catalog: null, efforts: null })
|
|
5008
5119
|
/* The user's latest intent per field, written SYNCHRONOUSLY. React state is
|
|
5009
5120
|
not enough: two clicks inside one frame would both read the pre-click
|
|
5010
5121
|
snapshot, so the second click recomputed the same target and the toggle
|
|
@@ -5073,6 +5184,32 @@ window.__ModuleLoader__.load({
|
|
|
5073
5184
|
setTextDraft((now) => { const copy = Object.assign({}, now); delete copy[field]; return copy })
|
|
5074
5185
|
})
|
|
5075
5186
|
}
|
|
5187
|
+
/* A picker row writes on CHANGE (a select has no blur-edit cycle): the
|
|
5188
|
+
optimistic value lands in the same textDraft the text rows use, so the
|
|
5189
|
+
control does not snap back to the accepted document for the round trip
|
|
5190
|
+
the host needs to echo the write. */
|
|
5191
|
+
const commitSelectField = (field, value) => {
|
|
5192
|
+
if (readOnly === true) { setNote(t('settings.rail.readonly')); return }
|
|
5193
|
+
setTextDraft((now) => Object.assign({}, now, { [field]: value }))
|
|
5194
|
+
const outcome = writeRailFields([{ op: 'set', path: [field], value: value }], scope)
|
|
5195
|
+
if (outcome.sent !== true) {
|
|
5196
|
+
setTextDraft((now) => { const copy = Object.assign({}, now); delete copy[field]; return copy })
|
|
5197
|
+
setNote(t(outcome.reason === 'read-only' ? 'settings.rail.readonly' : 'settings.rail.writeFailed'))
|
|
5198
|
+
return
|
|
5199
|
+
}
|
|
5200
|
+
if (outcome.settled === undefined) return
|
|
5201
|
+
void outcome.settled.then((ok) => {
|
|
5202
|
+
if (ok !== true) {
|
|
5203
|
+
setTextDraft((now) => { const copy = Object.assign({}, now); delete copy[field]; return copy })
|
|
5204
|
+
setNote(t('settings.rail.writeFailed'))
|
|
5205
|
+
return
|
|
5206
|
+
}
|
|
5207
|
+
/* The accepted document now carries it: drop the draft so the control
|
|
5208
|
+
shows the Host's own value again — the same contract a text row's
|
|
5209
|
+
blur commit keeps. */
|
|
5210
|
+
setTextDraft((now) => { const copy = Object.assign({}, now); delete copy[field]; return copy })
|
|
5211
|
+
})
|
|
5212
|
+
}
|
|
5076
5213
|
const textRow = (field, labelKey, hintKey, max, multiline) => {
|
|
5077
5214
|
const accepted = values[field] === undefined || values[field] === null ? '' : String(values[field])
|
|
5078
5215
|
const shown = Object.prototype.hasOwnProperty.call(textDraft, field) ? textDraft[field] : accepted
|
|
@@ -5090,6 +5227,21 @@ window.__ModuleLoader__.load({
|
|
|
5090
5227
|
multiline === true ? E('textarea', Object.assign({ rows: 3 }, common)) : E('input', Object.assign({ type: 'text' }, common)),
|
|
5091
5228
|
shown.length >= max ? E('div', { className: 'dig-settings-error' }, fill(t('settings.commit.tooLong'), { n: String(max) })) : null)
|
|
5092
5229
|
}
|
|
5230
|
+
/* A picker row: same shell as a text row, but a <select>. `children` are
|
|
5231
|
+
the options (built by the caller); a stored value the list does not
|
|
5232
|
+
carry is injected as its own option, because a controlled select whose
|
|
5233
|
+
value matches no option renders blank and the setting would LOOK lost. */
|
|
5234
|
+
const selectRow = (field, labelKey, value, children, disabled, hint) => {
|
|
5235
|
+
const id = 'dig-' + field
|
|
5236
|
+
return E('div', { className: 'dig-settings-text', key: field },
|
|
5237
|
+
E('label', { id: id + '-label', className: 'dig-settings-text-label', htmlFor: id }, t(labelKey)),
|
|
5238
|
+
E('select', {
|
|
5239
|
+
id: id, className: 'dig-input dig-settings-input', value: value, disabled: disabled === true,
|
|
5240
|
+
'aria-labelledby': id + '-label',
|
|
5241
|
+
onChange: (event) => commitSelectField(field, event.target.value),
|
|
5242
|
+
}, children),
|
|
5243
|
+
hint === null ? null : E('div', { className: 'dig-settings-subnote' }, hint))
|
|
5244
|
+
}
|
|
5093
5245
|
const flush = () => {
|
|
5094
5246
|
pendingFlush.current = null
|
|
5095
5247
|
/* The pending TARGET per field, not "whatever the render-time intent
|
|
@@ -5192,6 +5344,39 @@ window.__ModuleLoader__.load({
|
|
|
5192
5344
|
return changed ? next : current
|
|
5193
5345
|
})
|
|
5194
5346
|
})
|
|
5347
|
+
/* The picker catalog loads once per mount (cached module-wide); a rejected
|
|
5348
|
+
lookup answers null and the rows simply stay text fields. */
|
|
5349
|
+
useEffect(() => {
|
|
5350
|
+
let alive = true
|
|
5351
|
+
fetchCommitCatalog().then(
|
|
5352
|
+
(providers) => { if (alive === true) setPickers((now) => Object.assign({}, now, { catalog: providers })) },
|
|
5353
|
+
() => { if (alive === true) setPickers((now) => Object.assign({}, now, { catalog: null })) },
|
|
5354
|
+
)
|
|
5355
|
+
return () => { alive = false }
|
|
5356
|
+
}, [])
|
|
5357
|
+
/* The efforts list follows whichever model the settings name — the draft
|
|
5358
|
+
counts too, so the picker reacts to a fresh selection before the host
|
|
5359
|
+
has echoed the write. Following the session has no fixed model, so
|
|
5360
|
+
there is nothing to list (and nothing to ask). */
|
|
5361
|
+
const pickerModel = Object.prototype.hasOwnProperty.call(textDraft, 'commitModel') && typeof textDraft.commitModel === 'string'
|
|
5362
|
+
? textDraft.commitModel
|
|
5363
|
+
: (values === null ? '' : (values.commitModel === undefined || values.commitModel === null ? '' : String(values.commitModel)))
|
|
5364
|
+
useEffect(() => {
|
|
5365
|
+
let alive = true
|
|
5366
|
+
if (pickerModel === '') {
|
|
5367
|
+
setPickers((now) => (now.efforts === null ? now : Object.assign({}, now, { efforts: null })))
|
|
5368
|
+
return () => { alive = false }
|
|
5369
|
+
}
|
|
5370
|
+
fetchCommitEfforts(pickerModel).then(
|
|
5371
|
+
(value) => {
|
|
5372
|
+
if (alive !== true) return
|
|
5373
|
+
const rows = value !== null && typeof value === 'object' && Array.isArray(value.efforts) ? value.efforts : []
|
|
5374
|
+
setPickers((now) => Object.assign({}, now, { efforts: rows }))
|
|
5375
|
+
},
|
|
5376
|
+
() => { if (alive === true) setPickers((now) => Object.assign({}, now, { efforts: null })) },
|
|
5377
|
+
)
|
|
5378
|
+
return () => { alive = false }
|
|
5379
|
+
}, [pickerModel])
|
|
5195
5380
|
// Switches exist only where they can actually write: a host that does not
|
|
5196
5381
|
// serve this entry's settings gets a sentence instead of dead controls.
|
|
5197
5382
|
if (status !== 'ready' || values === null) {
|
|
@@ -5263,8 +5448,51 @@ window.__ModuleLoader__.load({
|
|
|
5263
5448
|
E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t(spec.key)))
|
|
5264
5449
|
})),
|
|
5265
5450
|
E('div', { className: 'dig-settings-sub' }, t('settings.commit.title')),
|
|
5266
|
-
|
|
5267
|
-
|
|
5451
|
+
(() => {
|
|
5452
|
+
/* The model row is a picker over the host's provider catalog; while
|
|
5453
|
+
the catalog has not answered (or cannot), the row stays the plain
|
|
5454
|
+
text field it always was. */
|
|
5455
|
+
const accepted = values.commitModel === undefined || values.commitModel === null ? '' : String(values.commitModel)
|
|
5456
|
+
const shown = Object.prototype.hasOwnProperty.call(textDraft, 'commitModel') && typeof textDraft.commitModel === 'string' ? textDraft.commitModel : accepted
|
|
5457
|
+
if (Array.isArray(pickers.catalog)) {
|
|
5458
|
+
const listed = pickers.catalog.some((provider) => Array.isArray(provider.models)
|
|
5459
|
+
&& provider.models.some((model) => provider.id + '/' + model.id === shown))
|
|
5460
|
+
return selectRow('commitModel', 'settings.commit.model', shown, [
|
|
5461
|
+
E('option', { key: 'follow', value: '' }, t('settings.commit.modelFollow')),
|
|
5462
|
+
shown === '' || listed === true ? null : E('option', { key: 'kept', value: shown }, shown),
|
|
5463
|
+
].concat(pickers.catalog.map((provider) => E('optgroup', {
|
|
5464
|
+
key: provider.id,
|
|
5465
|
+
label: provider.name === provider.id ? provider.id : provider.name + ' (' + provider.id + ')',
|
|
5466
|
+
}, (Array.isArray(provider.models) ? provider.models : []).map((model) => E('option', {
|
|
5467
|
+
key: model.id, value: provider.id + '/' + model.id,
|
|
5468
|
+
}, model.name === model.id ? model.id : model.name + ' — ' + model.id))))), false, null)
|
|
5469
|
+
}
|
|
5470
|
+
return textRow('commitModel', 'settings.commit.model', 'settings.commit.modelHint', 120, false)
|
|
5471
|
+
})(),
|
|
5472
|
+
(() => {
|
|
5473
|
+
/* The reasoning row follows the model row: following the session has
|
|
5474
|
+
no fixed model, so it is the fixed no-reasoning default (the host
|
|
5475
|
+
asks for "off" for us); a fixed model lists exactly the efforts
|
|
5476
|
+
that route accepts — hand-typing an effort id was issue #7's
|
|
5477
|
+
error-prone path. No list (yet/unavailable) keeps the text field. */
|
|
5478
|
+
if (pickerModel === '') {
|
|
5479
|
+
return selectRow('commitReasoning', 'settings.commit.reasoning', '', [
|
|
5480
|
+
E('option', { key: 'default', value: '' }, t('settings.commit.reasoningDefault')),
|
|
5481
|
+
], true, t('settings.commit.reasoningFollowHint'))
|
|
5482
|
+
}
|
|
5483
|
+
if (pickers.efforts !== null && pickers.efforts.length > 0) {
|
|
5484
|
+
const accepted = values.commitReasoning === undefined || values.commitReasoning === null ? '' : String(values.commitReasoning)
|
|
5485
|
+
const shown = Object.prototype.hasOwnProperty.call(textDraft, 'commitReasoning') && typeof textDraft.commitReasoning === 'string' ? textDraft.commitReasoning : accepted
|
|
5486
|
+
const listed = pickers.efforts.some((row) => row.id === shown)
|
|
5487
|
+
return selectRow('commitReasoning', 'settings.commit.reasoning', shown, [
|
|
5488
|
+
E('option', { key: 'default', value: '' }, t('settings.commit.reasoningDefault')),
|
|
5489
|
+
shown === '' || listed === true ? null : E('option', { key: 'kept', value: shown }, shown),
|
|
5490
|
+
].concat(pickers.efforts.map((row) => E('option', {
|
|
5491
|
+
key: row.id, value: row.id,
|
|
5492
|
+
}, row.name === row.id ? row.id : row.name + ' (' + row.id + ')'))), false, null)
|
|
5493
|
+
}
|
|
5494
|
+
return textRow('commitReasoning', 'settings.commit.reasoning', 'settings.commit.reasoningHint', 60, false)
|
|
5495
|
+
})(),
|
|
5268
5496
|
textRow('commitPrompt', 'settings.commit.prompt', 'settings.commit.promptHint', 2000, true)))
|
|
5269
5497
|
}
|
|
5270
5498
|
|
|
@@ -7307,6 +7535,7 @@ window.__ModuleLoader__.load({
|
|
|
7307
7535
|
'.dig-settings-note{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary));font-weight:400}',
|
|
7308
7536
|
'.dig-settings-note-hidden{display:none}',
|
|
7309
7537
|
'.dig-settings-error{color:var(--dsw-alias-state-error-primary);font-weight:400}',
|
|
7538
|
+
'.dig-settings-subnote{color:var(--dsw-alias-label-tertiary);font-weight:400;font-size:12px}',
|
|
7310
7539
|
'.dig-settings-sub{margin-top:6px;font-weight:600;color:var(--dsw-alias-label-primary)}',
|
|
7311
7540
|
'.dig-settings-text{display:flex;flex-direction:column;gap:3px;min-width:0}',
|
|
7312
7541
|
'.dig-settings-text-label{color:var(--dsw-alias-label-secondary);font-weight:400}',
|
|
@@ -7727,10 +7956,10 @@ window.__ModuleLoader__.load({
|
|
|
7727
7956
|
}, 'dsh-ide-git: row config')
|
|
7728
7957
|
})
|
|
7729
7958
|
|
|
7730
|
-
/* The settings card, on
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
the slots keeps whatever surface it has. */
|
|
7959
|
+
/* The settings card, on the plugin detail page (plugins.bundle.config,
|
|
7960
|
+
keyed by the PACKAGE name; dsh >= 0.1.6-alpha.2). The legacy settings-list
|
|
7961
|
+
seat served pre-alpha.2 hosts only and is gone with the raised floor. The
|
|
7962
|
+
seat is optional — a host without the slots keeps whatever surface it has. */
|
|
7734
7963
|
ctx.inject(['slots'], (slotCtx) => {
|
|
7735
7964
|
const slots = slotCtx.get('slots')
|
|
7736
7965
|
if (slots === undefined || slots === null || typeof slots.inject !== 'function') return
|
|
@@ -7740,13 +7969,8 @@ window.__ModuleLoader__.load({
|
|
|
7740
7969
|
{ name: 'plugins.bundle.config', key: SETTINGS_BUNDLE, locale: LOCALE_NS },
|
|
7741
7970
|
card,
|
|
7742
7971
|
))
|
|
7743
|
-
const offItem = slots.inject('settings.plugin.item', () => slots.register(
|
|
7744
|
-
{ name: 'settings.plugin.item', key: RAIL_NS, locale: LOCALE_NS },
|
|
7745
|
-
card,
|
|
7746
|
-
))
|
|
7747
7972
|
return () => {
|
|
7748
7973
|
try { offBundle() } catch (error) { void error }
|
|
7749
|
-
try { offItem() } catch (error) { void error }
|
|
7750
7974
|
}
|
|
7751
7975
|
}, 'dsh-ide-git: settings card')
|
|
7752
7976
|
})
|
package/src/index.js
CHANGED
|
@@ -1315,12 +1315,14 @@ const COMMIT_BINARY = /\.(png|jpe?g|gif|webp|ico|pdf|zip|gz|tgz|bz2|xz|7z|jar|wa
|
|
|
1315
1315
|
async function collectCommitStream(stream) {
|
|
1316
1316
|
let text = ''
|
|
1317
1317
|
let finish = null
|
|
1318
|
+
let sawReasoning = false
|
|
1318
1319
|
for await (const chunk of stream) {
|
|
1319
1320
|
if (chunk === null || chunk === undefined) continue
|
|
1320
1321
|
if (chunk.type === 'text-delta' && typeof chunk.text === 'string') text += chunk.text
|
|
1322
|
+
else if (chunk.type === 'reasoning-delta') sawReasoning = true
|
|
1321
1323
|
else if (chunk.type === 'finish') finish = chunk.reason
|
|
1322
1324
|
}
|
|
1323
|
-
return { text: text, finish: finish }
|
|
1325
|
+
return { text: text, finish: finish, sawReasoning: sawReasoning }
|
|
1324
1326
|
}
|
|
1325
1327
|
|
|
1326
1328
|
/** The change set, trimmed to a budget the model can actually be asked about. */
|
|
@@ -1433,6 +1435,89 @@ async function commitRouteOf(payload) {
|
|
|
1433
1435
|
return { provider: provider, model: model, source: 'setting' }
|
|
1434
1436
|
}
|
|
1435
1437
|
|
|
1438
|
+
/** The unset reasoning default: 'off' when this route offers it, '' otherwise.
|
|
1439
|
+
Failures stay silent on purpose — this is a preference, not a contract: an
|
|
1440
|
+
unreachable capability lookup must not turn a working model call into an
|
|
1441
|
+
error; it just leaves the effort to the adapter's own default. */
|
|
1442
|
+
async function quietOffEffortOf(route) {
|
|
1443
|
+
try {
|
|
1444
|
+
const llm = commitServices.llm
|
|
1445
|
+
if (typeof llm.resolveModelInfo !== 'function') return ''
|
|
1446
|
+
const info = await llm.resolveModelInfo(route.provider, route.model)
|
|
1447
|
+
const reasoning = info !== null && typeof info === 'object' && info.reasoning !== null && typeof info.reasoning === 'object' ? info.reasoning : null
|
|
1448
|
+
const efforts = reasoning !== null && Array.isArray(reasoning.efforts) ? reasoning.efforts : []
|
|
1449
|
+
return efforts.some((row) => row !== null && typeof row === 'object' && row.id === 'off') ? 'off' : ''
|
|
1450
|
+
} catch (error) {
|
|
1451
|
+
void error
|
|
1452
|
+
return ''
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/** The settings card's model picker: every provider route this host serves and
|
|
1457
|
+
the models each one lists. The catalog is advisory (routing accepts
|
|
1458
|
+
unlisted ids, see commitRouteOf), so a stored hand-typed value stays valid
|
|
1459
|
+
even when it is not in this list. */
|
|
1460
|
+
async function commitModels() {
|
|
1461
|
+
if (commitServices === null) {
|
|
1462
|
+
throw new PanelError('no-host-service', 'this host does not serve llm + sessions, so there is no model catalog here', 501)
|
|
1463
|
+
}
|
|
1464
|
+
const providers = []
|
|
1465
|
+
for (const row of commitServices.llm.listProviders()) {
|
|
1466
|
+
/* One provider's discovery failing must not hide the others: its entry
|
|
1467
|
+
ships with an empty model list and the picker still works. */
|
|
1468
|
+
let models = []
|
|
1469
|
+
try { models = await commitServices.llm.listModels(row.id) } catch (error) { models = []; void error }
|
|
1470
|
+
providers.push({
|
|
1471
|
+
id: row.id,
|
|
1472
|
+
name: typeof row.name === 'string' && row.name !== '' ? row.name : row.id,
|
|
1473
|
+
models: models
|
|
1474
|
+
.filter((model) => model !== null && typeof model === 'object' && typeof model.id === 'string' && model.id !== '')
|
|
1475
|
+
.map((model) => ({ id: model.id, name: typeof model.name === 'string' && model.name !== '' ? model.name : model.id })),
|
|
1476
|
+
})
|
|
1477
|
+
}
|
|
1478
|
+
return { providers: providers }
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/** The settings card's reasoning picker: the efforts one exact provider/model
|
|
1482
|
+
route accepts. An unreachable capability lookup answers with an EMPTY list
|
|
1483
|
+
(the browser falls back to its text field) instead of an error — the picker
|
|
1484
|
+
is a convenience, not a gate. */
|
|
1485
|
+
async function commitEfforts(payload) {
|
|
1486
|
+
if (commitServices === null) {
|
|
1487
|
+
throw new PanelError('no-host-service', 'this host does not serve llm + sessions, so there are no reasoning efforts here', 501)
|
|
1488
|
+
}
|
|
1489
|
+
const wanted = typeof payload.model === 'string' ? payload.model.trim() : ''
|
|
1490
|
+
const slash = wanted.indexOf('/')
|
|
1491
|
+
if (slash <= 0 || slash === wanted.length - 1 || wanted.indexOf('/', slash + 1) >= 0) {
|
|
1492
|
+
throw badRequest('model must look like "provider/model"')
|
|
1493
|
+
}
|
|
1494
|
+
const provider = wanted.slice(0, slash)
|
|
1495
|
+
const model = wanted.slice(slash + 1)
|
|
1496
|
+
const known = commitServices.llm.listProviders().map((row) => row.id)
|
|
1497
|
+
if (known.indexOf(provider) < 0) {
|
|
1498
|
+
throw new PanelError('unknown-provider', 'unknown provider "' + provider + '"; this host serves: ' + known.join(', '))
|
|
1499
|
+
}
|
|
1500
|
+
if (typeof commitServices.llm.resolveModelInfo !== 'function') {
|
|
1501
|
+
return { provider: provider, model: model, efforts: [], defaultEffort: null }
|
|
1502
|
+
}
|
|
1503
|
+
try {
|
|
1504
|
+
const info = await commitServices.llm.resolveModelInfo(provider, model)
|
|
1505
|
+
const reasoning = info !== null && typeof info === 'object' && info.reasoning !== null && typeof info.reasoning === 'object' ? info.reasoning : null
|
|
1506
|
+
const efforts = reasoning !== null && Array.isArray(reasoning.efforts) ? reasoning.efforts : []
|
|
1507
|
+
return {
|
|
1508
|
+
provider: provider,
|
|
1509
|
+
model: model,
|
|
1510
|
+
efforts: efforts
|
|
1511
|
+
.filter((row) => row !== null && typeof row === 'object' && typeof row.id === 'string' && row.id !== '')
|
|
1512
|
+
.map((row) => ({ id: row.id, name: typeof row.name === 'string' && row.name !== '' ? row.name : row.id })),
|
|
1513
|
+
defaultEffort: reasoning !== null && typeof reasoning.defaultEffort === 'string' ? reasoning.defaultEffort : null,
|
|
1514
|
+
}
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
void error
|
|
1517
|
+
return { provider: provider, model: model, efforts: [], defaultEffort: null }
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1436
1521
|
async function commitMessage(payload) {
|
|
1437
1522
|
if (commitServices === null) {
|
|
1438
1523
|
throw new PanelError('no-host-service', 'this host does not serve llm + sessions, so the message cannot be written here', 501)
|
|
@@ -1464,10 +1549,23 @@ async function commitMessage(payload) {
|
|
|
1464
1549
|
model: route.model,
|
|
1465
1550
|
system: spoken.system,
|
|
1466
1551
|
messages: [{ role: 'user', content: [{ type: 'text', text: spoken.user }] }],
|
|
1467
|
-
maxTokens: 512,
|
|
1468
1552
|
temperature: 0.2,
|
|
1469
1553
|
}
|
|
1470
|
-
|
|
1554
|
+
/* No maxTokens on purpose: omitted, the runtime materializes the selected
|
|
1555
|
+
model's own configured output cap (resolveModelInfo().defaultMaxTokens) —
|
|
1556
|
+
exactly what a session call gets. The hardcoded 512 here was half of
|
|
1557
|
+
issue #7: with thinking enabled, max_tokens is the budget for reasoning
|
|
1558
|
+
AND message together, so 512 died inside the reasoning phase before one
|
|
1559
|
+
word of the message existed. */
|
|
1560
|
+
/* The other half of issue #7: an UNSET effort is not a neutral "model
|
|
1561
|
+
default" — the DeepSeek adapter resolves "unspecified" to reasoning ON at
|
|
1562
|
+
"high". A commit message is a formatting task, so the unset default now
|
|
1563
|
+
asks for "off" whenever the route offers it. The id is read from the
|
|
1564
|
+
route's own efforts table (resolveModelInfo), never assumed: the runtime
|
|
1565
|
+
rejects an effort the route does not list, so a blind 'off' would trade
|
|
1566
|
+
one loud failure for another. The user's explicit choice always wins. */
|
|
1567
|
+
const effectiveReasoning = reasoning !== '' ? reasoning : await quietOffEffortOf(route)
|
|
1568
|
+
if (effectiveReasoning !== '') options.reasoningEffort = effectiveReasoning
|
|
1471
1569
|
if (typeof payload.sessionId === 'string' && payload.sessionId !== '') options.sessionId = payload.sessionId
|
|
1472
1570
|
const started = Date.now()
|
|
1473
1571
|
const collected = await collectCommitStream(commitServices.llm.stream(options))
|
|
@@ -1480,6 +1578,12 @@ async function commitMessage(payload) {
|
|
|
1480
1578
|
if (finish.kind === 'error') throw gatewayError('llm-error', failureTextOf(finish.failure))
|
|
1481
1579
|
if (finish.kind === 'aborted') throw gatewayError('llm-aborted', 'the call was aborted: ' + failureTextOf(finish.failure))
|
|
1482
1580
|
if (finish.kind !== 'stop') {
|
|
1581
|
+
/* The max-tokens + no-text + reasoning-delta shape is the exact signature
|
|
1582
|
+
of issue #7: the budget (reasoning included) ran out mid-thought. Say
|
|
1583
|
+
what happened and what to do, instead of a bare finish kind. */
|
|
1584
|
+
if (finish.kind === 'max-tokens' && collected.text.trim() === '' && collected.sawReasoning === true) {
|
|
1585
|
+
throw gatewayError('llm-unfinished', 'the model stopped with "max-tokens" before writing the message: its reasoning spent the whole output budget; lower the reasoning effort in the plugin settings or pick a lighter model')
|
|
1586
|
+
}
|
|
1483
1587
|
throw gatewayError('llm-unfinished', 'the model stopped with "' + String(finish.kind) + '"; the message may be incomplete')
|
|
1484
1588
|
}
|
|
1485
1589
|
const message = cleanupCommitMessage(collected.text)
|
|
@@ -1489,7 +1593,7 @@ async function commitMessage(payload) {
|
|
|
1489
1593
|
provider: route.provider,
|
|
1490
1594
|
model: route.model,
|
|
1491
1595
|
routeSource: route.source,
|
|
1492
|
-
reasoningEffort:
|
|
1596
|
+
reasoningEffort: effectiveReasoning === '' ? null : effectiveReasoning,
|
|
1493
1597
|
elapsedMs: Date.now() - started,
|
|
1494
1598
|
input: { files: input.files.length, bytes: input.bytes, dropped: input.dropped, truncated: input.truncated },
|
|
1495
1599
|
promptTruncated: asked.length > COMMIT_MAX_PROMPT_CHARS,
|
|
@@ -1502,6 +1606,8 @@ async function commitMessage(payload) {
|
|
|
1502
1606
|
const METHODS = {
|
|
1503
1607
|
version,
|
|
1504
1608
|
'commit-message': commitMessage,
|
|
1609
|
+
'commit-models': commitModels,
|
|
1610
|
+
'commit-efforts': commitEfforts,
|
|
1505
1611
|
repos,
|
|
1506
1612
|
summary,
|
|
1507
1613
|
branches,
|