dsh-ide-git 0.16.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-ide-git",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "main": "./src/index.js",
5
5
  "description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ide-git",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
5
5
  "type": "module",
6
6
  "repository": {
package/src/client.js CHANGED
@@ -279,7 +279,7 @@ window.__ModuleLoader__.load({
279
279
  'lf.truncated': '只显示前 {count} 行',
280
280
  'preview.title': 'Git 预览',
281
281
  'preview.hint': '点击文件或提交的「显示差异」,差异会在侧边栏这里打开。',
282
- 'settings.rail.title': '动作条按钮',
282
+ 'settings.rail.title': '动作条',
283
283
  'settings.rail.hint': '勾选要显示在动作条上的动作;顺序固定为默认顺序,取消勾选即隐藏。',
284
284
  'settings.rail.unavailable': '当前宿主没有设置服务,请用面板内的「动作条设置」。',
285
285
  'settings.commit.title': 'AI 提交信息',
@@ -568,7 +568,7 @@ window.__ModuleLoader__.load({
568
568
  'lf.truncated': 'Showing the first {count} lines',
569
569
  'preview.title': 'Git Preview',
570
570
  'preview.hint': 'Click “Show diff” on a file or commit — the diff opens here in the sidebar.',
571
- 'settings.rail.title': 'Action rail buttons',
571
+ 'settings.rail.title': 'Action rail',
572
572
  'settings.rail.hint': 'Tick the actions shown on the rail. The order is fixed; unticking one hides it.',
573
573
  'settings.rail.unavailable': 'This host has no settings service; use the in-panel “Action rail settings”.',
574
574
  'settings.commit.title': 'AI commit message',
@@ -865,7 +865,7 @@ window.__ModuleLoader__.load({
865
865
  'lf.truncated': '只顯示前 {count} 行',
866
866
  'preview.title': 'Git 預覽',
867
867
  'preview.hint': '喺檔案或提交撳「顯示差異」,差異就會喺側邊欄度開啟。',
868
- 'settings.rail.title': '動作列按鈕',
868
+ 'settings.rail.title': '動作列',
869
869
  'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
870
870
  'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
871
871
  'settings.commit.title': 'AI 提交訊息',
@@ -1154,7 +1154,7 @@ window.__ModuleLoader__.load({
1154
1154
  'lf.truncated': '只顯示前 {count} 行',
1155
1155
  'preview.title': 'Git 預覽',
1156
1156
  'preview.hint': '點選檔案或提交的「顯示差異」,差異就會在側邊欄開啟。',
1157
- 'settings.rail.title': '動作列按鈕',
1157
+ 'settings.rail.title': '動作列',
1158
1158
  'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
1159
1159
  'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
1160
1160
  'settings.commit.title': 'AI 提交訊息',
@@ -1443,7 +1443,7 @@ window.__ModuleLoader__.load({
1443
1443
  'lf.truncated': '只顯示前 {count} 行',
1444
1444
  'preview.title': 'Git 預覽',
1445
1445
  'preview.hint': '喺檔案或提交撳「顯示差異」,差異就會喺側邊欄度開啟。',
1446
- 'settings.rail.title': '動作列按鈕',
1446
+ 'settings.rail.title': '動作列',
1447
1447
  'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
1448
1448
  'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
1449
1449
  'settings.commit.title': 'AI 提交訊息',
@@ -1732,7 +1732,7 @@ window.__ModuleLoader__.load({
1732
1732
  'lf.truncated': '先頭 {count} 行のみ表示',
1733
1733
  'preview.title': 'Git プレビュー',
1734
1734
  'preview.hint': 'ファイルやコミットの「差分を表示」をクリックすると、差分がここに開きます。',
1735
- 'settings.rail.title': 'アクションレールのボタン',
1735
+ 'settings.rail.title': 'レール',
1736
1736
  'settings.rail.hint': 'レールに表示する操作にチェックを入れてください。順序は固定で、チェックを外すと非表示になります。',
1737
1737
  'settings.rail.unavailable': 'このホストには設定サービスがないため、パネル内の「アクションレール設定」を使ってください。',
1738
1738
  'settings.commit.title': 'AI コミットメッセージ',
@@ -2021,7 +2021,7 @@ window.__ModuleLoader__.load({
2021
2021
  'lf.truncated': '처음 {count}줄만 표시',
2022
2022
  'preview.title': 'Git 미리보기',
2023
2023
  'preview.hint': '파일이나 커밋의 「차이 보기」를 클릭하면 차이가 사이드바 여기에 열립니다.',
2024
- 'settings.rail.title': '액션 레일 버튼',
2024
+ 'settings.rail.title': '레일',
2025
2025
  'settings.rail.hint': '레일에 표시할 동작을 선택하세요. 순서는 고정이며 선택을 해제하면 숨겨집니다.',
2026
2026
  'settings.rail.unavailable': '이 호스트에는 설정 서비스가 없으므로 패널의 "액션 레일 설정"을 사용하세요.',
2027
2027
  'settings.commit.title': 'AI 커밋 메시지',
@@ -2310,7 +2310,7 @@ window.__ModuleLoader__.load({
2310
2310
  'lf.truncated': 'Nur die ersten {count} Zeilen',
2311
2311
  'preview.title': 'Git-Vorschau',
2312
2312
  'preview.hint': '„Diff anzeigen“ an einer Datei oder einem Commit öffnet den Diff hier in der Seitenleiste.',
2313
- 'settings.rail.title': 'Schaltflächen der Aktionsleiste',
2313
+ 'settings.rail.title': 'Aktionsleiste',
2314
2314
  'settings.rail.hint': 'Die auf der Leiste sichtbaren Aktionen ankreuzen. Die Reihenfolge ist fest; abwählen blendet eine aus.',
2315
2315
  'settings.rail.unavailable': 'Dieser Host hat keinen Einstellungsdienst; nutze die „Aktionsleisten-Einstellungen“ im Panel.',
2316
2316
  'settings.commit.title': 'KI-Commit-Nachricht',
@@ -2599,7 +2599,7 @@ window.__ModuleLoader__.load({
2599
2599
  'lf.truncated': 'Affichage des {count} premières lignes',
2600
2600
  'preview.title': 'Aperçu Git',
2601
2601
  'preview.hint': 'Cliquez sur « Afficher le diff » sur un fichier ou un commit : le diff s’ouvre ici, dans la barre latérale.',
2602
- 'settings.rail.title': 'Boutons de la barre d’actions',
2602
+ 'settings.rail.title': 'Barre d’actions',
2603
2603
  'settings.rail.hint': 'Cochez les actions affichées sur la barre. L’ordre est fixe ; décocher masque une action.',
2604
2604
  '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.',
2605
2605
  'settings.commit.title': 'Message de commit par IA',
@@ -2888,7 +2888,7 @@ window.__ModuleLoader__.load({
2888
2888
  'lf.truncated': 'Показаны первые {count} строк',
2889
2889
  'preview.title': 'Git-предпросмотр',
2890
2890
  'preview.hint': 'Нажмите «Показать различия» у файла или коммита — различия откроются здесь, в боковой панели.',
2891
- 'settings.rail.title': 'Кнопки панели действий',
2891
+ 'settings.rail.title': 'Панель действий',
2892
2892
  'settings.rail.hint': 'Отметьте действия, показываемые на панели. Порядок фиксирован; снятие флажка скрывает действие.',
2893
2893
  'settings.rail.unavailable': 'На этом хосте нет службы настроек; используйте «Настройки панели действий» в панели.',
2894
2894
  'settings.commit.title': 'Сообщение коммита от ИИ',
@@ -3177,7 +3177,7 @@ window.__ModuleLoader__.load({
3177
3177
  'lf.truncated': 'Mostrando as primeiras {count} linhas',
3178
3178
  'preview.title': 'Pré-visualização do Git',
3179
3179
  'preview.hint': 'Clique em “Mostrar diff” num arquivo ou commit — o diff abre aqui, na barra lateral.',
3180
- 'settings.rail.title': 'Botões da barra de ações',
3180
+ 'settings.rail.title': 'Barra de ações',
3181
3181
  'settings.rail.hint': 'Marque as ações exibidas na barra. A ordem é fixa; desmarcar oculta uma ação.',
3182
3182
  'settings.rail.unavailable': 'Este host não tem serviço de configurações; use «Configurações da barra de ações» no painel.',
3183
3183
  'settings.commit.title': 'Mensagem de commit por IA',
@@ -3466,7 +3466,7 @@ window.__ModuleLoader__.load({
3466
3466
  'lf.truncated': 'Mostrate le prime {count} righe',
3467
3467
  'preview.title': 'Anteprima Git',
3468
3468
  'preview.hint': 'Fai clic su «Mostra diff» su un file o un commit: il diff si apre qui, nella barra laterale.',
3469
- 'settings.rail.title': 'Pulsanti della barra azioni',
3469
+ 'settings.rail.title': 'Barra delle azioni',
3470
3470
  'settings.rail.hint': 'Seleziona le azioni mostrate sulla barra. L’ordine è fisso; deselezionando se ne nasconde una.',
3471
3471
  'settings.rail.unavailable': 'Questo host non ha un servizio di impostazioni; usa «Impostazioni barra azioni» nel pannello.',
3472
3472
  'settings.commit.title': 'Messaggio di commit con IA',
@@ -3755,7 +3755,7 @@ window.__ModuleLoader__.load({
3755
3755
  'lf.truncated': 'Eerste {count} regels worden weergegeven',
3756
3756
  'preview.title': 'Git-voorbeeld',
3757
3757
  'preview.hint': 'Klik op „Diff tonen“ bij een bestand of commit — de diff opent hier in de zijbalk.',
3758
- 'settings.rail.title': 'Knoppen van de actiebalk',
3758
+ 'settings.rail.title': 'Actiebalk',
3759
3759
  'settings.rail.hint': 'Vink de acties aan die op de balk verschijnen. De volgorde ligt vast; uitvinken verbergt er een.',
3760
3760
  'settings.rail.unavailable': 'Deze host heeft geen instellingenservice; gebruik "Actiebalk-instellingen" in het paneel.',
3761
3761
  'settings.commit.title': 'AI-commitbericht',
@@ -4044,7 +4044,7 @@ window.__ModuleLoader__.load({
4044
4044
  'lf.truncated': 'Pokazano pierwsze {count} wierszy',
4045
4045
  'preview.title': 'Podgląd Git',
4046
4046
  'preview.hint': 'Kliknij „Pokaż różnice” przy pliku lub commicie — różnice otworzą się tutaj, na pasku bocznym.',
4047
- 'settings.rail.title': 'Przyciski paska akcji',
4047
+ 'settings.rail.title': 'Pasek akcji',
4048
4048
  'settings.rail.hint': 'Zaznacz akcje widoczne na pasku. Kolejność jest stała; odznaczenie ukrywa akcję.',
4049
4049
  'settings.rail.unavailable': 'Ten host nie ma usługi ustawień; użyj „Ustawienia paska akcji” w panelu.',
4050
4050
  'settings.commit.title': 'Wiadomość commita od AI',
@@ -4333,7 +4333,7 @@ window.__ModuleLoader__.load({
4333
4333
  'lf.truncated': 'Visar de första {count} raderna',
4334
4334
  'preview.title': 'Git-förhandsvisning',
4335
4335
  'preview.hint': 'Klicka på ”Visa diff” på en fil eller en commit — diffen öppnas här i sidopanelen.',
4336
- 'settings.rail.title': 'Knappar i åtgärdsfältet',
4336
+ 'settings.rail.title': 'Åtgärdsfältet',
4337
4337
  'settings.rail.hint': 'Markera de åtgärder som visas i fältet. Ordningen är fast; avmarkering döljer en.',
4338
4338
  'settings.rail.unavailable': 'Denna värd saknar inställningstjänst; använd ”Åtgärdsfältets inställningar” i panelen.',
4339
4339
  'settings.commit.title': 'AI-commitmeddelande',
@@ -4622,7 +4622,7 @@ window.__ModuleLoader__.load({
4622
4622
  'lf.truncated': 'Yalnızca ilk {count} satır gösteriliyor',
4623
4623
  'preview.title': 'Git Önizlemesi',
4624
4624
  'preview.hint': 'Bir dosya veya commit üzerinde „Farkı göster”e tıklayın — fark burada, kenar çubuğunda açılır.',
4625
- 'settings.rail.title': 'Eylem çubuğu düğmeleri',
4625
+ 'settings.rail.title': 'Eylem çubuğu',
4626
4626
  'settings.rail.hint': 'Çubukta gösterilecek eylemleri işaretleyin. Sıra sabittir; işareti kaldırmak eylemi gizler.',
4627
4627
  'settings.rail.unavailable': 'Bu ana bilgisayarda ayar hizmeti yok; paneldeki "Eylem çubuğu ayarları"nı kullanın.',
4628
4628
  'settings.commit.title': 'AI commit mesajı',
@@ -4911,7 +4911,7 @@ window.__ModuleLoader__.load({
4911
4911
  'lf.truncated': 'Menampilkan {count} baris pertama',
4912
4912
  'preview.title': 'Pratinjau Git',
4913
4913
  'preview.hint': 'Klik “Tampilkan diff” pada file atau commit — diff terbuka di sini, di bilah sisi.',
4914
- 'settings.rail.title': 'Tombol bilah aksi',
4914
+ 'settings.rail.title': 'Bilah aksi',
4915
4915
  'settings.rail.hint': 'Centang aksi yang ditampilkan di bilah. Urutannya tetap; menghapus centang menyembunyikannya.',
4916
4916
  'settings.rail.unavailable': 'Host ini tidak punya layanan pengaturan; gunakan "Pengaturan bilah aksi" di panel.',
4917
4917
  'settings.commit.title': 'Pesan commit AI',
@@ -5200,7 +5200,7 @@ window.__ModuleLoader__.load({
5200
5200
  'lf.truncated': 'Chỉ hiện {count} dòng đầu',
5201
5201
  'preview.title': 'Xem trước Git',
5202
5202
  'preview.hint': 'Bấm “Xem diff” trên tệp hoặc commit — diff sẽ mở ở đây, trên thanh bên.',
5203
- 'settings.rail.title': 'Nút thanh tác vụ',
5203
+ 'settings.rail.title': 'Thanh tác vụ',
5204
5204
  '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ụ đó.',
5205
5205
  '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.',
5206
5206
  'settings.commit.title': 'Thông điệp commit do AI viết',
@@ -5489,7 +5489,7 @@ window.__ModuleLoader__.load({
5489
5489
  'lf.truncated': 'عرض أول {count} سطرًا فقط',
5490
5490
  'preview.title': 'معاينة Git',
5491
5491
  'preview.hint': 'انقر «عرض الفروق» على ملف أو commit — تُفتح الفروق هنا في الشريط الجانبي.',
5492
- 'settings.rail.title': 'أزرار شريط الإجراءات',
5492
+ 'settings.rail.title': 'شريط الإجراءات',
5493
5493
  'settings.rail.hint': 'حدّد الإجراءات المعروضة على الشريط. الترتيب ثابت، وإلغاء التحديد يخفي الإجراء.',
5494
5494
  'settings.rail.unavailable': 'لا تتوفر خدمة إعدادات على هذا المضيف؛ استخدم «إعدادات شريط الإجراءات» في اللوحة.',
5495
5495
  'settings.commit.title': 'رسالة إيداع بالذكاء الاصطناعي',
@@ -5778,7 +5778,7 @@ window.__ModuleLoader__.load({
5778
5778
  'lf.truncated': 'केवल पहली {count} पंक्तियाँ दिखाई जा रही हैं',
5779
5779
  'preview.title': 'Git पूर्वावलोकन',
5780
5780
  'preview.hint': 'फ़ाइल या कमिट पर “अंतर दिखाएँ” पर क्लिक करें — अंतर यहीं साइडबार में खुलेगा।',
5781
- 'settings.rail.title': 'एक्शन रेल बटन',
5781
+ 'settings.rail.title': 'क्रिया रेल',
5782
5782
  'settings.rail.hint': 'रेल पर दिखने वाली क्रियाएँ चुनें। क्रम निश्चित है; चुनाव हटाने पर वह छिप जाती है।',
5783
5783
  'settings.rail.unavailable': 'इस होस्ट में सेटिंग सेवा नहीं है; पैनल में «एक्शन रेल सेटिंग» का उपयोग करें।',
5784
5784
  'settings.commit.title': 'AI कमिट संदेश',
@@ -6067,7 +6067,7 @@ window.__ModuleLoader__.load({
6067
6067
  'lf.truncated': 'แสดงเฉพาะ {count} บรรทัดแรก',
6068
6068
  'preview.title': 'ดูตัวอย่าง Git',
6069
6069
  'preview.hint': 'คลิก “แสดงความต่าง” ที่ไฟล์หรือคอมมิต — ความต่างจะเปิดที่นี่ในแถบข้าง',
6070
- 'settings.rail.title': 'ปุ่มแถบการทำงาน',
6070
+ 'settings.rail.title': 'แถบการทำงาน',
6071
6071
  'settings.rail.hint': 'ทำเครื่องหมายการทำงานที่จะแสดงบนแถบ ลำดับคงที่; ยกเลิกเครื่องหมายเพื่อซ่อน',
6072
6072
  'settings.rail.unavailable': 'โฮสต์นี้ไม่มีบริการตั้งค่า ให้ใช้ «ตั้งค่าแถบการทำงาน» ในแผง',
6073
6073
  'settings.commit.title': 'ข้อความคอมมิตโดย AI',
@@ -8574,6 +8574,31 @@ window.__ModuleLoader__.load({
8574
8574
  }, children),
8575
8575
  hint === null ? null : E('div', { className: 'dig-settings-subnote' }, hint))
8576
8576
  }
8577
+ /* Collapsible sections, HAND-DRAWN. The official disclosure row was ruled
8578
+ out the same way the sibling dsh-gitbash-shell ruled it out (v0.38.0 -
8579
+ 0.38.3): the ui-primitives module shape this plugin's restricted require
8580
+ returns does not expose it, so a borrowed component silently degrades to
8581
+ a flat title+stack pile. One button + one conditional draws the whole
8582
+ thing: the header row always renders, the body only while open, and all
8583
+ sections ship collapsed. State lives with the other hooks, above every
8584
+ early return, so a status flip cannot reorder them. */
8585
+ const [folds, setFolds] = useState({})
8586
+ const toggleFold = (key) => {
8587
+ setFolds((previous) => Object.assign({}, previous, { [key]: !(previous[key] === true) }))
8588
+ }
8589
+ const fold = (titleKey, ...body) => {
8590
+ const open = folds[titleKey] === true
8591
+ return E('div', { className: 'dig-fold' },
8592
+ E('button', {
8593
+ type: 'button',
8594
+ className: 'dig-foldHead',
8595
+ 'aria-expanded': open ? 'true' : 'false',
8596
+ onClick: () => toggleFold(titleKey),
8597
+ },
8598
+ E('span', { className: 'dig-foldChevron' + (open ? ' dig-foldChevronOpen' : ''), 'aria-hidden': 'true' }, '▸'),
8599
+ E('span', { className: 'dig-foldTitle' }, t(titleKey))),
8600
+ open ? E('div', { className: 'dig-stack' }, body) : null)
8601
+ }
8577
8602
  const flush = () => {
8578
8603
  pendingFlush.current = null
8579
8604
  /* The pending TARGET per field, not "whatever the render-time intent
@@ -8728,9 +8753,14 @@ window.__ModuleLoader__.load({
8728
8753
  and a sentence, not controls that silently do nothing. */
8729
8754
  const readOnly = snapshot.writable !== true
8730
8755
  return wrapCard(E('div', { className: 'dig-settings dig-card-body', 'data-settings-status': status, 'data-settings-readonly': readOnly === true ? 'true' : 'false' },
8731
- E('div', { className: 'dig-settings-hint' }, t('settings.rail.hint')),
8732
8756
  E('div', { className: readOnly === true ? 'dig-settings-note' : 'dig-settings-note dig-settings-note-hidden', 'data-settings-readonly-note': '' }, t('settings.rail.readonly')),
8733
8757
  note === '' ? null : E('div', { className: 'dig-settings-error', 'data-settings-error': '' }, note),
8758
+ /* Three collapsible sections, one per functional domain — the header
8759
+ rows are what a collapsed card shows. The readonly/error notes stay
8760
+ OUTSIDE them: they are status, not settings, and must stay visible.
8761
+ Section 1 carries its own hint (it describes exactly this list). */
8762
+ fold('settings.rail.title',
8763
+ E('div', { className: 'dig-settings-hint' }, t('settings.rail.hint')),
8734
8764
  E('div', { className: 'dig-settings-list' }, RAIL_SPECS.map((spec) => {
8735
8765
  const inputId = 'dig-rail-' + spec.id
8736
8766
  const visible = shown(spec)
@@ -8778,11 +8808,11 @@ window.__ModuleLoader__.load({
8778
8808
  }),
8779
8809
  E('span', { className: 'dig-settings-glyph' }, E(Icon, { name: spec.icon, size: 13 })),
8780
8810
  E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t(spec.key)))
8781
- })),
8782
- /* Placement (issue #8): one switch, the same optimistic-write contract
8783
- the rail rows keep. The conversation tab registers/unregisters live
8784
- (see door 3 in apply), so the flip is visible without a reload. */
8785
- E('div', { className: 'dig-settings-sub' }, t('settings.placement.title')),
8811
+ }))),
8812
+ /* Section 2 — placement (issue #8): one switch, the same optimistic-write
8813
+ contract the rail rows keep. The conversation tab registers/unregisters
8814
+ live (see door 3 in apply), so the flip is visible without a reload. */
8815
+ fold('settings.placement.title',
8786
8816
  (() => {
8787
8817
  const field = 'conversationTab'
8788
8818
  const inputId = 'dig-placement-' + field
@@ -8865,8 +8895,10 @@ window.__ModuleLoader__.load({
8865
8895
  E('span', { className: 'dig-settings-glyph' }, E(Icon, { name: 'commit', size: 13 })),
8866
8896
  E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t('settings.placement.immersive'))),
8867
8897
  E('div', { className: 'dig-settings-subnote' }, t('settings.placement.immersiveHint')))
8868
- })(),
8869
- E('div', { className: 'dig-settings-sub' }, t('settings.commit.title')),
8898
+ })()),
8899
+ /* Section 3 — the AI commit message (issue #6): model, reasoning and the
8900
+ prompt template. */
8901
+ fold('settings.commit.title',
8870
8902
  (() => {
8871
8903
  /* The model row is a picker over the host's provider catalog; while
8872
8904
  the catalog has not answered (or cannot), the row stays the plain
@@ -8912,7 +8944,7 @@ window.__ModuleLoader__.load({
8912
8944
  }
8913
8945
  return textRow('commitReasoning', 'settings.commit.reasoning', 'settings.commit.reasoningHint', 60, false)
8914
8946
  })(),
8915
- textRow('commitPrompt', 'settings.commit.prompt', 'settings.commit.promptHint', 2000, true)))
8947
+ textRow('commitPrompt', 'settings.commit.prompt', 'settings.commit.promptHint', 2000, true))))
8916
8948
  }
8917
8949
 
8918
8950
  function RailSettings(props) {
@@ -9578,11 +9610,12 @@ window.__ModuleLoader__.load({
9578
9610
  for (const item of changeItems) sums.push(sums[sums.length - 1] + item.h)
9579
9611
  return sums
9580
9612
  }, [changeItems])
9613
+ /* Virtual window, VSCode-style (ListView.getVisibleRange): both ends are
9614
+ pixel→index conversions over the same prefix-sum array, so no "row
9615
+ count" ever enters the math (issue #12 — the old winLimit computed a
9616
+ row count while the loop accumulated pixels, stalling at 2 entries). */
9581
9617
  const winFirst = Math.max(0, windowStart(changeOffsets, changesViewport.scrollTop) - WINDOW_OVERSCAN)
9582
- const winLimit = Math.ceil(changesViewport.viewH / ITEM_HEIGHTS.row) + WINDOW_OVERSCAN * 3
9583
- let winLast = winFirst
9584
- let used = 0
9585
- while (winLast < changeItems.length && used < winLimit) { used += changeItems[winLast].h; winLast += 1 }
9618
+ const winLast = Math.min(changeItems.length, windowStart(changeOffsets, changesViewport.scrollTop + changesViewport.viewH) + 1 + WINDOW_OVERSCAN)
9586
9619
  const visibleItems = changeItems.slice(winFirst, winLast)
9587
9620
  const aiConfirmDialog = aiAsk !== true ? null : E(ConfirmDialog, {
9588
9621
  t: t,
@@ -12051,6 +12084,14 @@ window.__ModuleLoader__.load({
12051
12084
  '.dig-settings-glyph{display:inline-flex;flex:none;color:var(--dsw-alias-label-secondary)}',
12052
12085
  '.dig-settings-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}',
12053
12086
  '.dig-root-narrow .dig-settings-label{white-space:normal}',
12087
+ /* Collapsible setting sections (hand-drawn, same pattern as dsh-gitbash-shell v0.38.4). */
12088
+ '.dig-fold{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,8px);padding:4px 12px 12px;background:var(--dsw-alias-bg-layer-1,transparent)}',
12089
+ '.dig-fold .dig-stack{padding-top:10px}',
12090
+ '.dig-foldHead{display:flex;align-items:center;gap:8px;width:100%;appearance:none;background:none;border:0;padding:6px 0;font:inherit;font-size:13px;font-weight:600;letter-spacing:.02em;color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:left}',
12091
+ '.dig-foldHead:hover{color:var(--dsw-alias-label-primary)}',
12092
+ '.dig-foldChevron{flex:none;display:inline-block;color:var(--dsw-alias-label-tertiary);transition:transform .16s}',
12093
+ '.dig-foldChevron.dig-foldChevronOpen{transform:rotate(90deg)}',
12094
+ '.dig-foldTitle{min-width:0;overflow-wrap:anywhere}',
12054
12095
  '.dig-root-narrow .dig-menu-item{white-space:normal}',
12055
12096
  '.dig-root-narrow .dig-toast-text{white-space:normal}',
12056
12097
  '.dig-toasts{position:absolute;right:8px;bottom:8px;display:flex;flex-direction:column;gap:6px;z-index:80;max-width:min(340px,92%)}',
package/src/index.js CHANGED
@@ -239,7 +239,11 @@ function cwdOf(payload) {
239
239
  /* ============================== git reads ============================== */
240
240
 
241
241
  async function repoRootOf(cwd, payload) {
242
- if (payload !== undefined && payload.repoRoot !== undefined) return requireAbsolute(payload.repoRoot, 'repoRoot')
242
+ /* Normalize the separator: `rev-parse --show-toplevel` spells drive paths
243
+ with forward slashes on Windows while path.join produces backslashes, so
244
+ the untracked-diff fence (root + path.sep prefix check) rejected every
245
+ in-repo path there. macOS/Linux are byte-identical no-ops. */
246
+ if (payload !== undefined && payload.repoRoot !== undefined) return path.normalize(requireAbsolute(payload.repoRoot, 'repoRoot'))
243
247
  const result = await runGit(cwd, ['rev-parse', '--show-toplevel'])
244
248
  if (result.code !== 0) {
245
249
  // A workspace that is not a repository is a normal state, not a failure:
@@ -247,7 +251,7 @@ async function repoRootOf(cwd, payload) {
247
251
  throw new PanelError('not-a-repo', 'not a git repository: ' + cwd, 409)
248
252
  }
249
253
  const top = result.stdout.trim()
250
- return top === '' ? cwd : top
254
+ return path.normalize(top === '' ? cwd : top)
251
255
  }
252
256
 
253
257
  async function currentBranchOf(root) {
@@ -800,7 +804,12 @@ async function diff(payload) {
800
804
  /* --no-index reads the file for real, so unlike the plain `git diff --`
801
805
  form an absolute path must NOT be allowed to escape the repository. */
802
806
  if (absolute !== root && !absolute.startsWith(root + path.sep)) throw badRequest('path must stay inside the repository')
803
- const result = await runGit(root, ['diff', '--no-color', '--no-ext-diff', '-U3', '--no-index', '--', '/dev/null', absolute])
807
+ /* git quotes and C-escapes backslash paths inside the diff header (and
808
+ git ≥2.55 spells the a/ side with the real path instead of /dev/null);
809
+ passing forward slashes keeps the header unquoted, so the rewrite below
810
+ can match it verbatim. POSIX is a no-op. */
811
+ const headerPath = absolute.split(path.sep).join('/')
812
+ const result = await runGit(root, ['diff', '--no-color', '--no-ext-diff', '-U3', '--no-index', '--', '/dev/null', headerPath])
804
813
  if (result.code !== 0 && result.code !== 1) {
805
814
  const detail = result.stderr.trim() === '' ? result.stdout.trim() : result.stderr.trim()
806
815
  throw new PanelError('git-failed', 'git diff --no-index failed: ' + (detail === '' ? 'exit ' + result.code : detail), 409)
@@ -809,12 +818,12 @@ async function diff(payload) {
809
818
  the header lines back to the repository-relative spelling so the preview
810
819
  reads like every other patch (body lines are left untouched). */
811
820
  let out = result.stdout
812
- if (out.indexOf(absolute) >= 0 && out.indexOf('\n') !== -1) {
821
+ if (out.indexOf(headerPath) >= 0 && out.indexOf('\n') !== -1) {
813
822
  out = out.split('\n').map((line) => {
814
- if (line.startsWith('diff --git ') && line.indexOf(absolute) >= 0) return 'diff --git a/' + rel + ' b/' + rel
815
- /* the b/ side of --no-index carries the absolute path verbatim (its
816
- leading slash merged into the separator): match '+++ b' + absolute. */
817
- if (line.startsWith('+++ b' + absolute)) return '+++ b/' + rel
823
+ if (line.startsWith('diff --git ') && line.indexOf(headerPath) >= 0) return 'diff --git a/' + rel + ' b/' + rel
824
+ /* git ≥2.55 quotes the header and spells the a/ side with the real
825
+ path instead of /dev/null, so match on the path, not the prefix. */
826
+ if (line.startsWith('+++ ') && line.indexOf(headerPath) >= 0) return '+++ b/' + rel
818
827
  return line
819
828
  }).join('\n')
820
829
  }