dsh-ide-git 0.19.6 → 0.19.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-ide-git",
3
- "version": "0.19.6",
3
+ "version": "0.19.8",
4
4
  "main": "./src/index.js",
5
5
  "description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ide-git",
3
- "version": "0.19.6",
3
+ "version": "0.19.8",
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
@@ -315,6 +315,7 @@ window.__ModuleLoader__.load({
315
315
  'lf.truncated': '只显示前 {count} 行',
316
316
  'preview.title': 'Git 预览',
317
317
  'preview.hint': '点击文件或提交的「显示差异」,差异会在侧边栏这里打开。',
318
+ 'preview.boundary': '预览渲染出错——请重载页面,或反馈这个问题。',
318
319
  'settings.rail.title': '动作条',
319
320
  'settings.rail.hint': '勾选要显示在动作条上的动作;顺序固定为默认顺序,取消勾选即隐藏。',
320
321
  'settings.rail.unavailable': '当前宿主没有设置服务,请用面板内的「动作条设置」。',
@@ -642,6 +643,7 @@ window.__ModuleLoader__.load({
642
643
  'lf.truncated': 'Showing the first {count} lines',
643
644
  'preview.title': 'Git Preview',
644
645
  'preview.hint': 'Click “Show diff” on a file or commit — the diff opens here in the sidebar.',
646
+ 'preview.boundary': 'The preview failed to render — reload the page, or report an issue.',
645
647
  'settings.rail.title': 'Action rail',
646
648
  'settings.rail.hint': 'Tick the actions shown on the rail. The order is fixed; unticking one hides it.',
647
649
  'settings.rail.unavailable': 'This host has no settings service; use the in-panel “Action rail settings”.',
@@ -977,6 +979,7 @@ window.__ModuleLoader__.load({
977
979
  'lf.truncated': '只顯示前 {count} 行',
978
980
  'preview.title': 'Git 預覽',
979
981
  'preview.hint': '喺檔案或提交撳「顯示差異」,差異就會喺側邊欄度開啟。',
982
+ 'preview.boundary': '預覽渲染出錯——請重新載入頁面,或者回報呢個問題。',
980
983
  'settings.rail.title': '動作列',
981
984
  'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
982
985
  'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
@@ -1304,6 +1307,7 @@ window.__ModuleLoader__.load({
1304
1307
  'lf.truncated': '只顯示前 {count} 行',
1305
1308
  'preview.title': 'Git 預覽',
1306
1309
  'preview.hint': '點選檔案或提交的「顯示差異」,差異就會在側邊欄開啟。',
1310
+ 'preview.boundary': '預覽算繪出錯——請重新載入頁面,或回報這個問題。',
1307
1311
  'settings.rail.title': '動作列',
1308
1312
  'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
1309
1313
  'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
@@ -1631,6 +1635,7 @@ window.__ModuleLoader__.load({
1631
1635
  'lf.truncated': '只顯示前 {count} 行',
1632
1636
  'preview.title': 'Git 預覽',
1633
1637
  'preview.hint': '喺檔案或提交撳「顯示差異」,差異就會喺側邊欄度開啟。',
1638
+ 'preview.boundary': '預覽渲染出錯——請重新載入頁面,或者回報呢個問題。',
1634
1639
  'settings.rail.title': '動作列',
1635
1640
  'settings.rail.hint': '勾選要顯示在動作列上的動作;順序固定,取消勾選即隱藏。',
1636
1641
  'settings.rail.unavailable': '目前宿主沒有設定服務,請用面板內的「動作條設定」。',
@@ -1958,6 +1963,7 @@ window.__ModuleLoader__.load({
1958
1963
  'lf.truncated': '先頭 {count} 行のみ表示',
1959
1964
  'preview.title': 'Git プレビュー',
1960
1965
  'preview.hint': 'ファイルやコミットの「差分を表示」をクリックすると、差分がここに開きます。',
1966
+ 'preview.boundary': 'プレビューの描画に失敗しました——ページを再読み込みするか、問題を報告してください。',
1961
1967
  'settings.rail.title': 'レール',
1962
1968
  'settings.rail.hint': 'レールに表示する操作にチェックを入れてください。順序は固定で、チェックを外すと非表示になります。',
1963
1969
  'settings.rail.unavailable': 'このホストには設定サービスがないため、パネル内の「アクションレール設定」を使ってください。',
@@ -2285,6 +2291,7 @@ window.__ModuleLoader__.load({
2285
2291
  'lf.truncated': '처음 {count}줄만 표시',
2286
2292
  'preview.title': 'Git 미리보기',
2287
2293
  'preview.hint': '파일이나 커밋의 「차이 보기」를 클릭하면 차이가 사이드바 여기에 열립니다.',
2294
+ 'preview.boundary': '미리보기 렌더링에 실패했습니다 — 페이지를 새로고침하거나 문제를 보고해 주세요.',
2288
2295
  'settings.rail.title': '레일',
2289
2296
  'settings.rail.hint': '레일에 표시할 동작을 선택하세요. 순서는 고정이며 선택을 해제하면 숨겨집니다.',
2290
2297
  'settings.rail.unavailable': '이 호스트에는 설정 서비스가 없으므로 패널의 "액션 레일 설정"을 사용하세요.',
@@ -2612,6 +2619,7 @@ window.__ModuleLoader__.load({
2612
2619
  'lf.truncated': 'Nur die ersten {count} Zeilen',
2613
2620
  'preview.title': 'Git-Vorschau',
2614
2621
  'preview.hint': '„Diff anzeigen“ an einer Datei oder einem Commit öffnet den Diff hier in der Seitenleiste.',
2622
+ 'preview.boundary': 'Die Vorschau konnte nicht gerendert werden – Seite neu laden oder das Problem melden.',
2615
2623
  'settings.rail.title': 'Aktionsleiste',
2616
2624
  'settings.rail.hint': 'Die auf der Leiste sichtbaren Aktionen ankreuzen. Die Reihenfolge ist fest; abwählen blendet eine aus.',
2617
2625
  'settings.rail.unavailable': 'Dieser Host hat keinen Einstellungsdienst; nutze die „Aktionsleisten-Einstellungen“ im Panel.',
@@ -2939,6 +2947,7 @@ window.__ModuleLoader__.load({
2939
2947
  'lf.truncated': 'Affichage des {count} premières lignes',
2940
2948
  'preview.title': 'Aperçu Git',
2941
2949
  'preview.hint': 'Cliquez sur « Afficher le diff » sur un fichier ou un commit : le diff s’ouvre ici, dans la barre latérale.',
2950
+ 'preview.boundary': 'L\'aperçu n\'a pas pu être affiché — rechargez la page ou signalez le problème.',
2942
2951
  'settings.rail.title': 'Barre d’actions',
2943
2952
  'settings.rail.hint': 'Cochez les actions affichées sur la barre. L’ordre est fixe ; décocher masque une action.',
2944
2953
  '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.',
@@ -3266,6 +3275,7 @@ window.__ModuleLoader__.load({
3266
3275
  'lf.truncated': 'Показаны первые {count} строк',
3267
3276
  'preview.title': 'Git-предпросмотр',
3268
3277
  'preview.hint': 'Нажмите «Показать различия» у файла или коммита — различия откроются здесь, в боковой панели.',
3278
+ 'preview.boundary': 'Не удалось отобразить предпросмотр — перезагрузите страницу или сообщите о проблеме.',
3269
3279
  'settings.rail.title': 'Панель действий',
3270
3280
  'settings.rail.hint': 'Отметьте действия, показываемые на панели. Порядок фиксирован; снятие флажка скрывает действие.',
3271
3281
  'settings.rail.unavailable': 'На этом хосте нет службы настроек; используйте «Настройки панели действий» в панели.',
@@ -3593,6 +3603,7 @@ window.__ModuleLoader__.load({
3593
3603
  'lf.truncated': 'Mostrando as primeiras {count} linhas',
3594
3604
  'preview.title': 'Pré-visualização do Git',
3595
3605
  'preview.hint': 'Clique em “Mostrar diff” num arquivo ou commit — o diff abre aqui, na barra lateral.',
3606
+ 'preview.boundary': 'Falha ao renderizar a pré-visualização — recarregue a página ou relate o problema.',
3596
3607
  'settings.rail.title': 'Barra de ações',
3597
3608
  'settings.rail.hint': 'Marque as ações exibidas na barra. A ordem é fixa; desmarcar oculta uma ação.',
3598
3609
  'settings.rail.unavailable': 'Este host não tem serviço de configurações; use «Configurações da barra de ações» no painel.',
@@ -3920,6 +3931,7 @@ window.__ModuleLoader__.load({
3920
3931
  'lf.truncated': 'Mostrate le prime {count} righe',
3921
3932
  'preview.title': 'Anteprima Git',
3922
3933
  'preview.hint': 'Fai clic su «Mostra diff» su un file o un commit: il diff si apre qui, nella barra laterale.',
3934
+ 'preview.boundary': 'Impossibile visualizzare l\'anteprima — ricarica la pagina o segnala il problema.',
3923
3935
  'settings.rail.title': 'Barra delle azioni',
3924
3936
  'settings.rail.hint': 'Seleziona le azioni mostrate sulla barra. L’ordine è fisso; deselezionando se ne nasconde una.',
3925
3937
  'settings.rail.unavailable': 'Questo host non ha un servizio di impostazioni; usa «Impostazioni barra azioni» nel pannello.',
@@ -4247,6 +4259,7 @@ window.__ModuleLoader__.load({
4247
4259
  'lf.truncated': 'Eerste {count} regels worden weergegeven',
4248
4260
  'preview.title': 'Git-voorbeeld',
4249
4261
  'preview.hint': 'Klik op „Diff tonen“ bij een bestand of commit — de diff opent hier in de zijbalk.',
4262
+ 'preview.boundary': 'De voorvertoning kon niet worden weergegeven — herlaad de pagina of meld het probleem.',
4250
4263
  'settings.rail.title': 'Actiebalk',
4251
4264
  'settings.rail.hint': 'Vink de acties aan die op de balk verschijnen. De volgorde ligt vast; uitvinken verbergt er een.',
4252
4265
  'settings.rail.unavailable': 'Deze host heeft geen instellingenservice; gebruik "Actiebalk-instellingen" in het paneel.',
@@ -4574,6 +4587,7 @@ window.__ModuleLoader__.load({
4574
4587
  'lf.truncated': 'Pokazano pierwsze {count} wierszy',
4575
4588
  'preview.title': 'Podgląd Git',
4576
4589
  'preview.hint': 'Kliknij „Pokaż różnice” przy pliku lub commicie — różnice otworzą się tutaj, na pasku bocznym.',
4590
+ 'preview.boundary': 'Nie udało się wyświetlić podglądu — odśwież stronę lub zgłoś problem.',
4577
4591
  'settings.rail.title': 'Pasek akcji',
4578
4592
  'settings.rail.hint': 'Zaznacz akcje widoczne na pasku. Kolejność jest stała; odznaczenie ukrywa akcję.',
4579
4593
  'settings.rail.unavailable': 'Ten host nie ma usługi ustawień; użyj „Ustawienia paska akcji” w panelu.',
@@ -4901,6 +4915,7 @@ window.__ModuleLoader__.load({
4901
4915
  'lf.truncated': 'Visar de första {count} raderna',
4902
4916
  'preview.title': 'Git-förhandsvisning',
4903
4917
  'preview.hint': 'Klicka på ”Visa diff” på en fil eller en commit — diffen öppnas här i sidopanelen.',
4918
+ 'preview.boundary': 'Förhandsvisningen kunde inte visas — ladda om sidan eller rapportera problemet.',
4904
4919
  'settings.rail.title': 'Åtgärdsfältet',
4905
4920
  'settings.rail.hint': 'Markera de åtgärder som visas i fältet. Ordningen är fast; avmarkering döljer en.',
4906
4921
  'settings.rail.unavailable': 'Denna värd saknar inställningstjänst; använd ”Åtgärdsfältets inställningar” i panelen.',
@@ -5228,6 +5243,7 @@ window.__ModuleLoader__.load({
5228
5243
  'lf.truncated': 'Yalnızca ilk {count} satır gösteriliyor',
5229
5244
  'preview.title': 'Git Önizlemesi',
5230
5245
  'preview.hint': 'Bir dosya veya commit üzerinde „Farkı göster”e tıklayın — fark burada, kenar çubuğunda açılır.',
5246
+ 'preview.boundary': 'Önizleme işlenemedi — sayfayı yeniden yükleyin veya sorunu bildirin.',
5231
5247
  'settings.rail.title': 'Eylem çubuğu',
5232
5248
  'settings.rail.hint': 'Çubukta gösterilecek eylemleri işaretleyin. Sıra sabittir; işareti kaldırmak eylemi gizler.',
5233
5249
  'settings.rail.unavailable': 'Bu ana bilgisayarda ayar hizmeti yok; paneldeki "Eylem çubuğu ayarları"nı kullanın.',
@@ -5555,6 +5571,7 @@ window.__ModuleLoader__.load({
5555
5571
  'lf.truncated': 'Menampilkan {count} baris pertama',
5556
5572
  'preview.title': 'Pratinjau Git',
5557
5573
  'preview.hint': 'Klik “Tampilkan diff” pada file atau commit — diff terbuka di sini, di bilah sisi.',
5574
+ 'preview.boundary': 'Pratinjau gagal dirender — muat ulang halaman atau laporkan masalahnya.',
5558
5575
  'settings.rail.title': 'Bilah aksi',
5559
5576
  'settings.rail.hint': 'Centang aksi yang ditampilkan di bilah. Urutannya tetap; menghapus centang menyembunyikannya.',
5560
5577
  'settings.rail.unavailable': 'Host ini tidak punya layanan pengaturan; gunakan "Pengaturan bilah aksi" di panel.',
@@ -5882,6 +5899,7 @@ window.__ModuleLoader__.load({
5882
5899
  'lf.truncated': 'Chỉ hiện {count} dòng đầu',
5883
5900
  'preview.title': 'Xem trước Git',
5884
5901
  'preview.hint': 'Bấm “Xem diff” trên tệp hoặc commit — diff sẽ mở ở đây, trên thanh bên.',
5902
+ 'preview.boundary': 'Không thể hiển thị bản xem trước — hãy tải lại trang hoặc báo cáo sự cố.',
5885
5903
  'settings.rail.title': 'Thanh tác vụ',
5886
5904
  '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ụ đó.',
5887
5905
  '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.',
@@ -6209,6 +6227,7 @@ window.__ModuleLoader__.load({
6209
6227
  'lf.truncated': 'عرض أول {count} سطرًا فقط',
6210
6228
  'preview.title': 'معاينة Git',
6211
6229
  'preview.hint': 'انقر «عرض الفروق» على ملف أو commit — تُفتح الفروق هنا في الشريط الجانبي.',
6230
+ 'preview.boundary': 'تعذّر عرض المعاينة — أعد تحميل الصفحة أو أبلغ عن المشكلة.',
6212
6231
  'settings.rail.title': 'شريط الإجراءات',
6213
6232
  'settings.rail.hint': 'حدّد الإجراءات المعروضة على الشريط. الترتيب ثابت، وإلغاء التحديد يخفي الإجراء.',
6214
6233
  'settings.rail.unavailable': 'لا تتوفر خدمة إعدادات على هذا المضيف؛ استخدم «إعدادات شريط الإجراءات» في اللوحة.',
@@ -6536,6 +6555,7 @@ window.__ModuleLoader__.load({
6536
6555
  'lf.truncated': 'केवल पहली {count} पंक्तियाँ दिखाई जा रही हैं',
6537
6556
  'preview.title': 'Git पूर्वावलोकन',
6538
6557
  'preview.hint': 'फ़ाइल या कमिट पर “अंतर दिखाएँ” पर क्लिक करें — अंतर यहीं साइडबार में खुलेगा।',
6558
+ 'preview.boundary': 'पूर्वावलोकन रेंडर नहीं हो सका — पृष्ठ पुनः लोड करें या समस्या की रिपोर्ट करें।',
6539
6559
  'settings.rail.title': 'क्रिया रेल',
6540
6560
  'settings.rail.hint': 'रेल पर दिखने वाली क्रियाएँ चुनें। क्रम निश्चित है; चुनाव हटाने पर वह छिप जाती है।',
6541
6561
  'settings.rail.unavailable': 'इस होस्ट में सेटिंग सेवा नहीं है; पैनल में «एक्शन रेल सेटिंग» का उपयोग करें।',
@@ -6863,6 +6883,7 @@ window.__ModuleLoader__.load({
6863
6883
  'lf.truncated': 'แสดงเฉพาะ {count} บรรทัดแรก',
6864
6884
  'preview.title': 'ดูตัวอย่าง Git',
6865
6885
  'preview.hint': 'คลิก “แสดงความต่าง” ที่ไฟล์หรือคอมมิต — ความต่างจะเปิดที่นี่ในแถบข้าง',
6886
+ 'preview.boundary': 'แสดงผลตัวอย่างไม่สำเร็จ — โหลดหน้าซ้ำหรือรายงานปัญหา',
6866
6887
  'settings.rail.title': 'แถบการทำงาน',
6867
6888
  'settings.rail.hint': 'ทำเครื่องหมายการทำงานที่จะแสดงบนแถบ ลำดับคงที่; ยกเลิกเครื่องหมายเพื่อซ่อน',
6868
6889
  'settings.rail.unavailable': 'โฮสต์นี้ไม่มีบริการตั้งค่า ให้ใช้ «ตั้งค่าแถบการทำงาน» ในแผง',
@@ -9095,6 +9116,30 @@ window.__ModuleLoader__.load({
9095
9116
  return E(SeedTooltip, { label: props.label, side: 'bottom', delayMs: 500 }, inner)
9096
9117
  }
9097
9118
 
9119
+ /* Render boundary around the preview body (issue #16): a throw inside the
9120
+ diff rows used to bubble past every plugin component into the host's
9121
+ slot boundary, which unmounts the whole tab — the user sees a silently
9122
+ blank "Git 预览" pane. With the boundary the panel keeps its chrome and
9123
+ shows one line of explanation instead. React only supports error
9124
+ boundaries as classes; this stays a module-level pure class (no hooks,
9125
+ no context) so the no-build client rules are untouched. */
9126
+ class RenderBoundary extends React.Component {
9127
+ constructor(props) {
9128
+ super(props)
9129
+ this.state = { failed: false }
9130
+ }
9131
+ static getDerivedStateFromError() {
9132
+ return { failed: true }
9133
+ }
9134
+ componentDidCatch(error) {
9135
+ try { console.error('[dsh-ide-git] preview render failed:', error) } catch (_) { /* diagnostics must never rethrow */ }
9136
+ }
9137
+ render() {
9138
+ if (this.state.failed === true) return E('div', { className: 'dig-preview-boundary' }, this.props.message)
9139
+ return this.props.children
9140
+ }
9141
+ }
9142
+
9098
9143
  /* The external preview's diff view: the official review's header (file
9099
9144
  selector + counts + split/wrap/open tools) over the official row
9100
9145
  anatomy, fed by the plugin's own patch text instead of the host's
@@ -9200,10 +9245,18 @@ window.__ModuleLoader__.load({
9200
9245
  ? E(SplitColumns, { hunks: cut.hunks, highlights: cut.maps })
9201
9246
  : cut.hunks.map((hunk, position) => {
9202
9247
  const paired = split === true ? splitRows(hunk) : null
9248
+ // The wrapped branch must iterate the SAME row list it indexes
9249
+ // (issue #16): splitRows pairs del/add runs, so its length is
9250
+ // max(dels, adds) per run — strictly less than hunkRows'
9251
+ // per-line count whenever anything pairs. Indexing paired with
9252
+ // hunkRows' indices handed SplitLine an undefined row and took
9253
+ // the whole panel down (no boundary above it). Each paired
9254
+ // entry is already { left, right } — exactly SplitLine's shape.
9255
+ const rows = split === true ? paired : hunkRows(hunk)
9203
9256
  return E('section', { key: position, className: 'dig-lf-hunk' },
9204
9257
  E('div', { className: 'dig-lf-hunkHeader', 'data-diff-hunk-header': '' }, hunkHeader(hunk)),
9205
- hunkRows(hunk).map((row, at) => split === true
9206
- ? E(SplitLine, { key: at, row: paired[at], spans: cut.maps[position] })
9258
+ rows.map((row, at) => split === true
9259
+ ? E(SplitLine, { key: at, row: row, spans: cut.maps[position] })
9207
9260
  : E(UnifiedLine, { key: at, row: row, spans: cut.maps[position] })))
9208
9261
  })))
9209
9262
  }
@@ -9751,6 +9804,24 @@ window.__ModuleLoader__.load({
9751
9804
  /* A host that serves the document but refuses writes gets disabled switches
9752
9805
  and a sentence, not controls that silently do nothing. */
9753
9806
  const readOnly = snapshot.writable !== true
9807
+ /* The three placement switches (conversation tab / immersive / composer
9808
+ pill) share one target rule: the control's own answer wins; otherwise
9809
+ the target is the INVERSE of what is on screen — the synchronous intent
9810
+ when the user already touched the field, the accepted document
9811
+ otherwise. The parentheses are load-bearing: `cond ? a : b !== false`
9812
+ parses as `cond ? a : (b !== false)`, so the old spelling of the
9813
+ fallback (`: values[field] !== false) !== true`) made a label click a
9814
+ silent no-op — it recomputed the CURRENT value and wrote it back
9815
+ unchanged (v0.19.6 user report, fixed in v0.19.7). Keep the inversion
9816
+ on the whole expression, never on its last operand. */
9817
+ const placementShown = (field) => (Object.prototype.hasOwnProperty.call(intent.current, field)
9818
+ ? intent.current[field] === true
9819
+ : values[field] !== false)
9820
+ const placementTarget = (field, fromControl) => {
9821
+ if (fromControl !== null) return fromControl
9822
+ const current = placementShown(field)
9823
+ return current !== true
9824
+ }
9754
9825
  return wrapCard(E('div', { className: 'dig-settings dig-card-body', 'data-settings-status': status, 'data-settings-readonly': readOnly === true ? 'true' : 'false' },
9755
9826
  E('div', { className: readOnly === true ? 'dig-settings-note' : 'dig-settings-note dig-settings-note-hidden', 'data-settings-readonly-note': '' }, t('settings.rail.readonly')),
9756
9827
  note === '' ? null : E('div', { className: 'dig-settings-error', 'data-settings-error': '' }, note),
@@ -9815,20 +9886,14 @@ window.__ModuleLoader__.load({
9815
9886
  (() => {
9816
9887
  const field = 'conversationTab'
9817
9888
  const inputId = 'dig-placement-' + field
9818
- const shown = Object.prototype.hasOwnProperty.call(intent.current, field)
9819
- ? intent.current[field] === true
9820
- : values[field] !== false
9889
+ const shown = placementShown(field)
9821
9890
  const toggle = (event) => {
9822
9891
  if (readOnly === true) { setNote(t('settings.rail.readonly')); return }
9823
9892
  const fromControl = event !== undefined && event !== null && event.target !== null
9824
9893
  && event.target !== undefined && typeof event.target.checked === 'boolean'
9825
9894
  ? event.target.checked
9826
9895
  : null
9827
- const wanted = fromControl !== null
9828
- ? fromControl
9829
- : (Object.prototype.hasOwnProperty.call(intent.current, field)
9830
- ? intent.current[field] !== true
9831
- : values[field] !== false) !== true
9896
+ const wanted = placementTarget(field, fromControl)
9832
9897
  intent.current[field] = wanted
9833
9898
  dirty.current.set(field, wanted)
9834
9899
  setPending((now) => Object.assign({}, now, { [field]: wanted }))
@@ -9859,20 +9924,14 @@ window.__ModuleLoader__.load({
9859
9924
  same config). */
9860
9925
  const field = 'immersive'
9861
9926
  const inputId = 'dig-placement-' + field
9862
- const shown = Object.prototype.hasOwnProperty.call(intent.current, field)
9863
- ? intent.current[field] === true
9864
- : values[field] !== false
9927
+ const shown = placementShown(field)
9865
9928
  const toggle = (event) => {
9866
9929
  if (readOnly === true) { setNote(t('settings.rail.readonly')); return }
9867
9930
  const fromControl = event !== undefined && event !== null && event.target !== null
9868
9931
  && event.target !== undefined && typeof event.target.checked === 'boolean'
9869
9932
  ? event.target.checked
9870
9933
  : null
9871
- const wanted = fromControl !== null
9872
- ? fromControl
9873
- : (Object.prototype.hasOwnProperty.call(intent.current, field)
9874
- ? intent.current[field] !== true
9875
- : values[field] !== false) !== true
9934
+ const wanted = placementTarget(field, fromControl)
9876
9935
  intent.current[field] = wanted
9877
9936
  dirty.current.set(field, wanted)
9878
9937
  setPending((now) => Object.assign({}, now, { [field]: wanted }))
@@ -9902,20 +9961,14 @@ window.__ModuleLoader__.load({
9902
9961
  disappears live. Default ON. */
9903
9962
  const field = 'composerStatus'
9904
9963
  const inputId = 'dig-placement-' + field
9905
- const shown = Object.prototype.hasOwnProperty.call(intent.current, field)
9906
- ? intent.current[field] === true
9907
- : values[field] !== false
9964
+ const shown = placementShown(field)
9908
9965
  const toggle = (event) => {
9909
9966
  if (readOnly === true) { setNote(t('settings.rail.readonly')); return }
9910
9967
  const fromControl = event !== undefined && event !== null && event.target !== null
9911
9968
  && event.target !== undefined && typeof event.target.checked === 'boolean'
9912
9969
  ? event.target.checked
9913
9970
  : null
9914
- const wanted = fromControl !== null
9915
- ? fromControl
9916
- : (Object.prototype.hasOwnProperty.call(intent.current, field)
9917
- ? intent.current[field] !== true
9918
- : values[field] !== false) !== true
9971
+ const wanted = placementTarget(field, fromControl)
9919
9972
  intent.current[field] = wanted
9920
9973
  dirty.current.set(field, wanted)
9921
9974
  setPending((now) => Object.assign({}, now, { [field]: wanted }))
@@ -14544,10 +14597,15 @@ window.__ModuleLoader__.load({
14544
14597
  vars, which are absent when that feature is off — the seat inherits
14545
14598
  guaranteed-invalid values, and var() fallbacks do NOT rescue IVCT, so the
14546
14599
  whole background chain below resolves to transparent. Re-declaring the two
14547
- aliases from the official static token (the exact source the light theme's
14548
- own alias defs point at, theme-correct in dark too) makes the chain valid
14549
- again regardless of any-bg's injection state. */
14600
+ aliases from the official static token — the exact source the LIGHT
14601
+ theme's own alias defs point at — makes the chain valid again regardless
14602
+ of any-bg's injection state. static never varies by theme, so this only
14603
+ reproduces the LIGHT surface; the dark override right below re-pins the
14604
+ same aliases to the dark theme's own sources (layer-1→bluish-875,
14605
+ layer-2→bluish-850). Without that override every dark theme renders
14606
+ white surfaces under light text here (issue #15, 1.13:1). */
14550
14607
  '.dig-selection-bar{--dsw-alias-bg-layer-1:var(--dsw-static-neutral-bluish-00,#fff);--dsw-alias-bg-layer-2:var(--dsw-static-neutral-bluish-00,#fff);display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:5px 10px;margin:6px 0;font-size:11px;line-height:16px;border-radius:var(--dsw-radius-sm,8px);background:var(--dsh-any-op-menu-cordis,var(--dsw-alias-bg-layer-2));border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-subtle,transparent));box-shadow:var(--dsw-elevation-prominent,0 4px 14px rgba(0,0,0,.25))}',
14608
+ 'body[data-ds-dark-theme] .dig-selection-bar{--dsw-alias-bg-layer-1:var(--dsw-static-neutral-bluish-875,#232324);--dsw-alias-bg-layer-2:var(--dsw-static-neutral-bluish-850,#2c2c2e)}',
14551
14609
  '.dig-selection-bar .dig-link{margin-left:0;padding:2px 0}',
14552
14610
  '.dig-selection-bar .dig-link-danger{color:var(--dsw-alias-state-error-primary)}',
14553
14611
  '.dig-selection-count{color:var(--dsw-alias-label-secondary);white-space:nowrap}',
@@ -14694,6 +14752,7 @@ window.__ModuleLoader__.load({
14694
14752
  '.dig-lf-root{--diff-empty-fill:color-mix(in srgb,var(--dsw-alias-interactive-bg-hover) 50%,transparent);display:flex;flex-direction:column;box-sizing:border-box;width:100%;min-height:0;color:var(--dsw-alias-label-primary)}',
14695
14753
  '.dig-lf-header{display:flex;flex:0 0 auto;gap:6px;align-items:center;box-sizing:border-box;min-height:38px;padding:0 6px 0 8px;border-bottom:0.5px solid var(--dsw-alias-border-l3)}',
14696
14754
  '.dig-lf-status{display:flex;gap:12px;align-items:center;margin:0;padding:16px;font-size:13px;color:var(--dsw-alias-label-secondary)}',
14755
+ '.dig-preview-boundary{display:flex;align-items:center;justify-content:center;margin:0;padding:24px 16px;font-size:13px;color:var(--dsw-alias-label-secondary);text-align:center}',
14697
14756
  '.dig-lf-body{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;overflow:auto;padding:8px 0 16px;font:var(--dsw-font-markdown-code-block)}',
14698
14757
  '.dig-lf-columns{display:grid;flex:1 1 auto;grid-template-columns:minmax(0,1fr) minmax(0,1fr);min-height:0}',
14699
14758
  '.dig-lf-column,.dig-lf-body[data-review-view=unified]:not([data-review-wrap]){display:grid;grid-template-columns:minmax(max-content,100%);align-content:start}',
@@ -14761,8 +14820,12 @@ window.__ModuleLoader__.load({
14761
14820
  // surface sampling misses and that remap would otherwise IVCT to transparent).
14762
14821
  // --dsh-any-op-menu-cordis at the input opacity tier, plus its input blur rule), so fill
14763
14822
  // opacity and frost follow the composer card exactly. Without that plugin the
14764
- // theme-owned menu material applies.
14823
+ // theme-owned menu material applies. The light re-declaration on the rule below
14824
+ // pins the LIGHT static surface (see the .dig-selection-bar note); dark themes
14825
+ // need the matching override right after it, or the menu renders white under
14826
+ // light text (issue #15).
14765
14827
  '.dig-cs-menu{--dsw-alias-bg-layer-1:var(--dsw-static-neutral-bluish-00,#fff);--dsw-alias-bg-layer-2:var(--dsw-static-neutral-bluish-00,#fff);position:absolute;bottom:calc(100% + 6px);left:0;z-index:60;min-width:220px;max-width:320px;max-height:320px;overflow:auto;padding:6px;border-radius:var(--dsw-radius-lg,12px);background:var(--dsh-any-op-menu-cordis,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-elevation-prominent,var(--dsw-elevation-dimmed,0 8px 24px rgba(0,0,0,.2)));color:var(--dsw-alias-label-secondary);font-size:12px;line-height:20px}',
14828
+ 'body[data-ds-dark-theme] .dig-cs-menu{--dsw-alias-bg-layer-1:var(--dsw-static-neutral-bluish-875,#232324);--dsw-alias-bg-layer-2:var(--dsw-static-neutral-bluish-850,#2c2c2e)}',
14766
14829
  '.dig-cs-item{display:flex;align-items:center;gap:8px;width:100%;padding:5px 10px;border:none;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:13px;line-height:20px;cursor:pointer;text-align:left}',
14767
14830
  '.dig-cs-item:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}',
14768
14831
  '.dig-cs-item:disabled{opacity:.5;cursor:default}',
@@ -15530,14 +15593,14 @@ window.__ModuleLoader__.load({
15530
15593
  ? E('div', { className: 'dig-preview-body' }, E('div', { className: 'dig-empty' }, t('preview.hint')))
15531
15594
  : state.error !== null
15532
15595
  ? E('div', { className: 'dig-preview-body' }, E('div', { className: 'dig-empty' }, state.error))
15533
- : E('div', { className: 'dig-preview-body' }, E(LineDiff, {
15596
+ : E('div', { className: 'dig-preview-body' }, E(RenderBoundary, { message: t('preview.boundary') }, E(LineDiff, {
15534
15597
  patch: state.patch,
15535
15598
  loading: state.loading,
15536
15599
  t: t,
15537
15600
  /* worktree seeds carry the file path: the review's open-file
15538
15601
  tool jumps to the host's whole-file preview. */
15539
15602
  onOpenFile: seed.path !== undefined && seed.path !== null && seed.path !== '' ? openInFilePreview : undefined,
15540
- })))
15603
+ }))))
15541
15604
  }
15542
15605
 
15543
15606
  function apply(ctx) {
package/src/index.js CHANGED
@@ -77,6 +77,13 @@ function railConfigSchema(schema) {
77
77
  does not mark volatile ("Config field … is not volatile"), and the settings
78
78
  switch then rolls back silently. */
79
79
  shape.immersive = live(schema.boolean().default(true))
80
+ /* Composer status pill (v0.19.0): the settings card's "输入框 Git 状态" switch
81
+ and the pill itself read this field. v0.19.0-v0.19.6 shipped the switch and
82
+ the reader WITHOUT this declaration, so the Host rejected every write with
83
+ `Config field "composerStatus" is not volatile` — the card rolled the switch
84
+ back and said "设置没写进去". Any new row-Config field the client writes MUST
85
+ be declared (and volatile) here, or the write is refused before it lands. */
86
+ shape.composerStatus = live(schema.boolean().default(true))
80
87
  return schema.object(shape)
81
88
  }
82
89