dsh-ide-git 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/package.json +3 -2
- package/src/client.js +1202 -7
- package/src/index.js +260 -0
package/src/client.js
CHANGED
|
@@ -38,6 +38,9 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
would escape the mount and 404 behind any strict proxy. */
|
|
39
39
|
const API_BASE = 'dsh-ide-git/api'
|
|
40
40
|
const TAB_ID = 'dsh-ide-git:panel'
|
|
41
|
+
/* The bundle's package name: the key the Plugins page uses for a bundle's own
|
|
42
|
+
page and its `plugins.bundle.config` slot. */
|
|
43
|
+
const SETTINGS_BUNDLE = 'dsh-ide-git'
|
|
41
44
|
/* The namespace this plugin's dictionaries are published under in the DSH
|
|
42
45
|
locale registry. */
|
|
43
46
|
const LOCALE_NS = 'dsh-ide-git'
|
|
@@ -155,6 +158,30 @@ window.__ModuleLoader__.load({
|
|
|
155
158
|
'toolbar.tree': '分支栏',
|
|
156
159
|
'rail.more': '更多操作',
|
|
157
160
|
'rail.settings': '动作条设置',
|
|
161
|
+
'rail.openSettings': '打开插件设置',
|
|
162
|
+
'settings.card.title': 'Git 工具窗口设置',
|
|
163
|
+
'settings.card.desc': '动作条按钮与 AI 提交信息',
|
|
164
|
+
'settings.rail.title': '动作条按钮',
|
|
165
|
+
'settings.rail.hint': '勾选要显示在动作条上的动作;顺序固定为默认顺序,取消勾选即隐藏。',
|
|
166
|
+
'settings.rail.unavailable': '当前宿主没有设置服务,请用面板内的「动作条设置」。',
|
|
167
|
+
'settings.commit.title': 'AI 提交信息',
|
|
168
|
+
'settings.commit.model': '使用的模型',
|
|
169
|
+
'settings.commit.modelHint': '留空 = 跟随当前会话(provider/model)',
|
|
170
|
+
'settings.commit.reasoning': '思考强度',
|
|
171
|
+
'settings.commit.reasoningHint': '留空 = 模型默认',
|
|
172
|
+
'settings.commit.prompt': '补充提示词',
|
|
173
|
+
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,不超过 72 字符',
|
|
174
|
+
'settings.commit.tooLong': '超过 {n} 个字符的部分不会保存',
|
|
175
|
+
'changes.aiWrite': 'AI 写提交信息',
|
|
176
|
+
'changes.aiBusy': '正在生成…',
|
|
177
|
+
'changes.aiQuota': '会消耗你的额度',
|
|
178
|
+
'changes.aiOverwriteTitle': '覆盖当前草稿?',
|
|
179
|
+
'changes.aiOverwriteText': 'AI 生成的内容会替换你已经输入的提交信息(可以撤销)。',
|
|
180
|
+
'changes.aiUndo': '撤销,恢复我的草稿',
|
|
181
|
+
'changes.aiFailed': '生成失败:',
|
|
182
|
+
'changes.aiNotice': '部分变更没有发给模型(已截断或丢弃),信息可能不完整。',
|
|
183
|
+
'settings.rail.writeFailed': '设置没写进去,已退回原状态;请重试。',
|
|
184
|
+
'settings.rail.readonly': '当前宿主把配置设为只读,开关不可修改。',
|
|
158
185
|
'rail.settingsHint': '拖动或用箭头调整顺序,眼睛图标控制显示',
|
|
159
186
|
'rail.reset': '恢复默认',
|
|
160
187
|
'rail.up': '上移',
|
|
@@ -316,6 +343,30 @@ window.__ModuleLoader__.load({
|
|
|
316
343
|
'toolbar.tree': 'Branch pane',
|
|
317
344
|
'rail.more': 'More actions',
|
|
318
345
|
'rail.settings': 'Action bar settings',
|
|
346
|
+
'rail.openSettings': 'Open plugin settings',
|
|
347
|
+
'settings.card.title': 'Git tool window settings',
|
|
348
|
+
'settings.card.desc': 'Action rail buttons and AI commit message',
|
|
349
|
+
'settings.rail.title': 'Action rail buttons',
|
|
350
|
+
'settings.rail.hint': 'Tick the actions shown on the rail. The order is fixed; unticking one hides it.',
|
|
351
|
+
'settings.rail.unavailable': 'This host has no settings service; use the in-panel “Action rail settings”.',
|
|
352
|
+
'settings.commit.title': 'AI commit message',
|
|
353
|
+
'settings.commit.model': 'Model',
|
|
354
|
+
'settings.commit.modelHint': 'Empty = follow this session (provider/model)',
|
|
355
|
+
'settings.commit.reasoning': 'Reasoning effort',
|
|
356
|
+
'settings.commit.reasoningHint': 'Empty = the model\'s default',
|
|
357
|
+
'settings.commit.prompt': 'Extra instructions',
|
|
358
|
+
'settings.commit.promptHint': 'e.g. Conventional Commits, Chinese subject, at most 72 characters',
|
|
359
|
+
'settings.commit.tooLong': 'Only the first {n} characters are saved',
|
|
360
|
+
'changes.aiWrite': 'Write with AI',
|
|
361
|
+
'changes.aiBusy': 'Writing…',
|
|
362
|
+
'changes.aiQuota': 'Uses your own quota',
|
|
363
|
+
'changes.aiOverwriteTitle': 'Replace the current draft?',
|
|
364
|
+
'changes.aiOverwriteText': 'The AI text will replace the commit message you typed (you can undo it).',
|
|
365
|
+
'changes.aiUndo': 'Undo, bring my draft back',
|
|
366
|
+
'changes.aiFailed': 'Could not write it:',
|
|
367
|
+
'changes.aiNotice': 'Some changes were not sent to the model (truncated or dropped); the message may be incomplete.',
|
|
368
|
+
'settings.rail.writeFailed': 'The setting did not save; the switch went back. Try again.',
|
|
369
|
+
'settings.rail.readonly': 'This host serves the configuration read-only.',
|
|
319
370
|
'rail.settingsHint': 'Drag or use the arrows to reorder; the eye toggles visibility',
|
|
320
371
|
'rail.reset': 'Restore defaults',
|
|
321
372
|
'rail.up': 'Move up',
|
|
@@ -485,6 +536,30 @@ window.__ModuleLoader__.load({
|
|
|
485
536
|
'toolbar.tree': '分支面板',
|
|
486
537
|
'rail.more': '更多操作',
|
|
487
538
|
'rail.settings': '動作列設定',
|
|
539
|
+
'rail.openSettings': '開啟外掛設定',
|
|
540
|
+
'settings.card.title': 'Git 工具視窗設定',
|
|
541
|
+
'settings.card.desc': '動作列按鈕與 AI 提交訊息',
|
|
542
|
+
'settings.rail.title': '動作列按鈕',
|
|
543
|
+
'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
|
|
544
|
+
'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
|
|
545
|
+
'settings.commit.title': 'AI 提交訊息',
|
|
546
|
+
'settings.commit.model': '使用嘅模型',
|
|
547
|
+
'settings.commit.modelHint': '留空 = 跟隨當前對話(provider/model)',
|
|
548
|
+
'settings.commit.reasoning': '思考強度',
|
|
549
|
+
'settings.commit.reasoningHint': '留空 = 模型預設',
|
|
550
|
+
'settings.commit.prompt': '補充提示詞',
|
|
551
|
+
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,唔超過 72 字',
|
|
552
|
+
'settings.commit.tooLong': '超過 {n} 個字嘅部分唔會儲存',
|
|
553
|
+
'changes.aiWrite': 'AI 寫提交訊息',
|
|
554
|
+
'changes.aiBusy': '生成緊…',
|
|
555
|
+
'changes.aiQuota': '會消耗你嘅額度',
|
|
556
|
+
'changes.aiOverwriteTitle': '覆蓋當前草稿?',
|
|
557
|
+
'changes.aiOverwriteText': 'AI 生成嘅內容會換走你打咗嘅提交訊息(可以撤銷)。',
|
|
558
|
+
'changes.aiUndo': '撤銷,還原我嘅草稿',
|
|
559
|
+
'changes.aiFailed': '生成失敗:',
|
|
560
|
+
'changes.aiNotice': '有部分變更冇發俾模型(已截斷或丟棄),訊息可能唔完整。',
|
|
561
|
+
'settings.rail.writeFailed': '設定寫唔入,已經退回原狀;請再試。',
|
|
562
|
+
'settings.rail.readonly': '目前宿主將設定設為唯讀,開關改唔到。',
|
|
488
563
|
'rail.settingsHint': '拖曳或用箭嘴排序;眼睛圖示切換顯示',
|
|
489
564
|
'rail.reset': '還原預設',
|
|
490
565
|
'rail.up': '上移',
|
|
@@ -646,6 +721,30 @@ window.__ModuleLoader__.load({
|
|
|
646
721
|
'toolbar.tree': '分支面板',
|
|
647
722
|
'rail.more': '更多操作',
|
|
648
723
|
'rail.settings': '動作列設定',
|
|
724
|
+
'rail.openSettings': '開啟外掛設定',
|
|
725
|
+
'settings.card.title': 'Git 工具視窗設定',
|
|
726
|
+
'settings.card.desc': '動作列按鈕與 AI 提交訊息',
|
|
727
|
+
'settings.rail.title': '動作列按鈕',
|
|
728
|
+
'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
|
|
729
|
+
'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
|
|
730
|
+
'settings.commit.title': 'AI 提交訊息',
|
|
731
|
+
'settings.commit.model': '使用的模型',
|
|
732
|
+
'settings.commit.modelHint': '留空 = 跟隨目前工作階段(provider/model)',
|
|
733
|
+
'settings.commit.reasoning': '思考強度',
|
|
734
|
+
'settings.commit.reasoningHint': '留空 = 模型預設',
|
|
735
|
+
'settings.commit.prompt': '補充提示詞',
|
|
736
|
+
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,不超過 72 字元',
|
|
737
|
+
'settings.commit.tooLong': '超過 {n} 個字元的部分不會儲存',
|
|
738
|
+
'changes.aiWrite': 'AI 寫提交訊息',
|
|
739
|
+
'changes.aiBusy': '正在生成…',
|
|
740
|
+
'changes.aiQuota': '會消耗你的額度',
|
|
741
|
+
'changes.aiOverwriteTitle': '覆蓋目前的草稿?',
|
|
742
|
+
'changes.aiOverwriteText': 'AI 生成的內容會取代你已輸入的提交訊息(可以復原)。',
|
|
743
|
+
'changes.aiUndo': '復原,還原我的草稿',
|
|
744
|
+
'changes.aiFailed': '生成失敗:',
|
|
745
|
+
'changes.aiNotice': '部分變更沒有傳給模型(已截斷或捨棄),訊息可能不完整。',
|
|
746
|
+
'settings.rail.writeFailed': '設定沒有寫入,已退回原狀態;請重試。',
|
|
747
|
+
'settings.rail.readonly': '目前宿主將設定設為唯讀,開關無法修改。',
|
|
649
748
|
'rail.settingsHint': '拖曳或用箭頭排序;眼睛圖示切換顯示',
|
|
650
749
|
'rail.reset': '還原預設',
|
|
651
750
|
'rail.up': '上移',
|
|
@@ -807,6 +906,30 @@ window.__ModuleLoader__.load({
|
|
|
807
906
|
'toolbar.tree': '分支面板',
|
|
808
907
|
'rail.more': '更多操作',
|
|
809
908
|
'rail.settings': '動作列設定',
|
|
909
|
+
'rail.openSettings': '開啟外掛設定',
|
|
910
|
+
'settings.card.title': 'Git 工具視窗設定',
|
|
911
|
+
'settings.card.desc': '動作列按鈕與 AI 提交訊息',
|
|
912
|
+
'settings.rail.title': '動作列按鈕',
|
|
913
|
+
'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
|
|
914
|
+
'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
|
|
915
|
+
'settings.commit.title': 'AI 提交訊息',
|
|
916
|
+
'settings.commit.model': '使用的模型',
|
|
917
|
+
'settings.commit.modelHint': '留空 = 跟隨目前工作階段(provider/model)',
|
|
918
|
+
'settings.commit.reasoning': '思考強度',
|
|
919
|
+
'settings.commit.reasoningHint': '留空 = 模型預設',
|
|
920
|
+
'settings.commit.prompt': '補充提示詞',
|
|
921
|
+
'settings.commit.promptHint': '例如:用 Conventional Commits,subject 用中文,不超過 72 字元',
|
|
922
|
+
'settings.commit.tooLong': '超過 {n} 個字元的部分不會儲存',
|
|
923
|
+
'changes.aiWrite': 'AI 寫提交訊息',
|
|
924
|
+
'changes.aiBusy': '正在生成…',
|
|
925
|
+
'changes.aiQuota': '會消耗你的額度',
|
|
926
|
+
'changes.aiOverwriteTitle': '覆蓋目前的草稿?',
|
|
927
|
+
'changes.aiOverwriteText': 'AI 生成的內容會取代你已輸入的提交訊息(可以復原)。',
|
|
928
|
+
'changes.aiUndo': '復原,還原我的草稿',
|
|
929
|
+
'changes.aiFailed': '生成失敗:',
|
|
930
|
+
'changes.aiNotice': '部分變更沒有傳給模型(已截斷或捨棄),訊息可能不完整。',
|
|
931
|
+
'settings.rail.writeFailed': '設定沒有寫入,已退回原狀態;請重試。',
|
|
932
|
+
'settings.rail.readonly': '目前宿主將設定設為唯讀,開關無法修改。',
|
|
810
933
|
'rail.settingsHint': '拖曳或用箭嘴排序;眼睛圖示切換顯示',
|
|
811
934
|
'rail.reset': '還原預設',
|
|
812
935
|
'rail.up': '上移',
|
|
@@ -968,6 +1091,30 @@ window.__ModuleLoader__.load({
|
|
|
968
1091
|
'toolbar.tree': 'ブランチペイン',
|
|
969
1092
|
'rail.more': 'その他の操作',
|
|
970
1093
|
'rail.settings': 'アクションバーの設定',
|
|
1094
|
+
'rail.openSettings': 'プラグイン設定を開く',
|
|
1095
|
+
'settings.card.title': 'Gitツールウィンドウの設定',
|
|
1096
|
+
'settings.card.desc': 'アクションレールのボタンと AI コミットメッセージ',
|
|
1097
|
+
'settings.rail.title': 'アクションレールのボタン',
|
|
1098
|
+
'settings.rail.hint': 'レールに表示する操作にチェックを入れてください。順序は固定で、チェックを外すと非表示になります。',
|
|
1099
|
+
'settings.rail.unavailable': 'このホストには設定サービスがないため、パネル内の「アクションレール設定」を使ってください。',
|
|
1100
|
+
'settings.commit.title': 'AI コミットメッセージ',
|
|
1101
|
+
'settings.commit.model': '使用するモデル',
|
|
1102
|
+
'settings.commit.modelHint': '空欄 = 現在のセッションに従う(provider/model)',
|
|
1103
|
+
'settings.commit.reasoning': '思考の強度',
|
|
1104
|
+
'settings.commit.reasoningHint': '空欄 = モデルの既定値',
|
|
1105
|
+
'settings.commit.prompt': '追加の指示',
|
|
1106
|
+
'settings.commit.promptHint': '例:Conventional Commits、subject は日本語、72 文字以内',
|
|
1107
|
+
'settings.commit.tooLong': '{n} 文字を超える部分は保存されません',
|
|
1108
|
+
'changes.aiWrite': 'AI で書く',
|
|
1109
|
+
'changes.aiBusy': '生成中…',
|
|
1110
|
+
'changes.aiQuota': 'あなたの利用枠を消費します',
|
|
1111
|
+
'changes.aiOverwriteTitle': '現在の下書きを置き換えますか?',
|
|
1112
|
+
'changes.aiOverwriteText': 'AI の文章が入力済みのコミットメッセージを置き換えます(元に戻せます)。',
|
|
1113
|
+
'changes.aiUndo': '元に戻す',
|
|
1114
|
+
'changes.aiFailed': '生成できませんでした:',
|
|
1115
|
+
'changes.aiNotice': '一部の変更はモデルに送られていません(切り詰めまたは除外)。内容が不完全な可能性があります。',
|
|
1116
|
+
'settings.rail.writeFailed': '設定を保存できませんでした。元の状態に戻しました。もう一度お試しください。',
|
|
1117
|
+
'settings.rail.readonly': 'このホストは設定を読み取り専用で提供しています。',
|
|
971
1118
|
'rail.settingsHint': 'ドラッグまたは矢印で並べ替え、目のアイコンで表示を切り替え',
|
|
972
1119
|
'rail.reset': '既定に戻す',
|
|
973
1120
|
'rail.up': '上へ',
|
|
@@ -1129,6 +1276,30 @@ window.__ModuleLoader__.load({
|
|
|
1129
1276
|
'toolbar.tree': '브랜치 창',
|
|
1130
1277
|
'rail.more': '추가 작업',
|
|
1131
1278
|
'rail.settings': '작업 표시줄 설정',
|
|
1279
|
+
'rail.openSettings': '플러그인 설정 열기',
|
|
1280
|
+
'settings.card.title': 'Git 도구 창 설정',
|
|
1281
|
+
'settings.card.desc': '액션 레일 버튼 및 AI 커밋 메시지',
|
|
1282
|
+
'settings.rail.title': '액션 레일 버튼',
|
|
1283
|
+
'settings.rail.hint': '레일에 표시할 동작을 선택하세요. 순서는 고정이며 선택을 해제하면 숨겨집니다.',
|
|
1284
|
+
'settings.rail.unavailable': '이 호스트에는 설정 서비스가 없으므로 패널의 "액션 레일 설정"을 사용하세요.',
|
|
1285
|
+
'settings.commit.title': 'AI 커밋 메시지',
|
|
1286
|
+
'settings.commit.model': '사용할 모델',
|
|
1287
|
+
'settings.commit.modelHint': '비우면 현재 세션을 따릅니다(provider/model)',
|
|
1288
|
+
'settings.commit.reasoning': '추론 강도',
|
|
1289
|
+
'settings.commit.reasoningHint': '비우면 모델 기본값',
|
|
1290
|
+
'settings.commit.prompt': '추가 지시',
|
|
1291
|
+
'settings.commit.promptHint': '예: Conventional Commits, 제목은 한국어, 72자 이내',
|
|
1292
|
+
'settings.commit.tooLong': '{n}자를 넘는 부분은 저장되지 않습니다',
|
|
1293
|
+
'changes.aiWrite': 'AI로 작성',
|
|
1294
|
+
'changes.aiBusy': '생성 중…',
|
|
1295
|
+
'changes.aiQuota': '사용자의 할당량을 소모합니다',
|
|
1296
|
+
'changes.aiOverwriteTitle': '현재 초안을 바꿀까요?',
|
|
1297
|
+
'changes.aiOverwriteText': 'AI 텍스트가 입력한 커밋 메시지를 대체합니다(되돌릴 수 있습니다).',
|
|
1298
|
+
'changes.aiUndo': '되돌리기',
|
|
1299
|
+
'changes.aiFailed': '생성하지 못했습니다:',
|
|
1300
|
+
'changes.aiNotice': '일부 변경이 모델에 전달되지 않았습니다(잘림 또는 제외). 내용이 불완전할 수 있습니다.',
|
|
1301
|
+
'settings.rail.writeFailed': '설정이 저장되지 않아 원래 상태로 되돌렸습니다. 다시 시도하세요.',
|
|
1302
|
+
'settings.rail.readonly': '이 호스트는 설정을 읽기 전용으로 제공합니다.',
|
|
1132
1303
|
'rail.settingsHint': '끌어서 또는 화살표로 순서를 바꾸고, 눈 아이콘으로 표시를 전환합니다',
|
|
1133
1304
|
'rail.reset': '기본값 복원',
|
|
1134
1305
|
'rail.up': '위로',
|
|
@@ -1290,6 +1461,30 @@ window.__ModuleLoader__.load({
|
|
|
1290
1461
|
'toolbar.tree': 'Branch-Bereich',
|
|
1291
1462
|
'rail.more': 'Weitere Aktionen',
|
|
1292
1463
|
'rail.settings': 'Einstellungen der Aktionsleiste',
|
|
1464
|
+
'rail.openSettings': 'Plugin-Einstellungen öffnen',
|
|
1465
|
+
'settings.card.title': 'Git-Werkzeugfenster-Einstellungen',
|
|
1466
|
+
'settings.card.desc': 'Aktionsleisten-Schaltflächen und KI-Commit-Nachricht',
|
|
1467
|
+
'settings.rail.title': 'Schaltflächen der Aktionsleiste',
|
|
1468
|
+
'settings.rail.hint': 'Die auf der Leiste sichtbaren Aktionen ankreuzen. Die Reihenfolge ist fest; abwählen blendet eine aus.',
|
|
1469
|
+
'settings.rail.unavailable': 'Dieser Host hat keinen Einstellungsdienst; nutze die „Aktionsleisten-Einstellungen“ im Panel.',
|
|
1470
|
+
'settings.commit.title': 'KI-Commit-Nachricht',
|
|
1471
|
+
'settings.commit.model': 'Modell',
|
|
1472
|
+
'settings.commit.modelHint': 'Leer = dieser Sitzung folgen (provider/model)',
|
|
1473
|
+
'settings.commit.reasoning': 'Denkaufwand',
|
|
1474
|
+
'settings.commit.reasoningHint': 'Leer = Standard des Modells',
|
|
1475
|
+
'settings.commit.prompt': 'Zusätzliche Anweisungen',
|
|
1476
|
+
'settings.commit.promptHint': 'z. B. Conventional Commits, Betreff auf Deutsch, höchstens 72 Zeichen',
|
|
1477
|
+
'settings.commit.tooLong': 'Nur die ersten {n} Zeichen werden gespeichert',
|
|
1478
|
+
'changes.aiWrite': 'Mit KI schreiben',
|
|
1479
|
+
'changes.aiBusy': 'Wird geschrieben…',
|
|
1480
|
+
'changes.aiQuota': 'Verbraucht dein eigenes Kontingent',
|
|
1481
|
+
'changes.aiOverwriteTitle': 'Aktuellen Entwurf ersetzen?',
|
|
1482
|
+
'changes.aiOverwriteText': 'Der KI-Text ersetzt deine eingegebene Commit-Nachricht (rückgängig möglich).',
|
|
1483
|
+
'changes.aiUndo': 'Rückgängig',
|
|
1484
|
+
'changes.aiFailed': 'Konnte nicht schreiben:',
|
|
1485
|
+
'changes.aiNotice': 'Einige Änderungen wurden nicht ans Modell gesendet (gekürzt oder verworfen); die Nachricht kann unvollständig sein.',
|
|
1486
|
+
'settings.rail.writeFailed': 'Die Einstellung wurde nicht gespeichert; der Schalter ist zurückgesprungen. Bitte erneut versuchen.',
|
|
1487
|
+
'settings.rail.readonly': 'Dieser Host liefert die Konfiguration schreibgeschützt.',
|
|
1293
1488
|
'rail.settingsHint': 'Ziehen oder Pfeile zum Sortieren, das Auge schaltet die Sichtbarkeit um',
|
|
1294
1489
|
'rail.reset': 'Standard wiederherstellen',
|
|
1295
1490
|
'rail.up': 'Nach oben',
|
|
@@ -1451,6 +1646,30 @@ window.__ModuleLoader__.load({
|
|
|
1451
1646
|
'toolbar.tree': 'Volet des branches',
|
|
1452
1647
|
'rail.more': 'Plus d\'actions',
|
|
1453
1648
|
'rail.settings': 'Réglages de la barre d\'actions',
|
|
1649
|
+
'rail.openSettings': 'Ouvrir les réglages du plugin',
|
|
1650
|
+
'settings.card.title': 'Paramètres de la fenêtre d’outils Git',
|
|
1651
|
+
'settings.card.desc': 'Boutons de la barre d’actions et message de commit par IA',
|
|
1652
|
+
'settings.rail.title': 'Boutons de la barre d’actions',
|
|
1653
|
+
'settings.rail.hint': 'Cochez les actions affichées sur la barre. L’ordre est fixe ; décocher masque une action.',
|
|
1654
|
+
'settings.rail.unavailable': 'Cet hôte n’a pas de service de réglages ; utilisez « Réglages de la barre d’actions » dans le panneau.',
|
|
1655
|
+
'settings.commit.title': 'Message de commit par IA',
|
|
1656
|
+
'settings.commit.model': 'Modèle',
|
|
1657
|
+
'settings.commit.modelHint': 'Vide = suivre cette session (provider/model)',
|
|
1658
|
+
'settings.commit.reasoning': 'Effort de raisonnement',
|
|
1659
|
+
'settings.commit.reasoningHint': 'Vide = valeur par défaut du modèle',
|
|
1660
|
+
'settings.commit.prompt': 'Instructions supplémentaires',
|
|
1661
|
+
'settings.commit.promptHint': 'ex. Conventional Commits, sujet en français, 72 caractères maximum',
|
|
1662
|
+
'settings.commit.tooLong': 'Seuls les {n} premiers caractères sont enregistrés',
|
|
1663
|
+
'changes.aiWrite': 'Écrire avec l’IA',
|
|
1664
|
+
'changes.aiBusy': 'Rédaction…',
|
|
1665
|
+
'changes.aiQuota': 'Consomme votre propre quota',
|
|
1666
|
+
'changes.aiOverwriteTitle': 'Remplacer le brouillon actuel ?',
|
|
1667
|
+
'changes.aiOverwriteText': 'Le texte de l’IA remplacera le message saisi (annulable).',
|
|
1668
|
+
'changes.aiUndo': 'Annuler, reprendre mon brouillon',
|
|
1669
|
+
'changes.aiFailed': 'Échec de la rédaction :',
|
|
1670
|
+
'changes.aiNotice': 'Certaines modifications n’ont pas été envoyées au modèle (tronquées ou écartées) ; le message peut être incomplet.',
|
|
1671
|
+
'settings.rail.writeFailed': 'Le réglage n’a pas été enregistré ; l’interrupteur est revenu en place. Réessayez.',
|
|
1672
|
+
'settings.rail.readonly': 'Cet hôte fournit la configuration en lecture seule.',
|
|
1454
1673
|
'rail.settingsHint': 'Glisser ou utiliser les flèches pour réordonner ; l\'œil bascule la visibilité',
|
|
1455
1674
|
'rail.reset': 'Rétablir les valeurs par défaut',
|
|
1456
1675
|
'rail.up': 'Monter',
|
|
@@ -1612,6 +1831,30 @@ window.__ModuleLoader__.load({
|
|
|
1612
1831
|
'toolbar.tree': 'Панель веток',
|
|
1613
1832
|
'rail.more': 'Другие действия',
|
|
1614
1833
|
'rail.settings': 'Настройки панели действий',
|
|
1834
|
+
'rail.openSettings': 'Открыть настройки плагина',
|
|
1835
|
+
'settings.card.title': 'Настройки окна инструментов Git',
|
|
1836
|
+
'settings.card.desc': 'Кнопки панели действий и сообщение коммита от ИИ',
|
|
1837
|
+
'settings.rail.title': 'Кнопки панели действий',
|
|
1838
|
+
'settings.rail.hint': 'Отметьте действия, показываемые на панели. Порядок фиксирован; снятие флажка скрывает действие.',
|
|
1839
|
+
'settings.rail.unavailable': 'На этом хосте нет службы настроек; используйте «Настройки панели действий» в панели.',
|
|
1840
|
+
'settings.commit.title': 'Сообщение коммита от ИИ',
|
|
1841
|
+
'settings.commit.model': 'Модель',
|
|
1842
|
+
'settings.commit.modelHint': 'Пусто = как в этой сессии (provider/model)',
|
|
1843
|
+
'settings.commit.reasoning': 'Усилие рассуждения',
|
|
1844
|
+
'settings.commit.reasoningHint': 'Пусто = по умолчанию у модели',
|
|
1845
|
+
'settings.commit.prompt': 'Дополнительные указания',
|
|
1846
|
+
'settings.commit.promptHint': 'напр. Conventional Commits, тема по-русски, до 72 символов',
|
|
1847
|
+
'settings.commit.tooLong': 'Сохраняются только первые {n} символов',
|
|
1848
|
+
'changes.aiWrite': 'Написать с ИИ',
|
|
1849
|
+
'changes.aiBusy': 'Генерация…',
|
|
1850
|
+
'changes.aiQuota': 'Расходует вашу квоту',
|
|
1851
|
+
'changes.aiOverwriteTitle': 'Заменить текущий черновик?',
|
|
1852
|
+
'changes.aiOverwriteText': 'Текст ИИ заменит введённое сообщение коммита (можно отменить).',
|
|
1853
|
+
'changes.aiUndo': 'Отменить',
|
|
1854
|
+
'changes.aiFailed': 'Не удалось сгенерировать:',
|
|
1855
|
+
'changes.aiNotice': 'Часть изменений не отправлена модели (обрезана или отброшена); сообщение может быть неполным.',
|
|
1856
|
+
'settings.rail.writeFailed': 'Настройка не сохранилась; переключатель вернулся назад. Попробуйте снова.',
|
|
1857
|
+
'settings.rail.readonly': 'Этот хост отдаёт конфигурацию только для чтения.',
|
|
1615
1858
|
'rail.settingsHint': 'Перетаскивайте или используйте стрелки для порядка; значок глаза переключает видимость',
|
|
1616
1859
|
'rail.reset': 'Вернуть значения по умолчанию',
|
|
1617
1860
|
'rail.up': 'Вверх',
|
|
@@ -1773,6 +2016,30 @@ window.__ModuleLoader__.load({
|
|
|
1773
2016
|
'toolbar.tree': 'Painel de branches',
|
|
1774
2017
|
'rail.more': 'Mais ações',
|
|
1775
2018
|
'rail.settings': 'Configurações da barra de ações',
|
|
2019
|
+
'rail.openSettings': 'Abrir configurações do plugin',
|
|
2020
|
+
'settings.card.title': 'Configurações da janela de ferramentas Git',
|
|
2021
|
+
'settings.card.desc': 'Botões da barra de ações e mensagem de commit por IA',
|
|
2022
|
+
'settings.rail.title': 'Botões da barra de ações',
|
|
2023
|
+
'settings.rail.hint': 'Marque as ações exibidas na barra. A ordem é fixa; desmarcar oculta uma ação.',
|
|
2024
|
+
'settings.rail.unavailable': 'Este host não tem serviço de configurações; use «Configurações da barra de ações» no painel.',
|
|
2025
|
+
'settings.commit.title': 'Mensagem de commit por IA',
|
|
2026
|
+
'settings.commit.model': 'Modelo',
|
|
2027
|
+
'settings.commit.modelHint': 'Vazio = seguir esta sessão (provider/model)',
|
|
2028
|
+
'settings.commit.reasoning': 'Esforço de raciocínio',
|
|
2029
|
+
'settings.commit.reasoningHint': 'Vazio = padrão do modelo',
|
|
2030
|
+
'settings.commit.prompt': 'Instruções extras',
|
|
2031
|
+
'settings.commit.promptHint': 'ex.: Conventional Commits, assunto em português, até 72 caracteres',
|
|
2032
|
+
'settings.commit.tooLong': 'Só os primeiros {n} caracteres são salvos',
|
|
2033
|
+
'changes.aiWrite': 'Escrever com IA',
|
|
2034
|
+
'changes.aiBusy': 'Gerando…',
|
|
2035
|
+
'changes.aiQuota': 'Consome a sua cota',
|
|
2036
|
+
'changes.aiOverwriteTitle': 'Substituir o rascunho atual?',
|
|
2037
|
+
'changes.aiOverwriteText': 'O texto da IA substituirá a mensagem que você digitou (dá para desfazer).',
|
|
2038
|
+
'changes.aiUndo': 'Desfazer',
|
|
2039
|
+
'changes.aiFailed': 'Não foi possível gerar:',
|
|
2040
|
+
'changes.aiNotice': 'Algumas alterações não foram enviadas ao modelo (truncadas ou descartadas); a mensagem pode ficar incompleta.',
|
|
2041
|
+
'settings.rail.writeFailed': 'A configuração não foi salva; o interruptor voltou ao lugar. Tente de novo.',
|
|
2042
|
+
'settings.rail.readonly': 'Este host fornece a configuração somente para leitura.',
|
|
1776
2043
|
'rail.settingsHint': 'Arraste ou use as setas para reordenar; o olho alterna a visibilidade',
|
|
1777
2044
|
'rail.reset': 'Restaurar padrões',
|
|
1778
2045
|
'rail.up': 'Mover para cima',
|
|
@@ -1934,6 +2201,30 @@ window.__ModuleLoader__.load({
|
|
|
1934
2201
|
'toolbar.tree': 'Riquadro dei branch',
|
|
1935
2202
|
'rail.more': 'Altre azioni',
|
|
1936
2203
|
'rail.settings': 'Impostazioni della barra delle azioni',
|
|
2204
|
+
'rail.openSettings': 'Apri le impostazioni del plugin',
|
|
2205
|
+
'settings.card.title': 'Impostazioni della finestra strumenti Git',
|
|
2206
|
+
'settings.card.desc': 'Pulsanti della barra azioni e messaggio di commit con IA',
|
|
2207
|
+
'settings.rail.title': 'Pulsanti della barra azioni',
|
|
2208
|
+
'settings.rail.hint': 'Seleziona le azioni mostrate sulla barra. L’ordine è fisso; deselezionando se ne nasconde una.',
|
|
2209
|
+
'settings.rail.unavailable': 'Questo host non ha un servizio di impostazioni; usa «Impostazioni barra azioni» nel pannello.',
|
|
2210
|
+
'settings.commit.title': 'Messaggio di commit con IA',
|
|
2211
|
+
'settings.commit.model': 'Modello',
|
|
2212
|
+
'settings.commit.modelHint': 'Vuoto = segui questa sessione (provider/model)',
|
|
2213
|
+
'settings.commit.reasoning': 'Sforzo di ragionamento',
|
|
2214
|
+
'settings.commit.reasoningHint': 'Vuoto = predefinito del modello',
|
|
2215
|
+
'settings.commit.prompt': 'Istruzioni aggiuntive',
|
|
2216
|
+
'settings.commit.promptHint': 'es. Conventional Commits, oggetto in italiano, max 72 caratteri',
|
|
2217
|
+
'settings.commit.tooLong': 'Solo i primi {n} caratteri vengono salvati',
|
|
2218
|
+
'changes.aiWrite': 'Scrivi con l’IA',
|
|
2219
|
+
'changes.aiBusy': 'Generazione…',
|
|
2220
|
+
'changes.aiQuota': 'Consuma la tua quota',
|
|
2221
|
+
'changes.aiOverwriteTitle': 'Sostituire la bozza attuale?',
|
|
2222
|
+
'changes.aiOverwriteText': 'Il testo dell’IA sostituirà il messaggio digitato (puoi annullare).',
|
|
2223
|
+
'changes.aiUndo': 'Annulla',
|
|
2224
|
+
'changes.aiFailed': 'Scrittura non riuscita:',
|
|
2225
|
+
'changes.aiNotice': 'Alcune modifiche non sono state inviate al modello (troncate o scartate); il messaggio può essere incompleto.',
|
|
2226
|
+
'settings.rail.writeFailed': 'L’impostazione non è stata salvata; l’interruttore è tornato indietro. Riprova.',
|
|
2227
|
+
'settings.rail.readonly': 'Questo host fornisce la configurazione in sola lettura.',
|
|
1937
2228
|
'rail.settingsHint': 'Trascina o usa le frecce per riordinare; l’occhio attiva o disattiva la visibilità',
|
|
1938
2229
|
'rail.reset': 'Ripristina i valori predefiniti',
|
|
1939
2230
|
'rail.up': 'Sposta su',
|
|
@@ -2095,6 +2386,30 @@ window.__ModuleLoader__.load({
|
|
|
2095
2386
|
'toolbar.tree': 'Branchpaneel',
|
|
2096
2387
|
'rail.more': 'Meer acties',
|
|
2097
2388
|
'rail.settings': 'Instellingen van de actiebalk',
|
|
2389
|
+
'rail.openSettings': 'Plugin-instellingen openen',
|
|
2390
|
+
'settings.card.title': 'Instellingen van het Git-werkvenster',
|
|
2391
|
+
'settings.card.desc': 'Knoppen van de actiebalk en AI-commitbericht',
|
|
2392
|
+
'settings.rail.title': 'Knoppen van de actiebalk',
|
|
2393
|
+
'settings.rail.hint': 'Vink de acties aan die op de balk verschijnen. De volgorde ligt vast; uitvinken verbergt er een.',
|
|
2394
|
+
'settings.rail.unavailable': 'Deze host heeft geen instellingenservice; gebruik "Actiebalk-instellingen" in het paneel.',
|
|
2395
|
+
'settings.commit.title': 'AI-commitbericht',
|
|
2396
|
+
'settings.commit.model': 'Model',
|
|
2397
|
+
'settings.commit.modelHint': 'Leeg = deze sessie volgen (provider/model)',
|
|
2398
|
+
'settings.commit.reasoning': 'Redeneerinspanning',
|
|
2399
|
+
'settings.commit.reasoningHint': 'Leeg = standaard van het model',
|
|
2400
|
+
'settings.commit.prompt': 'Extra instructies',
|
|
2401
|
+
'settings.commit.promptHint': 'bv. Conventional Commits, onderwerp in het Nederlands, max. 72 tekens',
|
|
2402
|
+
'settings.commit.tooLong': 'Alleen de eerste {n} tekens worden opgeslagen',
|
|
2403
|
+
'changes.aiWrite': 'Met AI schrijven',
|
|
2404
|
+
'changes.aiBusy': 'Bezig…',
|
|
2405
|
+
'changes.aiQuota': 'Verbruikt je eigen quota',
|
|
2406
|
+
'changes.aiOverwriteTitle': 'Huidig concept vervangen?',
|
|
2407
|
+
'changes.aiOverwriteText': 'De AI-tekst vervangt je getypte commitbericht (ongedaan te maken).',
|
|
2408
|
+
'changes.aiUndo': 'Ongedaan maken',
|
|
2409
|
+
'changes.aiFailed': 'Kon niet schrijven:',
|
|
2410
|
+
'changes.aiNotice': 'Sommige wijzigingen zijn niet naar het model gestuurd (afgekapt of overgeslagen); het bericht kan onvolledig zijn.',
|
|
2411
|
+
'settings.rail.writeFailed': 'De instelling is niet opgeslagen; de schakelaar sprong terug. Probeer opnieuw.',
|
|
2412
|
+
'settings.rail.readonly': 'Deze host levert de configuratie alleen-lezen.',
|
|
2098
2413
|
'rail.settingsHint': 'Sleep of gebruik de pijlen om te sorteren; het oog schakelt de zichtbaarheid',
|
|
2099
2414
|
'rail.reset': 'Standaardwaarden herstellen',
|
|
2100
2415
|
'rail.up': 'Omhoog',
|
|
@@ -2256,6 +2571,30 @@ window.__ModuleLoader__.load({
|
|
|
2256
2571
|
'toolbar.tree': 'Panel gałęzi',
|
|
2257
2572
|
'rail.more': 'Więcej działań',
|
|
2258
2573
|
'rail.settings': 'Ustawienia paska działań',
|
|
2574
|
+
'rail.openSettings': 'Otwórz ustawienia wtyczki',
|
|
2575
|
+
'settings.card.title': 'Ustawienia okna narzędzi Git',
|
|
2576
|
+
'settings.card.desc': 'Przyciski paska akcji i wiadomość commita od AI',
|
|
2577
|
+
'settings.rail.title': 'Przyciski paska akcji',
|
|
2578
|
+
'settings.rail.hint': 'Zaznacz akcje widoczne na pasku. Kolejność jest stała; odznaczenie ukrywa akcję.',
|
|
2579
|
+
'settings.rail.unavailable': 'Ten host nie ma usługi ustawień; użyj „Ustawienia paska akcji” w panelu.',
|
|
2580
|
+
'settings.commit.title': 'Wiadomość commita od AI',
|
|
2581
|
+
'settings.commit.model': 'Model',
|
|
2582
|
+
'settings.commit.modelHint': 'Puste = zgodnie z tą sesją (provider/model)',
|
|
2583
|
+
'settings.commit.reasoning': 'Poziom rozumowania',
|
|
2584
|
+
'settings.commit.reasoningHint': 'Puste = domyślne modelu',
|
|
2585
|
+
'settings.commit.prompt': 'Dodatkowe instrukcje',
|
|
2586
|
+
'settings.commit.promptHint': 'np. Conventional Commits, temat po polsku, maks. 72 znaki',
|
|
2587
|
+
'settings.commit.tooLong': 'Zapisywane jest tylko pierwszych {n} znaków',
|
|
2588
|
+
'changes.aiWrite': 'Napisz z AI',
|
|
2589
|
+
'changes.aiBusy': 'Generowanie…',
|
|
2590
|
+
'changes.aiQuota': 'Zużywa twój limit',
|
|
2591
|
+
'changes.aiOverwriteTitle': 'Zastąpić bieżący szkic?',
|
|
2592
|
+
'changes.aiOverwriteText': 'Tekst AI zastąpi wpisaną wiadomość commita (można cofnąć).',
|
|
2593
|
+
'changes.aiUndo': 'Cofnij',
|
|
2594
|
+
'changes.aiFailed': 'Nie udało się wygenerować:',
|
|
2595
|
+
'changes.aiNotice': 'Część zmian nie trafiła do modelu (przycięta lub pominięta); wiadomość może być niepełna.',
|
|
2596
|
+
'settings.rail.writeFailed': 'Ustawienie nie zostało zapisane; przełącznik wrócił na miejsce. Spróbuj ponownie.',
|
|
2597
|
+
'settings.rail.readonly': 'Ten host udostępnia konfigurację tylko do odczytu.',
|
|
2259
2598
|
'rail.settingsHint': 'Przeciągnij lub użyj strzałek, aby zmienić kolejność; oko przełącza widoczność',
|
|
2260
2599
|
'rail.reset': 'Przywróć domyślne',
|
|
2261
2600
|
'rail.up': 'W górę',
|
|
@@ -2417,6 +2756,30 @@ window.__ModuleLoader__.load({
|
|
|
2417
2756
|
'toolbar.tree': 'Grenpanel',
|
|
2418
2757
|
'rail.more': 'Fler åtgärder',
|
|
2419
2758
|
'rail.settings': 'Inställningar för åtgärdsfältet',
|
|
2759
|
+
'rail.openSettings': 'Öppna plugininställningar',
|
|
2760
|
+
'settings.card.title': 'Inställningar för Git-verktygsfönstret',
|
|
2761
|
+
'settings.card.desc': 'Knappar i åtgärdsfältet och AI-commitmeddelande',
|
|
2762
|
+
'settings.rail.title': 'Knappar i åtgärdsfältet',
|
|
2763
|
+
'settings.rail.hint': 'Markera de åtgärder som visas i fältet. Ordningen är fast; avmarkering döljer en.',
|
|
2764
|
+
'settings.rail.unavailable': 'Denna värd saknar inställningstjänst; använd ”Åtgärdsfältets inställningar” i panelen.',
|
|
2765
|
+
'settings.commit.title': 'AI-commitmeddelande',
|
|
2766
|
+
'settings.commit.model': 'Modell',
|
|
2767
|
+
'settings.commit.modelHint': 'Tomt = följ denna session (provider/model)',
|
|
2768
|
+
'settings.commit.reasoning': 'Resonemangsinsats',
|
|
2769
|
+
'settings.commit.reasoningHint': 'Tomt = modellens standard',
|
|
2770
|
+
'settings.commit.prompt': 'Extra instruktioner',
|
|
2771
|
+
'settings.commit.promptHint': 't.ex. Conventional Commits, ämne på svenska, högst 72 tecken',
|
|
2772
|
+
'settings.commit.tooLong': 'Endast de första {n} tecknen sparas',
|
|
2773
|
+
'changes.aiWrite': 'Skriv med AI',
|
|
2774
|
+
'changes.aiBusy': 'Skriver…',
|
|
2775
|
+
'changes.aiQuota': 'Använder din egen kvot',
|
|
2776
|
+
'changes.aiOverwriteTitle': 'Ersätt nuvarande utkast?',
|
|
2777
|
+
'changes.aiOverwriteText': 'AI-texten ersätter commitmeddelandet du skrev (går att ångra).',
|
|
2778
|
+
'changes.aiUndo': 'Ångra',
|
|
2779
|
+
'changes.aiFailed': 'Kunde inte skriva:',
|
|
2780
|
+
'changes.aiNotice': 'Vissa ändringar skickades inte till modellen (kapade eller utelämnade); meddelandet kan vara ofullständigt.',
|
|
2781
|
+
'settings.rail.writeFailed': 'Inställningen sparades inte; reglaget gick tillbaka. Försök igen.',
|
|
2782
|
+
'settings.rail.readonly': 'Denna värd tillhandahåller konfigurationen skrivskyddad.',
|
|
2420
2783
|
'rail.settingsHint': 'Dra eller använd pilarna för att sortera; ögat växlar synlighet',
|
|
2421
2784
|
'rail.reset': 'Återställ standard',
|
|
2422
2785
|
'rail.up': 'Flytta upp',
|
|
@@ -2578,6 +2941,30 @@ window.__ModuleLoader__.load({
|
|
|
2578
2941
|
'toolbar.tree': 'Dal paneli',
|
|
2579
2942
|
'rail.more': 'Diğer eylemler',
|
|
2580
2943
|
'rail.settings': 'Eylem çubuğu ayarları',
|
|
2944
|
+
'rail.openSettings': 'Eklenti ayarlarını aç',
|
|
2945
|
+
'settings.card.title': 'Git araç penceresi ayarları',
|
|
2946
|
+
'settings.card.desc': 'Eylem çubuğu düğmeleri ve AI commit mesajı',
|
|
2947
|
+
'settings.rail.title': 'Eylem çubuğu düğmeleri',
|
|
2948
|
+
'settings.rail.hint': 'Çubukta gösterilecek eylemleri işaretleyin. Sıra sabittir; işareti kaldırmak eylemi gizler.',
|
|
2949
|
+
'settings.rail.unavailable': 'Bu ana bilgisayarda ayar hizmeti yok; paneldeki "Eylem çubuğu ayarları"nı kullanın.',
|
|
2950
|
+
'settings.commit.title': 'AI commit mesajı',
|
|
2951
|
+
'settings.commit.model': 'Model',
|
|
2952
|
+
'settings.commit.modelHint': 'Boş = bu oturumu izle (provider/model)',
|
|
2953
|
+
'settings.commit.reasoning': 'Düşünme yoğunluğu',
|
|
2954
|
+
'settings.commit.reasoningHint': 'Boş = modelin varsayılanı',
|
|
2955
|
+
'settings.commit.prompt': 'Ek yönergeler',
|
|
2956
|
+
'settings.commit.promptHint': 'örn. Conventional Commits, konu Türkçe, en fazla 72 karakter',
|
|
2957
|
+
'settings.commit.tooLong': 'Yalnızca ilk {n} karakter kaydedilir',
|
|
2958
|
+
'changes.aiWrite': 'AI ile yaz',
|
|
2959
|
+
'changes.aiBusy': 'Yazılıyor…',
|
|
2960
|
+
'changes.aiQuota': 'Kendi kotanızı harcar',
|
|
2961
|
+
'changes.aiOverwriteTitle': 'Geçerli taslağın yerine geçilsin mi?',
|
|
2962
|
+
'changes.aiOverwriteText': 'AI metni yazdığınız commit mesajının yerine geçer (geri alınabilir).',
|
|
2963
|
+
'changes.aiUndo': 'Geri al',
|
|
2964
|
+
'changes.aiFailed': 'Yazılamadı:',
|
|
2965
|
+
'changes.aiNotice': 'Bazı değişiklikler modele gönderilmedi (kısaltıldı veya atlandı); mesaj eksik olabilir.',
|
|
2966
|
+
'settings.rail.writeFailed': 'Ayar kaydedilemedi; anahtar eski konumuna döndü. Yeniden deneyin.',
|
|
2967
|
+
'settings.rail.readonly': 'Bu ana bilgisayar yapılandırmayı salt okunur sunuyor.',
|
|
2581
2968
|
'rail.settingsHint': 'Sıralamak için sürükleyin veya okları kullanın; göz simgesi görünürlüğü değiştirir',
|
|
2582
2969
|
'rail.reset': 'Varsayılanlara dön',
|
|
2583
2970
|
'rail.up': 'Yukarı taşı',
|
|
@@ -2739,6 +3126,30 @@ window.__ModuleLoader__.load({
|
|
|
2739
3126
|
'toolbar.tree': 'Panel branch',
|
|
2740
3127
|
'rail.more': 'Tindakan lain',
|
|
2741
3128
|
'rail.settings': 'Pengaturan bilah tindakan',
|
|
3129
|
+
'rail.openSettings': 'Buka pengaturan plugin',
|
|
3130
|
+
'settings.card.title': 'Pengaturan jendela alat Git',
|
|
3131
|
+
'settings.card.desc': 'Tombol bilah aksi dan pesan commit AI',
|
|
3132
|
+
'settings.rail.title': 'Tombol bilah aksi',
|
|
3133
|
+
'settings.rail.hint': 'Centang aksi yang ditampilkan di bilah. Urutannya tetap; menghapus centang menyembunyikannya.',
|
|
3134
|
+
'settings.rail.unavailable': 'Host ini tidak punya layanan pengaturan; gunakan "Pengaturan bilah aksi" di panel.',
|
|
3135
|
+
'settings.commit.title': 'Pesan commit AI',
|
|
3136
|
+
'settings.commit.model': 'Model',
|
|
3137
|
+
'settings.commit.modelHint': 'Kosong = ikuti sesi ini (provider/model)',
|
|
3138
|
+
'settings.commit.reasoning': 'Intensitas penalaran',
|
|
3139
|
+
'settings.commit.reasoningHint': 'Kosong = bawaan model',
|
|
3140
|
+
'settings.commit.prompt': 'Instruksi tambahan',
|
|
3141
|
+
'settings.commit.promptHint': 'mis. Conventional Commits, subjek bahasa Indonesia, maks 72 karakter',
|
|
3142
|
+
'settings.commit.tooLong': 'Hanya {n} karakter pertama yang disimpan',
|
|
3143
|
+
'changes.aiWrite': 'Tulis dengan AI',
|
|
3144
|
+
'changes.aiBusy': 'Menulis…',
|
|
3145
|
+
'changes.aiQuota': 'Memakai kuota Anda',
|
|
3146
|
+
'changes.aiOverwriteTitle': 'Ganti draf saat ini?',
|
|
3147
|
+
'changes.aiOverwriteText': 'Teks AI akan menggantikan pesan commit yang Anda tulis (bisa dibatalkan).',
|
|
3148
|
+
'changes.aiUndo': 'Batalkan',
|
|
3149
|
+
'changes.aiFailed': 'Gagal menulis:',
|
|
3150
|
+
'changes.aiNotice': 'Sebagian perubahan tidak dikirim ke model (dipotong atau dibuang); pesan bisa tidak lengkap.',
|
|
3151
|
+
'settings.rail.writeFailed': 'Pengaturan tidak tersimpan; tombol kembali ke posisi semula. Coba lagi.',
|
|
3152
|
+
'settings.rail.readonly': 'Host ini menyajikan konfigurasi hanya-baca.',
|
|
2742
3153
|
'rail.settingsHint': 'Seret atau pakai panah untuk mengurutkan; ikon mata mengalihkan visibilitas',
|
|
2743
3154
|
'rail.reset': 'Kembalikan default',
|
|
2744
3155
|
'rail.up': 'Naikkan',
|
|
@@ -2900,6 +3311,30 @@ window.__ModuleLoader__.load({
|
|
|
2900
3311
|
'toolbar.tree': 'Bảng nhánh',
|
|
2901
3312
|
'rail.more': 'Thao tác khác',
|
|
2902
3313
|
'rail.settings': 'Cài đặt thanh thao tác',
|
|
3314
|
+
'rail.openSettings': 'Mở cài đặt plugin',
|
|
3315
|
+
'settings.card.title': 'Cài đặt cửa sổ công cụ Git',
|
|
3316
|
+
'settings.card.desc': 'Nút thanh tác vụ và thông điệp commit do AI viết',
|
|
3317
|
+
'settings.rail.title': 'Nút thanh tác vụ',
|
|
3318
|
+
'settings.rail.hint': 'Chọn các tác vụ hiển thị trên thanh. Thứ tự cố định; bỏ chọn sẽ ẩn tác vụ đó.',
|
|
3319
|
+
'settings.rail.unavailable': 'Máy chủ này không có dịch vụ cài đặt; hãy dùng “Cài đặt thanh tác vụ” trong bảng điều khiển.',
|
|
3320
|
+
'settings.commit.title': 'Thông điệp commit do AI viết',
|
|
3321
|
+
'settings.commit.model': 'Mô hình',
|
|
3322
|
+
'settings.commit.modelHint': 'Trống = theo phiên này (provider/model)',
|
|
3323
|
+
'settings.commit.reasoning': 'Mức suy luận',
|
|
3324
|
+
'settings.commit.reasoningHint': 'Trống = mặc định của mô hình',
|
|
3325
|
+
'settings.commit.prompt': 'Chỉ dẫn bổ sung',
|
|
3326
|
+
'settings.commit.promptHint': 'ví dụ Conventional Commits, tiêu đề tiếng Việt, tối đa 72 ký tự',
|
|
3327
|
+
'settings.commit.tooLong': 'Chỉ {n} ký tự đầu được lưu',
|
|
3328
|
+
'changes.aiWrite': 'Viết bằng AI',
|
|
3329
|
+
'changes.aiBusy': 'Đang viết…',
|
|
3330
|
+
'changes.aiQuota': 'Dùng hạn mức của bạn',
|
|
3331
|
+
'changes.aiOverwriteTitle': 'Thay bản nháp hiện tại?',
|
|
3332
|
+
'changes.aiOverwriteText': 'Nội dung AI sẽ thay thế thông điệp commit bạn đã nhập (có thể hoàn tác).',
|
|
3333
|
+
'changes.aiUndo': 'Hoàn tác',
|
|
3334
|
+
'changes.aiFailed': 'Không viết được:',
|
|
3335
|
+
'changes.aiNotice': 'Một số thay đổi chưa gửi tới mô hình (bị cắt hoặc bỏ); thông điệp có thể chưa đầy đủ.',
|
|
3336
|
+
'settings.rail.writeFailed': 'Cài đặt không lưu được; công tắc đã trở về trạng thái cũ. Hãy thử lại.',
|
|
3337
|
+
'settings.rail.readonly': 'Máy chủ này chỉ cung cấp cấu hình ở chế độ chỉ đọc.',
|
|
2903
3338
|
'rail.settingsHint': 'Kéo hoặc dùng mũi tên để sắp xếp; biểu tượng con mắt bật tắt hiển thị',
|
|
2904
3339
|
'rail.reset': 'Khôi phục mặc định',
|
|
2905
3340
|
'rail.up': 'Di chuyển lên',
|
|
@@ -3061,6 +3496,30 @@ window.__ModuleLoader__.load({
|
|
|
3061
3496
|
'toolbar.tree': 'لوحة الفروع',
|
|
3062
3497
|
'rail.more': 'مزيد من الإجراءات',
|
|
3063
3498
|
'rail.settings': 'إعدادات شريط الإجراءات',
|
|
3499
|
+
'rail.openSettings': 'فتح إعدادات الإضافة',
|
|
3500
|
+
'settings.card.title': 'إعدادات نافذة أدوات Git',
|
|
3501
|
+
'settings.card.desc': 'أزرار شريط الإجراءات ورسالة إيداع بالذكاء الاصطناعي',
|
|
3502
|
+
'settings.rail.title': 'أزرار شريط الإجراءات',
|
|
3503
|
+
'settings.rail.hint': 'حدّد الإجراءات المعروضة على الشريط. الترتيب ثابت، وإلغاء التحديد يخفي الإجراء.',
|
|
3504
|
+
'settings.rail.unavailable': 'لا تتوفر خدمة إعدادات على هذا المضيف؛ استخدم «إعدادات شريط الإجراءات» في اللوحة.',
|
|
3505
|
+
'settings.commit.title': 'رسالة إيداع بالذكاء الاصطناعي',
|
|
3506
|
+
'settings.commit.model': 'النموذج',
|
|
3507
|
+
'settings.commit.modelHint': 'فارغ = اتبع هذه الجلسة (provider/model)',
|
|
3508
|
+
'settings.commit.reasoning': 'مستوى التفكير',
|
|
3509
|
+
'settings.commit.reasoningHint': 'فارغ = الافتراضي للنموذج',
|
|
3510
|
+
'settings.commit.prompt': 'تعليمات إضافية',
|
|
3511
|
+
'settings.commit.promptHint': 'مثال: Conventional Commits، والعنوان بالعربية، وبحد أقصى 72 حرفًا',
|
|
3512
|
+
'settings.commit.tooLong': 'يُحفظ أول {n} حرفًا فقط',
|
|
3513
|
+
'changes.aiWrite': 'اكتب بالذكاء الاصطناعي',
|
|
3514
|
+
'changes.aiBusy': 'جارٍ الكتابة…',
|
|
3515
|
+
'changes.aiQuota': 'يستهلك حصتك',
|
|
3516
|
+
'changes.aiOverwriteTitle': 'استبدال المسودة الحالية؟',
|
|
3517
|
+
'changes.aiOverwriteText': 'سيستبدل نص الذكاء الاصطناعي رسالة الإيداع التي كتبتها (يمكن التراجع).',
|
|
3518
|
+
'changes.aiUndo': 'تراجع',
|
|
3519
|
+
'changes.aiFailed': 'تعذّرت الكتابة:',
|
|
3520
|
+
'changes.aiNotice': 'لم تُرسل بعض التغييرات إلى النموذج (مقتطعة أو متروكة)؛ قد تكون الرسالة ناقصة.',
|
|
3521
|
+
'settings.rail.writeFailed': 'لم يتم حفظ الإعداد؛ عاد المفتاح إلى حاله. حاول مرة أخرى.',
|
|
3522
|
+
'settings.rail.readonly': 'يوفّر هذا المضيف الإعدادات للقراءة فقط.',
|
|
3064
3523
|
'rail.settingsHint': 'اسحب أو استخدم الأسهم لإعادة الترتيب؛ وأيقونة العين تبدّل الظهور',
|
|
3065
3524
|
'rail.reset': 'استعادة الإعدادات الافتراضية',
|
|
3066
3525
|
'rail.up': 'تحريك لأعلى',
|
|
@@ -3222,6 +3681,30 @@ window.__ModuleLoader__.load({
|
|
|
3222
3681
|
'toolbar.tree': 'शाखा पैनल',
|
|
3223
3682
|
'rail.more': 'अधिक क्रियाएँ',
|
|
3224
3683
|
'rail.settings': 'क्रिया पट्टी सेटिंग्स',
|
|
3684
|
+
'rail.openSettings': 'प्लगइन सेटिंग खोलें',
|
|
3685
|
+
'settings.card.title': 'Git टूल विंडो सेटिंग्स',
|
|
3686
|
+
'settings.card.desc': 'एक्शन रेल बटन और AI कमिट संदेश',
|
|
3687
|
+
'settings.rail.title': 'एक्शन रेल बटन',
|
|
3688
|
+
'settings.rail.hint': 'रेल पर दिखने वाली क्रियाएँ चुनें। क्रम निश्चित है; चुनाव हटाने पर वह छिप जाती है।',
|
|
3689
|
+
'settings.rail.unavailable': 'इस होस्ट में सेटिंग सेवा नहीं है; पैनल में «एक्शन रेल सेटिंग» का उपयोग करें।',
|
|
3690
|
+
'settings.commit.title': 'AI कमिट संदेश',
|
|
3691
|
+
'settings.commit.model': 'मॉडल',
|
|
3692
|
+
'settings.commit.modelHint': 'खाली = इस सत्र का अनुसरण करें (provider/model)',
|
|
3693
|
+
'settings.commit.reasoning': 'तर्क की तीव्रता',
|
|
3694
|
+
'settings.commit.reasoningHint': 'खाली = मॉडल का डिफ़ॉल्ट',
|
|
3695
|
+
'settings.commit.prompt': 'अतिरिक्त निर्देश',
|
|
3696
|
+
'settings.commit.promptHint': 'जैसे Conventional Commits, विषय हिंदी में, अधिकतम 72 अक्षर',
|
|
3697
|
+
'settings.commit.tooLong': 'केवल पहले {n} अक्षर सहेजे जाते हैं',
|
|
3698
|
+
'changes.aiWrite': 'AI से लिखें',
|
|
3699
|
+
'changes.aiBusy': 'लिखा जा रहा है…',
|
|
3700
|
+
'changes.aiQuota': 'आपका कोटा खर्च होता है',
|
|
3701
|
+
'changes.aiOverwriteTitle': 'मौजूदा ड्राफ़्ट बदलें?',
|
|
3702
|
+
'changes.aiOverwriteText': 'AI पाठ आपके लिखे कमिट संदेश की जगह लेगा (पूर्ववत कर सकते हैं)।',
|
|
3703
|
+
'changes.aiUndo': 'पूर्ववत करें',
|
|
3704
|
+
'changes.aiFailed': 'लिख नहीं सका:',
|
|
3705
|
+
'changes.aiNotice': 'कुछ बदलाव मॉडल को नहीं भेजे गए (काटे या छोड़े गए); संदेश अधूरा हो सकता है।',
|
|
3706
|
+
'settings.rail.writeFailed': 'सेटिंग सहेजी नहीं गई; स्विच वापस पुरानी स्थिति में आ गया। फिर से कोशिश करें।',
|
|
3707
|
+
'settings.rail.readonly': 'यह होस्ट कॉन्फ़िगरेशन को केवल-पढ़ने के लिए देता है।',
|
|
3225
3708
|
'rail.settingsHint': 'क्रम बदलने के लिए खींचें या तीरों का उपयोग करें; आँख आइकन दृश्यता बदलता है',
|
|
3226
3709
|
'rail.reset': 'डिफ़ॉल्ट बहाल करें',
|
|
3227
3710
|
'rail.up': 'ऊपर ले जाएँ',
|
|
@@ -3383,6 +3866,30 @@ window.__ModuleLoader__.load({
|
|
|
3383
3866
|
'toolbar.tree': 'แผงแบรนช์',
|
|
3384
3867
|
'rail.more': 'การทำงานเพิ่มเติม',
|
|
3385
3868
|
'rail.settings': 'ตั้งค่าแถบการทำงาน',
|
|
3869
|
+
'rail.openSettings': 'เปิดการตั้งค่าปลั๊กอิน',
|
|
3870
|
+
'settings.card.title': 'การตั้งค่าหน้าต่างเครื่องมือ Git',
|
|
3871
|
+
'settings.card.desc': 'ปุ่มแถบการทำงานและข้อความคอมมิตโดย AI',
|
|
3872
|
+
'settings.rail.title': 'ปุ่มแถบการทำงาน',
|
|
3873
|
+
'settings.rail.hint': 'ทำเครื่องหมายการทำงานที่จะแสดงบนแถบ ลำดับคงที่; ยกเลิกเครื่องหมายเพื่อซ่อน',
|
|
3874
|
+
'settings.rail.unavailable': 'โฮสต์นี้ไม่มีบริการตั้งค่า ให้ใช้ «ตั้งค่าแถบการทำงาน» ในแผง',
|
|
3875
|
+
'settings.commit.title': 'ข้อความคอมมิตโดย AI',
|
|
3876
|
+
'settings.commit.model': 'โมเดล',
|
|
3877
|
+
'settings.commit.modelHint': 'ว่าง = ตามเซสชันนี้ (provider/model)',
|
|
3878
|
+
'settings.commit.reasoning': 'ระดับการใช้เหตุผล',
|
|
3879
|
+
'settings.commit.reasoningHint': 'ว่าง = ค่าเริ่มต้นของโมเดล',
|
|
3880
|
+
'settings.commit.prompt': 'คำสั่งเพิ่มเติม',
|
|
3881
|
+
'settings.commit.promptHint': 'เช่น Conventional Commits หัวข้อเป็นภาษาไทย ไม่เกิน 72 ตัวอักษร',
|
|
3882
|
+
'settings.commit.tooLong': 'บันทึกเพียง {n} ตัวอักษรแรก',
|
|
3883
|
+
'changes.aiWrite': 'เขียนด้วย AI',
|
|
3884
|
+
'changes.aiBusy': 'กำลังเขียน…',
|
|
3885
|
+
'changes.aiQuota': 'ใช้โควตาของคุณ',
|
|
3886
|
+
'changes.aiOverwriteTitle': 'แทนที่ฉบับร่างปัจจุบันหรือไม่',
|
|
3887
|
+
'changes.aiOverwriteText': 'ข้อความจาก AI จะแทนที่ข้อความคอมมิตที่คุณพิมพ์ (เลิกทำได้)',
|
|
3888
|
+
'changes.aiUndo': 'เลิกทำ',
|
|
3889
|
+
'changes.aiFailed': 'เขียนไม่สำเร็จ:',
|
|
3890
|
+
'changes.aiNotice': 'มีการเปลี่ยนแปลงบางส่วนที่ไม่ได้ส่งให้โมเดล (ถูกตัดหรือทิ้ง) ข้อความอาจไม่ครบถ้วน',
|
|
3891
|
+
'settings.rail.writeFailed': 'บันทึกการตั้งค่าไม่สำเร็จ สวิตช์กลับไปสถานะเดิม ลองอีกครั้ง',
|
|
3892
|
+
'settings.rail.readonly': 'โฮสต์นี้ให้บริการการตั้งค่าแบบอ่านอย่างเดียว',
|
|
3386
3893
|
'rail.settingsHint': 'ลากหรือใช้ลูกศรเพื่อจัดลำดับ; ไอคอนตาใช้สลับการแสดง',
|
|
3387
3894
|
'rail.reset': 'คืนค่าเริ่มต้น',
|
|
3388
3895
|
'rail.up': 'เลื่อนขึ้น',
|
|
@@ -3639,7 +4146,78 @@ window.__ModuleLoader__.load({
|
|
|
3639
4146
|
return { order, hidden }
|
|
3640
4147
|
}
|
|
3641
4148
|
|
|
4149
|
+
/* ---------------------------------------------------------------------
|
|
4150
|
+
Where the rail config LIVES. On dsh >= 0.1.7 a plugin's configuration is
|
|
4151
|
+
the row Config (the official settings surface, persisted by the host under
|
|
4152
|
+
the loader row id) and the browser reads it through ctx.configForms — so
|
|
4153
|
+
the settings card and the rail read ONE source and nothing is written
|
|
4154
|
+
twice. An older host has no such service: there the rail keeps its
|
|
4155
|
+
localStorage home and its in-panel editor, which is the only editor those
|
|
4156
|
+
hosts have. `railForm` is that switch; null means legacy.
|
|
4157
|
+
--------------------------------------------------------------------- */
|
|
4158
|
+
const RAIL_NS = 'ide-git'
|
|
4159
|
+
/* One write window for the settings card: a burst of switches becomes one
|
|
4160
|
+
atomic mutation instead of a stream the Host can lose. */
|
|
4161
|
+
const FLUSH_MS = 250
|
|
4162
|
+
const RAIL_MIGRATED_KEY = 'dsh-ide-git.rail.v1.migrated'
|
|
4163
|
+
let railForm = null
|
|
4164
|
+
let railMigrating = false
|
|
4165
|
+
const railWatchers = new Set()
|
|
4166
|
+
|
|
4167
|
+
/* ---- rail config core (pure: tests/smoke.mjs slices this block) ---- */
|
|
4168
|
+
function railFieldOf(id) {
|
|
4169
|
+
return 'rail' + id.charAt(0).toUpperCase() + id.slice(1)
|
|
4170
|
+
}
|
|
4171
|
+
|
|
4172
|
+
/* Stored booleans -> the { order, hidden } shape the rail has always used.
|
|
4173
|
+
The order is the code order now: the settings card edits one switch per
|
|
4174
|
+
action, so a stored permutation would have no editor left, and dropping it
|
|
4175
|
+
keeps this surface honest rather than half-editable. */
|
|
4176
|
+
function railConfigOfValues(values) {
|
|
4177
|
+
const source = values !== null && typeof values === 'object' ? values : {}
|
|
4178
|
+
const hidden = RAIL_IDS.filter((id) => source[railFieldOf(id)] === false)
|
|
4179
|
+
return { order: RAIL_IDS.slice(), hidden: hidden }
|
|
4180
|
+
}
|
|
4181
|
+
|
|
4182
|
+
/* The inverse of railFieldOf, for the one-field write path. Returns null for a
|
|
4183
|
+
field no rail action owns, so a caller cannot invent a row-Config key. */
|
|
4184
|
+
function railIdOfField(field) {
|
|
4185
|
+
if (typeof field !== 'string' || field.indexOf('rail') !== 0 || field.length <= 4) return null
|
|
4186
|
+
const name = field.slice(4)
|
|
4187
|
+
const id = name.charAt(0).toLowerCase() + name.slice(1)
|
|
4188
|
+
return RAIL_IDS.indexOf(id) < 0 ? null : id
|
|
4189
|
+
}
|
|
4190
|
+
|
|
4191
|
+
function railValuesOfConfig(config) {
|
|
4192
|
+
const values = {}
|
|
4193
|
+
for (const id of RAIL_IDS) values[railFieldOf(id)] = config.hidden.indexOf(id) < 0
|
|
4194
|
+
return values
|
|
4195
|
+
}
|
|
4196
|
+
|
|
4197
|
+
/* ---- end rail config core ---- */
|
|
4198
|
+
|
|
4199
|
+
/* The three AI-commit settings live in the same row Config document the
|
|
4200
|
+
settings card writes; the panel reads them per render, so a change in the
|
|
4201
|
+
card applies to the next click without a reload. */
|
|
4202
|
+
function readCommitSettings() {
|
|
4203
|
+
let values = {}
|
|
4204
|
+
const ready = railFormReady()
|
|
4205
|
+
if (ready !== null && ready.value !== null && typeof ready.value === 'object') values = ready.value
|
|
4206
|
+
const text = (field, max) => (typeof values[field] === 'string' ? values[field].trim().slice(0, max) : '')
|
|
4207
|
+
return { model: text('commitModel', 120), reasoning: text('commitReasoning', 60), prompt: text('commitPrompt', 2000) }
|
|
4208
|
+
}
|
|
4209
|
+
|
|
4210
|
+
function railFormReady() {
|
|
4211
|
+
if (railForm === null) return null
|
|
4212
|
+
const snapshot = railForm.getSnapshot()
|
|
4213
|
+
if (snapshot === null || snapshot === undefined || snapshot.status !== 'ready' || snapshot.value === undefined) return null
|
|
4214
|
+
return snapshot
|
|
4215
|
+
}
|
|
4216
|
+
|
|
3642
4217
|
function readRailConfig() {
|
|
4218
|
+
const ready = railFormReady()
|
|
4219
|
+
if (ready !== null) return railConfigOfValues(ready.value)
|
|
4220
|
+
// Legacy home (and the state before the host answers): localStorage.
|
|
3643
4221
|
try {
|
|
3644
4222
|
const raw = window.localStorage.getItem(RAIL_KEY)
|
|
3645
4223
|
if (raw === null || raw === '') return normalizeRail(null)
|
|
@@ -3648,10 +4226,151 @@ window.__ModuleLoader__.load({
|
|
|
3648
4226
|
return normalizeRail(null)
|
|
3649
4227
|
}
|
|
3650
4228
|
|
|
3651
|
-
|
|
4229
|
+
/* `form` lets a caller write through the exact handle it read from (the
|
|
4230
|
+
settings card gets the framework's own form); without it the module handle
|
|
4231
|
+
is used. When a config surface EXISTS but is not ready yet, the write is
|
|
4232
|
+
refused instead of silently landing in the legacy localStorage home — two
|
|
4233
|
+
sources would be worse than a refused edit. */
|
|
4234
|
+
function writeRailConfig(config, form) {
|
|
4235
|
+
const active = form !== undefined && form !== null && typeof form.getSnapshot === 'function' ? form : railForm
|
|
4236
|
+
let ready = null
|
|
4237
|
+
try {
|
|
4238
|
+
const snapshot = active === null || active === undefined ? null : active.getSnapshot()
|
|
4239
|
+
if (snapshot !== null && snapshot !== undefined && snapshot.status === 'ready' && snapshot.value !== undefined) ready = snapshot
|
|
4240
|
+
} catch (error) { void error }
|
|
4241
|
+
if (ready !== null) {
|
|
4242
|
+
if (ready.writable !== true || typeof active.set !== 'function') return
|
|
4243
|
+
railForm = active
|
|
4244
|
+
const current = ready.value !== null && typeof ready.value === 'object' ? ready.value : {}
|
|
4245
|
+
// Only the fields that actually changed: one write, not sixteen.
|
|
4246
|
+
for (const id of RAIL_IDS) {
|
|
4247
|
+
const field = railFieldOf(id)
|
|
4248
|
+
const visible = config.hidden.indexOf(id) < 0
|
|
4249
|
+
if (current[field] === visible) continue
|
|
4250
|
+
const written = active.set(field, visible)
|
|
4251
|
+
if (written !== undefined && written !== null && typeof written.then === 'function') {
|
|
4252
|
+
void written.then((accepted) => {
|
|
4253
|
+
if (accepted === false) console.warn('[dsh-ide-git] rail setting "' + field + '" was refused by the host')
|
|
4254
|
+
}, (error) => { console.warn('[dsh-ide-git] rail setting write failed: ' + String(error && error.message ? error.message : error)) })
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
return
|
|
4258
|
+
}
|
|
4259
|
+
if (railForm !== null) return
|
|
3652
4260
|
try { window.localStorage.setItem(RAIL_KEY, JSON.stringify(config)) } catch (error) { void error }
|
|
3653
4261
|
}
|
|
3654
4262
|
|
|
4263
|
+
/* One switch, one field, one write — computed from the value the user just
|
|
4264
|
+
chose rather than from a whole-config snapshot that may predate the click.
|
|
4265
|
+
The card edits one field at a time, and a full-config write derived from a
|
|
4266
|
+
stale render clobbers whichever sibling was toggled a moment earlier
|
|
4267
|
+
(measured on 16 rapid toggles: 4 fields never reached the row Config and
|
|
4268
|
+
reverted on reload).
|
|
4269
|
+
Returns the write's disposition so the caller can roll its optimistic value
|
|
4270
|
+
back and say so instead of leaving a switch that lies:
|
|
4271
|
+
sent:true + settled:Promise<boolean> when the Host answers
|
|
4272
|
+
sent:false, reason 'read-only' | 'not-ready' | 'unknown'. */
|
|
4273
|
+
function writeRailFields(ops, form) {
|
|
4274
|
+
const active = form !== undefined && form !== null && typeof form.getSnapshot === 'function' ? form : railForm
|
|
4275
|
+
let ready = null
|
|
4276
|
+
try {
|
|
4277
|
+
const snapshot = active === null || active === undefined ? null : active.getSnapshot()
|
|
4278
|
+
if (snapshot !== null && snapshot !== undefined && snapshot.status === 'ready' && snapshot.value !== undefined) ready = snapshot
|
|
4279
|
+
} catch (error) { void error }
|
|
4280
|
+
if (ready !== null) {
|
|
4281
|
+
if (ready.writable !== true || typeof active.mutate !== 'function') return { sent: false, reason: 'read-only' }
|
|
4282
|
+
railForm = active
|
|
4283
|
+
const settled = Promise.resolve(active.mutate(ops)).then(
|
|
4284
|
+
(accepted) => accepted !== false,
|
|
4285
|
+
(error) => {
|
|
4286
|
+
console.warn('[dsh-ide-git] rail setting write failed: ' + String(error && error.message ? error.message : error))
|
|
4287
|
+
return false
|
|
4288
|
+
},
|
|
4289
|
+
)
|
|
4290
|
+
return { sent: true, settled: settled }
|
|
4291
|
+
}
|
|
4292
|
+
/* A config surface that exists but has not answered yet must refuse rather
|
|
4293
|
+
than fall through to the legacy home: two sources would be worse. */
|
|
4294
|
+
if (railForm !== null) return { sent: false, reason: 'not-ready' }
|
|
4295
|
+
const current = readRailConfig()
|
|
4296
|
+
let hidden = current.hidden.slice()
|
|
4297
|
+
for (const op of ops) {
|
|
4298
|
+
const id = railIdOfField(String(op.path[0]))
|
|
4299
|
+
if (id === null) return { sent: false, reason: 'unknown' }
|
|
4300
|
+
const wanted = op.value === true
|
|
4301
|
+
if (wanted === true) hidden = hidden.filter((entry) => entry !== id)
|
|
4302
|
+
else if (hidden.indexOf(id) < 0) hidden = hidden.concat([id])
|
|
4303
|
+
}
|
|
4304
|
+
try { window.localStorage.setItem(RAIL_KEY, JSON.stringify({ order: current.order, hidden: hidden })) } catch (error) { void error }
|
|
4305
|
+
notifyRailConfig()
|
|
4306
|
+
return { sent: true }
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
/* One switch's write. The card batches a burst through writeRailFields so the
|
|
4310
|
+
Host sees one atomic mutation instead of a stream of overlapping ones. */
|
|
4311
|
+
function writeRailField(field, value, form) {
|
|
4312
|
+
return writeRailFields([{ op: 'set', path: [field], value: value }], form)
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
/* One-shot move of the pre-settings rail config into the row Config: read the
|
|
4316
|
+
legacy value ONCE, write it through, mark it done. After this the row Config
|
|
4317
|
+
is the only source (the legacy key is left untouched as a paper trail, but
|
|
4318
|
+
nothing writes it any more on this host). */
|
|
4319
|
+
function migrateRailConfig(form) {
|
|
4320
|
+
try {
|
|
4321
|
+
if (window.localStorage.getItem(RAIL_MIGRATED_KEY) !== null) return
|
|
4322
|
+
if (railMigrating === true) return
|
|
4323
|
+
const done = () => {
|
|
4324
|
+
railMigrating = false
|
|
4325
|
+
try { window.localStorage.setItem(RAIL_MIGRATED_KEY, '1') } catch (error) { void error }
|
|
4326
|
+
}
|
|
4327
|
+
const raw = window.localStorage.getItem(RAIL_KEY)
|
|
4328
|
+
if (raw === null || raw === '') { done(); return }
|
|
4329
|
+
const values = railValuesOfConfig(normalizeRail(JSON.parse(raw)))
|
|
4330
|
+
const ready = railFormReady()
|
|
4331
|
+
const accepted = ready !== null && ready.value !== null && typeof ready.value === 'object' ? ready.value : {}
|
|
4332
|
+
const ops = []
|
|
4333
|
+
for (const id of RAIL_IDS) {
|
|
4334
|
+
const field = railFieldOf(id)
|
|
4335
|
+
if (accepted[field] === values[field]) continue
|
|
4336
|
+
ops.push({ op: 'set', path: [field], value: values[field] === true })
|
|
4337
|
+
}
|
|
4338
|
+
if (ops.length === 0) { done(); return }
|
|
4339
|
+
/* ONE atomic mutation, and the marker ONLY once the Host accepted it.
|
|
4340
|
+
The per-field set() loop this replaces fired sixteen unawaited writes
|
|
4341
|
+
and stamped the marker right away, so on a real profile fifteen of the
|
|
4342
|
+
sixteen fields (and in one case all of them) never reached the row
|
|
4343
|
+
Config and were never retried — an upgrade silently lost the user's
|
|
4344
|
+
rail settings while the marker claimed success. A refusal, a failed
|
|
4345
|
+
write or a superseded one now leaves the marker unwritten so the next
|
|
4346
|
+
load migrates again. */
|
|
4347
|
+
railMigrating = true
|
|
4348
|
+
const outcome = writeRailFields(ops, form)
|
|
4349
|
+
if (outcome.sent !== true) {
|
|
4350
|
+
railMigrating = false
|
|
4351
|
+
console.warn('[dsh-ide-git] legacy rail migration was refused (' + String(outcome.reason) + '); it stays unmarked and is retried')
|
|
4352
|
+
return
|
|
4353
|
+
}
|
|
4354
|
+
if (outcome.settled === undefined) { done(); return }
|
|
4355
|
+
void outcome.settled.then((acceptedWrite) => {
|
|
4356
|
+
if (acceptedWrite === true) { done(); return }
|
|
4357
|
+
railMigrating = false
|
|
4358
|
+
console.warn('[dsh-ide-git] legacy rail migration was not accepted; it stays unmarked and is retried')
|
|
4359
|
+
})
|
|
4360
|
+
} catch (error) { void error }
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
function subscribeRailConfig(listener) {
|
|
4364
|
+
railWatchers.add(listener)
|
|
4365
|
+
return () => { railWatchers.delete(listener) }
|
|
4366
|
+
}
|
|
4367
|
+
|
|
4368
|
+
function notifyRailConfig() {
|
|
4369
|
+
for (const listener of Array.from(railWatchers)) {
|
|
4370
|
+
try { listener() } catch (error) { void error }
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
|
|
3655
4374
|
/* ============================== pane sizes ============================== */
|
|
3656
4375
|
|
|
3657
4376
|
/* ---- pane sizing core (pure: tests/smoke.mjs slices this block and drives
|
|
@@ -4157,7 +4876,11 @@ window.__ModuleLoader__.load({
|
|
|
4157
4876
|
compare: ['M4 4h7', 'M4 12h7', 'M6.5 1.5 4 4l2.5 2.5', 'M9.5 9.5 12 12l-2.5 2.5'],
|
|
4158
4877
|
stash: ['M2.5 3.5h11V6h-11z', 'M3.5 6v6.5h9V6', 'M6.5 8.5h3'],
|
|
4159
4878
|
tag: ['M2.5 7.5 7.5 2.5h6v6l-5 5z', 'M10.5 5.5h.01'],
|
|
4160
|
-
|
|
4879
|
+
// A real gear: the toothed contour plus the centre hole, borrowed from the
|
|
4880
|
+
// official product icon set (ui-primitives IconSettingsOutlineMedium, same
|
|
4881
|
+
// 16x16 grid and currentColor stroke) because the previous entry was a
|
|
4882
|
+
// centre dot with eight rays — a sunburst, not a settings control.
|
|
4883
|
+
settings: ['M13.0107 7.79377C12.9505 7.89401 12.9205 7.94413 12.9205 7.99951C12.9205 8.0549 12.9505 8.10502 13.0106 8.20528L13.9849 9.83006C14.045 9.93029 14.0751 9.9804 14.0751 10.0358C14.0751 10.0911 14.045 10.1413 13.9849 10.2415L13.0037 11.8777C12.9468 11.9726 12.9184 12.0201 12.8725 12.0461C12.8267 12.072 12.7713 12.072 12.6607 12.072H10.6704C10.5598 12.072 10.5045 12.072 10.4586 12.098C10.4128 12.1239 10.3843 12.1714 10.3274 12.2662L9.33825 13.9142C9.28133 14.009 9.25287 14.0564 9.20703 14.0823C9.16118 14.1083 9.10588 14.1083 8.99529 14.1083H7.00486C6.89426 14.1083 6.83896 14.1083 6.79312 14.0823C6.74727 14.0564 6.71881 14.009 6.6619 13.9142L5.67273 12.2662C5.61581 12.1714 5.58735 12.1239 5.54151 12.098C5.49566 12.072 5.44036 12.072 5.32977 12.072H3.33945C3.2288 12.072 3.17347 12.072 3.12761 12.0461C3.08176 12.0201 3.0533 11.9726 2.9964 11.8777L2.0152 10.2415C1.9551 10.1413 1.92505 10.0911 1.92505 10.0358C1.92505 9.9804 1.9551 9.93029 2.0152 9.83006L2.98951 8.20528C3.04963 8.10502 3.07969 8.0549 3.07969 7.99951C3.07968 7.94413 3.04961 7.89401 2.98946 7.79377L2.01529 6.17011C1.95514 6.06987 1.92507 6.01975 1.92507 5.96437C1.92506 5.90899 1.95512 5.85886 2.01524 5.7586L2.9964 4.1224C3.0533 4.0275 3.08176 3.98005 3.12761 3.95408C3.17347 3.92811 3.2288 3.92811 3.33945 3.92811H5.32977C5.44036 3.92811 5.49566 3.92811 5.54151 3.90216C5.58735 3.87621 5.61581 3.82879 5.67273 3.73397L6.6619 2.08599C6.71881 1.99116 6.74727 1.94375 6.79312 1.9178C6.83896 1.89185 6.89426 1.89185 7.00486 1.89185H8.99529C9.10588 1.89185 9.16118 1.89185 9.20703 1.9178C9.25287 1.94375 9.28133 1.99116 9.33825 2.08599L10.3274 3.73397C10.3843 3.82879 10.4128 3.87621 10.4586 3.90216C10.5045 3.92811 10.5598 3.92811 10.6704 3.92811H12.6607C12.7713 3.92811 12.8267 3.92811 12.8725 3.95408C12.9184 3.98005 12.9468 4.0275 13.0037 4.1224L13.9849 5.7586C14.045 5.85886 14.0751 5.90899 14.0751 5.96437C14.0751 6.01975 14.045 6.06987 13.9849 6.17011L13.0107 7.79377Z', 'M8 9.75012C8.9665 9.75012 9.75 8.96662 9.75 8.00012C9.75 7.03362 8.9665 6.25012 8 6.25012C7.0335 6.25012 6.25 7.03362 6.25 8.00012C6.25 8.96662 7.0335 9.75012 8 9.75012Z'],
|
|
4161
4884
|
more: ['M3.6 8h0.01', 'M8 8h0.01', 'M12.4 8h0.01'],
|
|
4162
4885
|
eye: ['M1.5 8S4.1 3.8 8 3.8 14.5 8 14.5 8 11.9 12.2 8 12.2 1.5 8 1.5 8Z', 'M8 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z'],
|
|
4163
4886
|
eyeOff: ['M1.6 8S4.2 4 8 4c1 0 1.9.2 2.7.6', 'M13 5.6c.9 1.1 1.5 2.4 1.5 2.4S11.9 12 8 12c-.9 0-1.7-.2-2.4-.5', 'M2.6 2.6l10.8 10.8'],
|
|
@@ -4263,6 +4986,288 @@ window.__ModuleLoader__.load({
|
|
|
4263
4986
|
list is drag-sortable and arrow-sortable (the arrows also work with a
|
|
4264
4987
|
keyboard); visibility is a per-action toggle, not a delete, so a hidden
|
|
4265
4988
|
action keeps its position. */
|
|
4989
|
+
/* The plugin's settings card. It is the SAME surface on the plugin's detail
|
|
4990
|
+
page (slot `plugins.bundle.config`, keyed by the PACKAGE name) and in
|
|
4991
|
+
Settings → Plugins (slot `settings.plugin.item`, keyed by the settings
|
|
4992
|
+
namespace = the loader row id), and it is the only editor when the host
|
|
4993
|
+
serves the row Config — the in-panel editor is not reachable there.
|
|
4994
|
+
|
|
4995
|
+
A host without that service still renders the card (the seat exists), so it
|
|
4996
|
+
says so instead of pretending to have settings. */
|
|
4997
|
+
function RailSettingsCard(props) {
|
|
4998
|
+
const t = props.t
|
|
4999
|
+
const [tick, setTick] = useState(0)
|
|
5000
|
+
/* Collapse state of the card shell (the li card on the Settings → Plugins
|
|
5001
|
+
seat). The plugin-page seat (props.view === 'page') renders flat. */
|
|
5002
|
+
const [openCard, setOpenCard] = useState(false)
|
|
5003
|
+
const [pending, setPending] = useState({})
|
|
5004
|
+
const [note, setNote] = useState('')
|
|
5005
|
+
/* The three AI-commit settings are free text, so they are drafted locally
|
|
5006
|
+
and written on blur / Enter — one write per edit, never one per keystroke. */
|
|
5007
|
+
const [textDraft, setTextDraft] = useState({})
|
|
5008
|
+
/* The user's latest intent per field, written SYNCHRONOUSLY. React state is
|
|
5009
|
+
not enough: two clicks inside one frame would both read the pre-click
|
|
5010
|
+
snapshot, so the second click recomputed the same target and the toggle
|
|
5011
|
+
was lost (measured: a fast double click on one switch netted one step
|
|
5012
|
+
instead of returning to where it started). */
|
|
5013
|
+
const intent = useRef({})
|
|
5014
|
+
const timers = useRef(new Set())
|
|
5015
|
+
const dirty = useRef(new Map())
|
|
5016
|
+
const pendingFlush = useRef(null)
|
|
5017
|
+
const bump = () => setTick((value) => value + 1)
|
|
5018
|
+
/* Writes the Host answers ok for can still be lost when several overlap its
|
|
5019
|
+
persist cycle: measured on this surface, 16 switches toggled 55ms apart
|
|
5020
|
+
left 3 fields at their old value in the row Config while every request
|
|
5021
|
+
answered 200, and the same 16 at 500ms apart all landed. So a switch is
|
|
5022
|
+
only "done" when the accepted document agrees — checked once after the
|
|
5023
|
+
burst settles, re-issued once, and otherwise rolled back and reported. */
|
|
5024
|
+
const reconcile = (field, want, attempt) => {
|
|
5025
|
+
const timer = setTimeout(() => {
|
|
5026
|
+
timers.current.delete(timer)
|
|
5027
|
+
let hostValue
|
|
5028
|
+
try {
|
|
5029
|
+
const snapshot = scope === null || scope === undefined ? null : scope.getSnapshot()
|
|
5030
|
+
hostValue = snapshot !== null && snapshot !== undefined && snapshot.value !== null && typeof snapshot.value === 'object' ? snapshot.value[field] : undefined
|
|
5031
|
+
} catch (error) { hostValue = undefined }
|
|
5032
|
+
if (hostValue === want) return
|
|
5033
|
+
if (attempt === 0) {
|
|
5034
|
+
const again = writeRailField(field, want, scope)
|
|
5035
|
+
if (again.sent === true) {
|
|
5036
|
+
if (again.settled !== undefined) void again.settled.then(() => { reconcile(field, want, 1) })
|
|
5037
|
+
else reconcile(field, want, 1)
|
|
5038
|
+
return
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
5041
|
+
delete intent.current[field]
|
|
5042
|
+
setPending((now) => { const copy = Object.assign({}, now); delete copy[field]; return copy })
|
|
5043
|
+
setNote(t('settings.rail.writeFailed'))
|
|
5044
|
+
}, 600)
|
|
5045
|
+
timers.current.add(timer)
|
|
5046
|
+
}
|
|
5047
|
+
const rollbackFields = (fields) => {
|
|
5048
|
+
for (const field of fields) {
|
|
5049
|
+
delete intent.current[field]
|
|
5050
|
+
setPending((now) => { const copy = Object.assign({}, now); delete copy[field]; return copy })
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
/* One atomic mutation for the whole burst. Sixteen separate writes 55ms
|
|
5054
|
+
apart lost 3 fields inside the Host's persist cycle while every request
|
|
5055
|
+
answered 200 (the same sixteen 500ms apart all landed), so the fix is to
|
|
5056
|
+
stop producing a burst: collect the fields the user touched and submit
|
|
5057
|
+
them together, at most one mutation per window. */
|
|
5058
|
+
const commitTextField = (field, max) => {
|
|
5059
|
+
const accepted = values[field] === undefined || values[field] === null ? '' : String(values[field])
|
|
5060
|
+
const shown = Object.prototype.hasOwnProperty.call(textDraft, field) ? textDraft[field] : accepted
|
|
5061
|
+
const wanted = shown.slice(0, max)
|
|
5062
|
+
if (wanted === accepted) return
|
|
5063
|
+
const outcome = writeRailFields([{ op: 'set', path: [field], value: wanted }], scope)
|
|
5064
|
+
if (outcome.sent !== true) {
|
|
5065
|
+
setNote(t(outcome.reason === 'read-only' ? 'settings.rail.readonly' : 'settings.rail.writeFailed'))
|
|
5066
|
+
return
|
|
5067
|
+
}
|
|
5068
|
+
if (outcome.settled === undefined) return
|
|
5069
|
+
void outcome.settled.then((ok) => {
|
|
5070
|
+
if (ok !== true) { setNote(t('settings.rail.writeFailed')); return }
|
|
5071
|
+
/* The accepted document now carries it: drop the draft so the control
|
|
5072
|
+
shows the Host's own value again. */
|
|
5073
|
+
setTextDraft((now) => { const copy = Object.assign({}, now); delete copy[field]; return copy })
|
|
5074
|
+
})
|
|
5075
|
+
}
|
|
5076
|
+
const textRow = (field, labelKey, hintKey, max, multiline) => {
|
|
5077
|
+
const accepted = values[field] === undefined || values[field] === null ? '' : String(values[field])
|
|
5078
|
+
const shown = Object.prototype.hasOwnProperty.call(textDraft, field) ? textDraft[field] : accepted
|
|
5079
|
+
const id = 'dig-' + field
|
|
5080
|
+
const common = {
|
|
5081
|
+
id: id, className: 'dig-input dig-settings-input', value: shown, spellCheck: false,
|
|
5082
|
+
disabled: readOnly === true, placeholder: t(hintKey), 'aria-labelledby': id + '-label',
|
|
5083
|
+
onChange: (event) => setTextDraft((now) => Object.assign({}, now, { [field]: event.target.value })),
|
|
5084
|
+
onBlur: () => commitTextField(field, max),
|
|
5085
|
+
}
|
|
5086
|
+
if (multiline === true) common.onKeyDown = (event) => { if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) commitTextField(field, max) }
|
|
5087
|
+
else common.onKeyDown = (event) => { if (event.key === 'Enter') commitTextField(field, max) }
|
|
5088
|
+
return E('div', { className: 'dig-settings-text', key: field },
|
|
5089
|
+
E('label', { id: id + '-label', className: 'dig-settings-text-label', htmlFor: id }, t(labelKey)),
|
|
5090
|
+
multiline === true ? E('textarea', Object.assign({ rows: 3 }, common)) : E('input', Object.assign({ type: 'text' }, common)),
|
|
5091
|
+
shown.length >= max ? E('div', { className: 'dig-settings-error' }, fill(t('settings.commit.tooLong'), { n: String(max) })) : null)
|
|
5092
|
+
}
|
|
5093
|
+
const flush = () => {
|
|
5094
|
+
pendingFlush.current = null
|
|
5095
|
+
/* The pending TARGET per field, not "whatever the render-time intent
|
|
5096
|
+
happens to hold now": the accepted document can already agree with the
|
|
5097
|
+
user's new value, which clears the optimistic intent before this flush
|
|
5098
|
+
runs — reading it here would then submit the opposite of the click and
|
|
5099
|
+
write the switch back to the wrong state. */
|
|
5100
|
+
const targets = new Map(dirty.current)
|
|
5101
|
+
dirty.current.clear()
|
|
5102
|
+
const fields = Array.from(targets.keys())
|
|
5103
|
+
if (fields.length === 0) return
|
|
5104
|
+
const ops = fields.map((field) => ({ op: 'set', path: [field], value: targets.get(field) === true }))
|
|
5105
|
+
const outcome = writeRailFields(ops, scope)
|
|
5106
|
+
if (outcome.sent !== true) {
|
|
5107
|
+
rollbackFields(fields)
|
|
5108
|
+
setNote(t(outcome.reason === 'read-only' ? 'settings.rail.readonly' : 'settings.rail.writeFailed'))
|
|
5109
|
+
return
|
|
5110
|
+
}
|
|
5111
|
+
if (outcome.settled === undefined) return
|
|
5112
|
+
void outcome.settled.then((accepted) => {
|
|
5113
|
+
if (accepted !== true) {
|
|
5114
|
+
rollbackFields(fields)
|
|
5115
|
+
setNote(t('settings.rail.writeFailed'))
|
|
5116
|
+
return
|
|
5117
|
+
}
|
|
5118
|
+
for (const field of fields) reconcile(field, targets.get(field) === true, 0)
|
|
5119
|
+
})
|
|
5120
|
+
}
|
|
5121
|
+
const scheduleFlush = () => {
|
|
5122
|
+
if (pendingFlush.current !== null) return
|
|
5123
|
+
pendingFlush.current = setTimeout(() => { pendingFlush.current = null; flush() }, FLUSH_MS)
|
|
5124
|
+
timers.current.add(pendingFlush.current)
|
|
5125
|
+
}
|
|
5126
|
+
/* Two handles, one document: the slot hands the card the framework's own
|
|
5127
|
+
form for this entry (`props.scope`), and our ctx.configForms handle is the
|
|
5128
|
+
fallback — both address the same namespace, so a write through either is
|
|
5129
|
+
the same row Config write. */
|
|
5130
|
+
const scope = props.scope !== undefined && props.scope !== null && typeof props.scope.getSnapshot === 'function'
|
|
5131
|
+
? props.scope
|
|
5132
|
+
: railForm
|
|
5133
|
+
useEffect(() => {
|
|
5134
|
+
const offRail = subscribeRailConfig(bump)
|
|
5135
|
+
let offScope
|
|
5136
|
+
if (scope !== null && scope !== undefined && typeof scope.subscribe === 'function') {
|
|
5137
|
+
const unsubscribe = scope.subscribe(bump)
|
|
5138
|
+
if (typeof unsubscribe === 'function') offScope = unsubscribe
|
|
5139
|
+
}
|
|
5140
|
+
return () => {
|
|
5141
|
+
offRail()
|
|
5142
|
+
if (offScope !== undefined) offScope()
|
|
5143
|
+
for (const timer of Array.from(timers.current)) clearTimeout(timer)
|
|
5144
|
+
timers.current.clear()
|
|
5145
|
+
}
|
|
5146
|
+
}, [])
|
|
5147
|
+
void tick
|
|
5148
|
+
/* Card shell, shared by both seats — a visual twin of the pc-card /
|
|
5149
|
+
dl-card / gb-card pattern in dsh-ptc-cordis-preset, dsh-agent-lang and
|
|
5150
|
+
dsh-gitbash-shell: a collapsible li card on the Settings → Plugins
|
|
5151
|
+
seat, a flat page card on the plugin-page seat. Title + description
|
|
5152
|
+
come from the settings.card.* dictionary keys (every locale). */
|
|
5153
|
+
const pageView = props.view === 'page'
|
|
5154
|
+
const cardHead = E('span', { className: 'dig-card-head-text' },
|
|
5155
|
+
E('span', { className: 'dig-card-name' }, t('settings.card.title')),
|
|
5156
|
+
E('span', { className: 'dig-card-desc' }, t('settings.card.desc')))
|
|
5157
|
+
const wrapCard = (body) => {
|
|
5158
|
+
if (pageView) return E('div', { className: 'dig-card dig-card-page' },
|
|
5159
|
+
E('div', { className: 'dig-card-header dig-card-header-flat' }, cardHead), body)
|
|
5160
|
+
return E('li', { className: 'dig-card' + (openCard ? ' dig-card-open' : '') },
|
|
5161
|
+
E('button', {
|
|
5162
|
+
type: 'button',
|
|
5163
|
+
className: 'dig-card-header',
|
|
5164
|
+
'aria-expanded': openCard,
|
|
5165
|
+
onClick: () => setOpenCard(!openCard),
|
|
5166
|
+
}, cardHead,
|
|
5167
|
+
E('span', { className: 'dig-card-chevron' + (openCard ? ' dig-card-chevron-open' : '') },
|
|
5168
|
+
E(Icon, { name: 'chevron', size: 12 }))),
|
|
5169
|
+
openCard ? body : null)
|
|
5170
|
+
}
|
|
5171
|
+
let snapshot = null
|
|
5172
|
+
try { snapshot = scope === null || scope === undefined ? null : scope.getSnapshot() } catch (error) { void error }
|
|
5173
|
+
const status = snapshot === null || snapshot === undefined || typeof snapshot.status !== 'string' ? 'absent' : snapshot.status
|
|
5174
|
+
const values = snapshot !== null && snapshot !== undefined && snapshot.value !== null && typeof snapshot.value === 'object' ? snapshot.value : null
|
|
5175
|
+
// Drop optimistic values the host has since confirmed (post-commit effect).
|
|
5176
|
+
useEffect(() => {
|
|
5177
|
+
setPending((current) => {
|
|
5178
|
+
const keys = Object.keys(current)
|
|
5179
|
+
if (keys.length === 0 || values === null) return current
|
|
5180
|
+
const next = {}
|
|
5181
|
+
let changed = false
|
|
5182
|
+
for (const key of keys) {
|
|
5183
|
+
if (values[key] === current[key]) {
|
|
5184
|
+
changed = true
|
|
5185
|
+
/* The accepted document agrees: this intent is no longer pending.
|
|
5186
|
+
Deleting twice is harmless, so a re-invoked updater stays safe. */
|
|
5187
|
+
delete intent.current[key]
|
|
5188
|
+
continue
|
|
5189
|
+
}
|
|
5190
|
+
next[key] = current[key]
|
|
5191
|
+
}
|
|
5192
|
+
return changed ? next : current
|
|
5193
|
+
})
|
|
5194
|
+
})
|
|
5195
|
+
// Switches exist only where they can actually write: a host that does not
|
|
5196
|
+
// serve this entry's settings gets a sentence instead of dead controls.
|
|
5197
|
+
if (status !== 'ready' || values === null) {
|
|
5198
|
+
return wrapCard(E('div', { className: 'dig-settings dig-card-body', 'data-settings-status': status },
|
|
5199
|
+
E('div', { className: 'dig-settings-note' }, t('settings.rail.unavailable'))))
|
|
5200
|
+
}
|
|
5201
|
+
const config = railConfigOfValues(values)
|
|
5202
|
+
/* A toggle is a host round trip, so the switch would snap back to the old
|
|
5203
|
+
value for a moment and read as "stuck". The optimistic value wins until
|
|
5204
|
+
the accepted document agrees with it (then it is dropped). */
|
|
5205
|
+
const shown = (spec) => {
|
|
5206
|
+
const field = railFieldOf(spec.id)
|
|
5207
|
+
if (Object.prototype.hasOwnProperty.call(intent.current, field)) return intent.current[field]
|
|
5208
|
+
return config.hidden.indexOf(spec.id) < 0
|
|
5209
|
+
}
|
|
5210
|
+
/* A host that serves the document but refuses writes gets disabled switches
|
|
5211
|
+
and a sentence, not controls that silently do nothing. */
|
|
5212
|
+
const readOnly = snapshot.writable !== true
|
|
5213
|
+
return wrapCard(E('div', { className: 'dig-settings dig-card-body', 'data-settings-status': status, 'data-settings-readonly': readOnly === true ? 'true' : 'false' },
|
|
5214
|
+
E('div', { className: 'dig-settings-hint' }, t('settings.rail.hint')),
|
|
5215
|
+
E('div', { className: readOnly === true ? 'dig-settings-note' : 'dig-settings-note dig-settings-note-hidden', 'data-settings-readonly-note': '' }, t('settings.rail.readonly')),
|
|
5216
|
+
note === '' ? null : E('div', { className: 'dig-settings-error', 'data-settings-error': '' }, note),
|
|
5217
|
+
E('div', { className: 'dig-settings-list' }, RAIL_SPECS.map((spec) => {
|
|
5218
|
+
const inputId = 'dig-rail-' + spec.id
|
|
5219
|
+
const visible = shown(spec)
|
|
5220
|
+
/* A row, not a <label>: a label that both carries htmlFor and wraps its
|
|
5221
|
+
own input toggles twice per click (once for the input, once for the
|
|
5222
|
+
label activation), which reads as a stuck switch and writes twice. */
|
|
5223
|
+
const toggle = (event) => {
|
|
5224
|
+
if (readOnly === true) { setNote(t('settings.rail.readonly')); return }
|
|
5225
|
+
const field = railFieldOf(spec.id)
|
|
5226
|
+
/* The control's own answer when the browser gives it to us: a click that
|
|
5227
|
+
lands while the previous one is still rendering would otherwise
|
|
5228
|
+
compose from the pre-click snapshot and be lost (measured: a fast
|
|
5229
|
+
double click netted one step instead of returning to its start). The
|
|
5230
|
+
row's label span is not a control, so it falls back to the
|
|
5231
|
+
synchronous intent — never to the render snapshot alone. */
|
|
5232
|
+
const fromControl = event !== undefined && event !== null && event.target !== null
|
|
5233
|
+
&& event.target !== undefined && typeof event.target.checked === 'boolean'
|
|
5234
|
+
? event.target.checked
|
|
5235
|
+
: null
|
|
5236
|
+
/* The control already holds the state the user just asked for, so it is
|
|
5237
|
+
the TARGET — inverting it here would write the opposite of the click.
|
|
5238
|
+
Without a control (the row's label span) the target is the inverse of
|
|
5239
|
+
the current intent, falling back to the accepted document. */
|
|
5240
|
+
const wanted = fromControl !== null
|
|
5241
|
+
? fromControl
|
|
5242
|
+
: (Object.prototype.hasOwnProperty.call(intent.current, field)
|
|
5243
|
+
? intent.current[field] !== true
|
|
5244
|
+
: config.hidden.indexOf(spec.id) < 0 !== true)
|
|
5245
|
+
intent.current[field] = wanted
|
|
5246
|
+
dirty.current.set(field, wanted)
|
|
5247
|
+
setPending((now) => Object.assign({}, now, { [field]: wanted }))
|
|
5248
|
+
setNote('')
|
|
5249
|
+
bump()
|
|
5250
|
+
scheduleFlush()
|
|
5251
|
+
}
|
|
5252
|
+
return E('div', { key: spec.id, className: 'dig-settings-row', 'data-rail-action': spec.id },
|
|
5253
|
+
E('input', {
|
|
5254
|
+
id: inputId,
|
|
5255
|
+
type: 'checkbox',
|
|
5256
|
+
className: 'dig-settings-check',
|
|
5257
|
+
'aria-labelledby': inputId + '-label',
|
|
5258
|
+
checked: visible,
|
|
5259
|
+
disabled: readOnly === true,
|
|
5260
|
+
onChange: toggle,
|
|
5261
|
+
}),
|
|
5262
|
+
E('span', { className: 'dig-settings-glyph' }, E(Icon, { name: spec.icon, size: 13 })),
|
|
5263
|
+
E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t(spec.key)))
|
|
5264
|
+
})),
|
|
5265
|
+
E('div', { className: 'dig-settings-sub' }, t('settings.commit.title')),
|
|
5266
|
+
textRow('commitModel', 'settings.commit.model', 'settings.commit.modelHint', 120, false),
|
|
5267
|
+
textRow('commitReasoning', 'settings.commit.reasoning', 'settings.commit.reasoningHint', 60, false),
|
|
5268
|
+
textRow('commitPrompt', 'settings.commit.prompt', 'settings.commit.promptHint', 2000, true)))
|
|
5269
|
+
}
|
|
5270
|
+
|
|
4266
5271
|
function RailSettings(props) {
|
|
4267
5272
|
const t = props.t
|
|
4268
5273
|
const [dragId, setDragId] = useState(null)
|
|
@@ -4691,6 +5696,12 @@ window.__ModuleLoader__.load({
|
|
|
4691
5696
|
const [message, setMessage] = useState('')
|
|
4692
5697
|
const [amend, setAmend] = useState(false)
|
|
4693
5698
|
const [collapsed, setCollapsed] = useState(false)
|
|
5699
|
+
/* AI-written message (issue #6). The draft is kept so the write can be
|
|
5700
|
+
undone, and an existing draft is never overwritten without a word. */
|
|
5701
|
+
const [aiBusy, setAiBusy] = useState(false)
|
|
5702
|
+
const [aiNote, setAiNote] = useState(null)
|
|
5703
|
+
const [aiAsk, setAiAsk] = useState(false)
|
|
5704
|
+
const [aiUndo, setAiUndo] = useState(null)
|
|
4694
5705
|
// 'flat' keeps git's own grouping (staged / changes / untracked); 'dir'
|
|
4695
5706
|
// clusters each group's files under their folder, the way "Group by:
|
|
4696
5707
|
// Directory" does in an IDE. Folders are addressed by <group>|<dir>, so a
|
|
@@ -4704,6 +5715,32 @@ window.__ModuleLoader__.load({
|
|
|
4704
5715
|
const untracked = changes === null ? [] : changes.untracked
|
|
4705
5716
|
const ignored = changes === null || changes.ignored === undefined ? [] : changes.ignored
|
|
4706
5717
|
const total = conflicted.length + staged.length + unstaged.length + untracked.length
|
|
5718
|
+
const aiRun = async () => {
|
|
5719
|
+
setAiAsk(false)
|
|
5720
|
+
setAiBusy(true)
|
|
5721
|
+
setAiNote(null)
|
|
5722
|
+
try {
|
|
5723
|
+
const settings = props.commitSettings === undefined ? { model: '', reasoning: '', prompt: '' } : props.commitSettings
|
|
5724
|
+
const data = await props.request('commit-message', {
|
|
5725
|
+
cwd: props.cwd,
|
|
5726
|
+
sessionId: props.sessionId,
|
|
5727
|
+
model: settings.model,
|
|
5728
|
+
reasoningEffort: settings.reasoning,
|
|
5729
|
+
prompt: settings.prompt,
|
|
5730
|
+
})
|
|
5731
|
+
setAiUndo(message === '' ? null : message)
|
|
5732
|
+
setMessage(typeof data.message === 'string' ? data.message : '')
|
|
5733
|
+
/* Say what the model did NOT see, instead of quietly sending less. */
|
|
5734
|
+
const input = data.input === undefined ? {} : data.input
|
|
5735
|
+
const incomplete = input.truncated === true || (typeof input.dropped === 'number' && input.dropped > 0) || data.promptTruncated === true
|
|
5736
|
+
setAiNote(incomplete === true ? t('changes.aiNotice') : null)
|
|
5737
|
+
} catch (error) {
|
|
5738
|
+
const code = error !== null && error !== undefined && typeof error.code === 'string' ? error.code : 'unknown'
|
|
5739
|
+
setAiNote(t('changes.aiFailed') + ' [' + code + '] ' + String(error && error.message ? error.message : error))
|
|
5740
|
+
} finally {
|
|
5741
|
+
setAiBusy(false)
|
|
5742
|
+
}
|
|
5743
|
+
}
|
|
4707
5744
|
const submit = (push) => {
|
|
4708
5745
|
if (message.trim() === '' || props.busy === true) return false
|
|
4709
5746
|
props.onCommit(message, amend, push === true)
|
|
@@ -4756,6 +5793,15 @@ window.__ModuleLoader__.load({
|
|
|
4756
5793
|
key === 'staged' ? E('button', { type: 'button', className: 'dig-link', onClick: () => props.onUnstageAll() }, t('changes.unstageAll')) : null,
|
|
4757
5794
|
key === 'unstaged' || key === 'untracked' ? E('button', { type: 'button', className: 'dig-link', onClick: () => props.onStageAll(entries) }, t('changes.stageAll')) : null),
|
|
4758
5795
|
rowsOf(key, entries))
|
|
5796
|
+
const aiConfirmDialog = aiAsk !== true ? null : E(ConfirmDialog, {
|
|
5797
|
+
t: t,
|
|
5798
|
+
title: t('changes.aiOverwriteTitle'),
|
|
5799
|
+
text: t('changes.aiOverwriteText'),
|
|
5800
|
+
okLabel: t('confirm.ok'),
|
|
5801
|
+
cancelLabel: t('confirm.cancel'),
|
|
5802
|
+
onConfirm: () => { void aiRun() },
|
|
5803
|
+
onCancel: () => setAiAsk(false),
|
|
5804
|
+
})
|
|
4759
5805
|
const composer = props.compact === true
|
|
4760
5806
|
? E('div', { className: 'dig-commit-box dig-commit-box-compact' },
|
|
4761
5807
|
E('input', {
|
|
@@ -4766,11 +5812,18 @@ window.__ModuleLoader__.load({
|
|
|
4766
5812
|
if (event.key === 'Enter') { event.preventDefault(); submit() }
|
|
4767
5813
|
},
|
|
4768
5814
|
}),
|
|
5815
|
+
E('button', {
|
|
5816
|
+
type: 'button', className: 'dig-btn dig-btn-small dig-btn-ai',
|
|
5817
|
+
disabled: aiBusy === true || props.busy === true,
|
|
5818
|
+
title: t('changes.aiQuota'),
|
|
5819
|
+
onClick: () => { if (message.trim() === '') void aiRun(); else setAiAsk(true) },
|
|
5820
|
+
}, aiBusy === true ? t('changes.aiBusy') : t('changes.aiWrite')),
|
|
4769
5821
|
E('button', {
|
|
4770
5822
|
type: 'button', className: 'dig-btn dig-btn-primary dig-btn-small',
|
|
4771
5823
|
disabled: message.trim() === '' || props.busy === true,
|
|
4772
5824
|
onClick: () => { submit(false) },
|
|
4773
|
-
}, t('changes.commit'))
|
|
5825
|
+
}, t('changes.commit')),
|
|
5826
|
+
E('span', { className: 'dig-commit-ai-hint dig-commit-ai-hint-block' }, t('changes.aiQuota')))
|
|
4774
5827
|
: E('div', { className: 'dig-commit-box' },
|
|
4775
5828
|
E('textarea', {
|
|
4776
5829
|
className: 'dig-textarea', value: message, spellCheck: false,
|
|
@@ -4780,6 +5833,18 @@ window.__ModuleLoader__.load({
|
|
|
4780
5833
|
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) { event.preventDefault(); submit() }
|
|
4781
5834
|
},
|
|
4782
5835
|
}),
|
|
5836
|
+
E('div', { className: 'dig-commit-ai' },
|
|
5837
|
+
E('button', {
|
|
5838
|
+
type: 'button', className: 'dig-btn dig-btn-small dig-btn-ai',
|
|
5839
|
+
disabled: aiBusy === true || props.busy === true,
|
|
5840
|
+
onClick: () => { if (message.trim() === '') void aiRun(); else setAiAsk(true) },
|
|
5841
|
+
}, aiBusy === true ? t('changes.aiBusy') : t('changes.aiWrite')),
|
|
5842
|
+
E('span', { className: 'dig-commit-ai-hint' }, t('changes.aiQuota')),
|
|
5843
|
+
aiUndo === null ? null : E('button', {
|
|
5844
|
+
type: 'button', className: 'dig-link',
|
|
5845
|
+
onClick: () => { setMessage(aiUndo); setAiUndo(null); setAiNote(null) },
|
|
5846
|
+
}, t('changes.aiUndo'))),
|
|
5847
|
+
aiNote === null ? null : E('div', { className: 'dig-commit-ai-note', 'data-ai-note': '' }, aiNote),
|
|
4783
5848
|
E('div', { className: 'dig-commit-actions' },
|
|
4784
5849
|
E('label', { className: 'dig-check' },
|
|
4785
5850
|
E('input', { type: 'checkbox', checked: amend, onChange: (event) => setAmend(event.target.checked) }),
|
|
@@ -4829,6 +5894,7 @@ window.__ModuleLoader__.load({
|
|
|
4829
5894
|
onClick: () => props.onToggleIgnored(),
|
|
4830
5895
|
}, E(Icon, { name: props.showIgnored === true ? 'eye' : 'eyeOff', size: 13 })))
|
|
4831
5896
|
return E('div', { className: 'dig-changes' },
|
|
5897
|
+
aiConfirmDialog,
|
|
4832
5898
|
head,
|
|
4833
5899
|
E('div', { className: 'dig-changes-body' },
|
|
4834
5900
|
E('div', { className: 'dig-changes-list' },
|
|
@@ -5100,6 +6166,7 @@ window.__ModuleLoader__.load({
|
|
|
5100
6166
|
const [favTick, setFavTick] = useState(0)
|
|
5101
6167
|
const [pathFilter, setPathFilter] = useState('')
|
|
5102
6168
|
const [railConfig, setRailConfig] = useState(readRailConfig)
|
|
6169
|
+
const [railTick, setRailTick] = useState(0)
|
|
5103
6170
|
const [railSettings, setRailSettings] = useState(false)
|
|
5104
6171
|
const [railBox, setRailBox] = useState({ width: 0, height: 0 })
|
|
5105
6172
|
const [toasts, setToasts] = useState([])
|
|
@@ -5135,6 +6202,15 @@ window.__ModuleLoader__.load({
|
|
|
5135
6202
|
// its own — v0.1.3 already made that mistake with a height < 330 test (a
|
|
5136
6203
|
// 1500x300 workbench came out single-column) and v0.3.5 still let
|
|
5137
6204
|
// height < 200 flip a 1320x180 workbench into the right-sidebar chrome.
|
|
6205
|
+
/* Where the gear goes: the plugin's own settings page when this host has
|
|
6206
|
+
both the row Config and the navigation to it, the in-panel editor
|
|
6207
|
+
otherwise (see RAIL_NS / railForm). */
|
|
6208
|
+
const railSettingsTarget = () => (railForm === null || props.openSettings === null || props.openSettings === undefined ? 'panel' : 'page')
|
|
6209
|
+
const openSettingsPage = () => {
|
|
6210
|
+
if (props.openSettings === null || props.openSettings === undefined) return
|
|
6211
|
+
props.openSettings()
|
|
6212
|
+
}
|
|
6213
|
+
|
|
5138
6214
|
const compact = size.width > 0 && size.width < COMPACT_MAX_WIDTH
|
|
5139
6215
|
const columns = !compact && size.width >= 600 && size.width >= size.height * 1.15
|
|
5140
6216
|
|
|
@@ -5692,8 +6768,10 @@ window.__ModuleLoader__.load({
|
|
|
5692
6768
|
E('span', { className: 'dig-banner-text' }, note),
|
|
5693
6769
|
E('button', { type: 'button', className: 'dig-link', onClick: () => setNote(null) }, t('error.dismiss')))
|
|
5694
6770
|
|
|
6771
|
+
const commitSettings = readCommitSettings()
|
|
5695
6772
|
const changesPane = E(ChangesPanel, {
|
|
5696
6773
|
t: t, summary: summary, busy: busy, compact: compact, hideHeader: compact,
|
|
6774
|
+
request: request, cwd: cwd, sessionId: sessionId, commitSettings: commitSettings,
|
|
5697
6775
|
showIgnored: showIgnored,
|
|
5698
6776
|
onToggleIgnored: () => setShowIgnored((value) => !value),
|
|
5699
6777
|
// Commit-and-push carries the confirm flag with it: the button already
|
|
@@ -5848,6 +6926,14 @@ window.__ModuleLoader__.load({
|
|
|
5848
6926
|
.filter((action) => action !== undefined && action.available !== false)
|
|
5849
6927
|
const railVisible = railOrdered.filter((action) => railConfig.hidden.indexOf(action.id) < 0)
|
|
5850
6928
|
|
|
6929
|
+
/* The rail is drawn from the row Config when the host serves it: a change
|
|
6930
|
+
made in the settings card must show up here without a reload. */
|
|
6931
|
+
useEffect(() => subscribeRailConfig(() => {
|
|
6932
|
+
setRailConfig(readRailConfig())
|
|
6933
|
+
setRailTick((value) => value + 1)
|
|
6934
|
+
}), [])
|
|
6935
|
+
void railTick
|
|
6936
|
+
|
|
5851
6937
|
const applyRail = (next) => { setRailConfig(next); writeRailConfig(next) }
|
|
5852
6938
|
const moveRailAction = (id, delta) => {
|
|
5853
6939
|
const order = railConfig.order.slice()
|
|
@@ -5926,8 +7012,16 @@ window.__ModuleLoader__.load({
|
|
|
5926
7012
|
key: 'settings',
|
|
5927
7013
|
type: 'button',
|
|
5928
7014
|
className: 'dig-rail-btn dig-rail-settings',
|
|
5929
|
-
|
|
5930
|
-
|
|
7015
|
+
'data-action': 'settings',
|
|
7016
|
+
// On a host that serves the row Config this button opens the plugin's own
|
|
7017
|
+
// settings page (where the card below the description edits the rail);
|
|
7018
|
+
// on an older host it keeps the in-panel editor. Never both.
|
|
7019
|
+
title: railSettingsTarget() === 'page' ? t('rail.openSettings') : t('rail.settings'),
|
|
7020
|
+
onClick: () => {
|
|
7021
|
+
const target = railSettingsTarget()
|
|
7022
|
+
if (target === 'page') { openSettingsPage(); return }
|
|
7023
|
+
setRailSettings(true)
|
|
7024
|
+
},
|
|
5931
7025
|
}, E(Icon, { name: 'settings', size: 15 })))
|
|
5932
7026
|
return E('div', { className: vertical ? 'dig-rail' : 'dig-rail-row', ref: railHostRef }, children)
|
|
5933
7027
|
}
|
|
@@ -6189,6 +7283,45 @@ window.__ModuleLoader__.load({
|
|
|
6189
7283
|
// A panel dragged narrower than COMPACT_MAX_WIDTH (the compact chrome) is the
|
|
6190
7284
|
// case where 'nowrap + ellipsis' stops being readable: the overlays clamp
|
|
6191
7285
|
// their own width, and their text wraps inside that box instead of clipping.
|
|
7286
|
+
// The settings card (the host's plugin page renders it inside its own
|
|
7287
|
+
// section): one row per rail action, themed entirely by tokens.
|
|
7288
|
+
// The card shell below is the visual twin of the pc-card / dl-card /
|
|
7289
|
+
// gb-card pattern the sibling plugins draw.
|
|
7290
|
+
'.dig-card{list-style:none;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-bg-layer-3);transition:border-color .16s,background .16s}',
|
|
7291
|
+
'.dig-card:hover{border-color:var(--dsw-alias-label-dimmed)}',
|
|
7292
|
+
'.dig-card.dig-card-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}',
|
|
7293
|
+
'.dig-card-header{width:100%;appearance:none;border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px 16px;border-radius:var(--dsw-radius-md,12px)}',
|
|
7294
|
+
'.dig-card-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}',
|
|
7295
|
+
'.dig-card-header-flat{cursor:default}',
|
|
7296
|
+
'.dig-card-head-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}',
|
|
7297
|
+
'.dig-card-name{font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}',
|
|
7298
|
+
'.dig-card-desc{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}',
|
|
7299
|
+
'.dig-card-chevron{flex:none;display:inline-flex;color:var(--dsw-alias-label-tertiary);transition:transform .16s}',
|
|
7300
|
+
'.dig-card-chevron.dig-card-chevron-open{transform:rotate(180deg)}',
|
|
7301
|
+
'.dig-card-body{padding:8px 16px 16px}',
|
|
7302
|
+
'.dig-card-page{max-width:640px}',
|
|
7303
|
+
'.dig-settings{display:flex;flex-direction:column;gap:8px;min-width:0}',
|
|
7304
|
+
'.dig-settings-head{display:flex;flex-direction:column;gap:2px}',
|
|
7305
|
+
'.dig-settings-title{font-weight:600;color:var(--dsw-alias-label-primary)}',
|
|
7306
|
+
'.dig-settings-hint{color:var(--dsw-alias-label-tertiary);font-weight:400}',
|
|
7307
|
+
'.dig-settings-note{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary));font-weight:400}',
|
|
7308
|
+
'.dig-settings-note-hidden{display:none}',
|
|
7309
|
+
'.dig-settings-error{color:var(--dsw-alias-state-error-primary);font-weight:400}',
|
|
7310
|
+
'.dig-settings-sub{margin-top:6px;font-weight:600;color:var(--dsw-alias-label-primary)}',
|
|
7311
|
+
'.dig-settings-text{display:flex;flex-direction:column;gap:3px;min-width:0}',
|
|
7312
|
+
'.dig-settings-text-label{color:var(--dsw-alias-label-secondary);font-weight:400}',
|
|
7313
|
+
'.dig-settings-input{width:100%;box-sizing:border-box;font:inherit}',
|
|
7314
|
+
'.dig-commit-ai{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:4px}',
|
|
7315
|
+
'.dig-commit-ai-hint{color:var(--dsw-alias-label-tertiary);font-size:11px}',
|
|
7316
|
+
'.dig-commit-ai-hint-block{flex:1 0 100%;font-size:11px}',
|
|
7317
|
+
'.dig-commit-ai-note{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary));font-size:11px;margin-bottom:4px;word-break:break-word}',
|
|
7318
|
+
'.dig-settings-list{display:flex;flex-direction:column;gap:2px;max-height:min(46vh,360px);overflow:auto;padding:2px;border:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1));border-radius:var(--dsw-radius-sm,8px)}',
|
|
7319
|
+
'.dig-settings-row{display:flex;align-items:center;gap:8px;padding:3px 6px;border-radius:calc(var(--dsw-radius-sm,8px) - 2px);cursor:pointer;min-width:0}',
|
|
7320
|
+
'.dig-settings-row:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
7321
|
+
'.dig-settings-check{flex:none;accent-color:var(--dsw-alias-brand-primary)}',
|
|
7322
|
+
'.dig-settings-glyph{display:inline-flex;flex:none;color:var(--dsw-alias-label-secondary)}',
|
|
7323
|
+
'.dig-settings-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}',
|
|
7324
|
+
'.dig-root-narrow .dig-settings-label{white-space:normal}',
|
|
6192
7325
|
'.dig-root-narrow .dig-menu-item{white-space:normal}',
|
|
6193
7326
|
'.dig-root-narrow .dig-toast-text{white-space:normal}',
|
|
6194
7327
|
'.dig-toasts{position:absolute;right:8px;bottom:8px;display:flex;flex-direction:column;gap:6px;z-index:80;max-width:min(340px,92%)}',
|
|
@@ -6334,7 +7467,7 @@ window.__ModuleLoader__.load({
|
|
|
6334
7467
|
'.dig-row:hover .dig-mini{opacity:1}',
|
|
6335
7468
|
'.dig-mini:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
|
|
6336
7469
|
'.dig-commit-box{flex:none;padding:6px;display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--dsw-alias-hairline,var(--dsw-alias-border-l1))}',
|
|
6337
|
-
'.dig-commit-box-compact{flex-direction:row;align-items:center;gap:6px;padding:4px 6px}',
|
|
7470
|
+
'.dig-commit-box-compact{flex-direction:row;align-items:center;gap:6px;padding:4px 6px;flex-wrap:wrap}',
|
|
6338
7471
|
'.dig-textarea{width:100%;box-sizing:border-box;min-height:50px;resize:vertical;padding:5px 6px;border-radius:var(--dsw-radius-sm,8px);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}',
|
|
6339
7472
|
'.dig-textarea::placeholder{color:var(--dsw-alias-label-tertiary)}',
|
|
6340
7473
|
'.dig-textarea:focus{border-color:var(--dsw-alias-state-business-primary,var(--dsw-alias-brand-primary))}',
|
|
@@ -6415,7 +7548,16 @@ window.__ModuleLoader__.load({
|
|
|
6415
7548
|
return typeof unsubscribe === 'function' ? unsubscribe : undefined
|
|
6416
7549
|
}, [])
|
|
6417
7550
|
void tick
|
|
6418
|
-
|
|
7551
|
+
/* Read at RENDER time (the service may arrive after this plugin's apply) and
|
|
7552
|
+
soft: a host without the plugin page simply keeps the in-panel editor. */
|
|
7553
|
+
let navigation
|
|
7554
|
+
try {
|
|
7555
|
+
navigation = props.ctx === undefined || props.ctx === null ? undefined : props.ctx.get('pluginNavigation')
|
|
7556
|
+
} catch (error) { navigation = undefined }
|
|
7557
|
+
const openSettings = navigation !== undefined && navigation !== null && typeof navigation.openBundle === 'function'
|
|
7558
|
+
? () => { navigation.openBundle(SETTINGS_BUNDLE) }
|
|
7559
|
+
: null
|
|
7560
|
+
return E(Panel, { scope: props.scope, t: props.t, visible: props.visible, dock: props.dock, openSettings: openSettings })
|
|
6419
7561
|
}
|
|
6420
7562
|
|
|
6421
7563
|
function NativePanel(props) {
|
|
@@ -6555,6 +7697,59 @@ window.__ModuleLoader__.load({
|
|
|
6555
7697
|
/* ctx.inject runs its callback straight away when the service is already
|
|
6556
7698
|
there, so only a host without better-sidebar reaches the native seats. */
|
|
6557
7699
|
if (!hostedByBetterSidebar) hostNatively()
|
|
7700
|
+
|
|
7701
|
+
/* ---- settings: the row Config is the rail's source of truth ---- */
|
|
7702
|
+
/* dsh >= 0.1.7 serves a plugin's row Config to the browser through
|
|
7703
|
+
ctx.configForms; from then on the settings card and the rail read that one
|
|
7704
|
+
document (and the legacy localStorage value is migrated once). A host
|
|
7705
|
+
without the service never fires this callback: railForm stays null and the
|
|
7706
|
+
localStorage home + in-panel editor keep working there. */
|
|
7707
|
+
ctx.inject(['configForms'], (formCtx) => {
|
|
7708
|
+
const forms = formCtx.get('configForms')
|
|
7709
|
+
if (forms === undefined || forms === null || typeof forms.get !== 'function') return
|
|
7710
|
+
let form
|
|
7711
|
+
try { form = forms.get(RAIL_NS) } catch (error) { void error }
|
|
7712
|
+
if (form === undefined || form === null || typeof form.getSnapshot !== 'function') return
|
|
7713
|
+
formCtx.effect(() => {
|
|
7714
|
+
railForm = form
|
|
7715
|
+
migrateRailConfig(form)
|
|
7716
|
+
/* The row Config is still 'loading' at bind time, so the migration is
|
|
7717
|
+
attempted here AND on every notification until it lands — it is
|
|
7718
|
+
idempotent (marker-gated, re-entrancy-guarded) and only stamps the
|
|
7719
|
+
marker once the Host accepted the write. */
|
|
7720
|
+
const unsubscribe = typeof form.subscribe === 'function' ? form.subscribe(() => { notifyRailConfig(); migrateRailConfig(form) }) : undefined
|
|
7721
|
+
notifyRailConfig()
|
|
7722
|
+
return () => {
|
|
7723
|
+
railForm = null
|
|
7724
|
+
if (typeof unsubscribe === 'function') unsubscribe()
|
|
7725
|
+
notifyRailConfig()
|
|
7726
|
+
}
|
|
7727
|
+
}, 'dsh-ide-git: row config')
|
|
7728
|
+
})
|
|
7729
|
+
|
|
7730
|
+
/* The settings card, on both official seats: the plugin's detail page
|
|
7731
|
+
(keyed by the PACKAGE name) and Settings → Plugins (keyed by the settings
|
|
7732
|
+
namespace = the loader row id). Both seats are optional — a host without
|
|
7733
|
+
the slots keeps whatever surface it has. */
|
|
7734
|
+
ctx.inject(['slots'], (slotCtx) => {
|
|
7735
|
+
const slots = slotCtx.get('slots')
|
|
7736
|
+
if (slots === undefined || slots === null || typeof slots.inject !== 'function') return
|
|
7737
|
+
slotCtx.effect(() => {
|
|
7738
|
+
const card = (cardProps) => E(RailSettingsCard, Object.assign({}, cardProps, { t: t }))
|
|
7739
|
+
const offBundle = slots.inject('plugins.bundle.config', () => slots.register(
|
|
7740
|
+
{ name: 'plugins.bundle.config', key: SETTINGS_BUNDLE, locale: LOCALE_NS },
|
|
7741
|
+
card,
|
|
7742
|
+
))
|
|
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
|
+
return () => {
|
|
7748
|
+
try { offBundle() } catch (error) { void error }
|
|
7749
|
+
try { offItem() } catch (error) { void error }
|
|
7750
|
+
}
|
|
7751
|
+
}, 'dsh-ide-git: settings card')
|
|
7752
|
+
})
|
|
6558
7753
|
}
|
|
6559
7754
|
|
|
6560
7755
|
return { name: 'dsh-ide-git', inject: [], apply }
|