dsh-ide-git 0.22.0 → 0.23.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/README.md CHANGED
@@ -104,7 +104,7 @@ DSH 自带的 Git 面板覆盖「暂存 / 提交 / 还原 / 看历史」;`dsh-id
104
104
  | 提交列表 | 列序对齐 IDEA:**日期 → 提交人 → 图谱 → 分支标签 → 提交信息**;lane 拓扑图整格连线(相邻行像素对齐)、HEAD 空心点、refs 徽章(HEAD 绿 / 本地蓝 / 远程紫 / 标签黄,最多 3 个 + `+n`)、加载更多/无限滚动(每页 120)、三列表框选批量选中 |
105
105
  | 提交右键 | 提交详情、复制修订号、签出该修订(确认)、在此新建分支、新建标签、cherry-pick、还原提交、重置到此(保留 / 丢弃更改)、发送到对话(短哈希 + 提交信息 + 仓库) |
106
106
  | 提交详情 | 完整提交信息、作者/日期、变更文件列表(+/−),点文件看逐行 diff |
107
- | diff 窗 | DSH 自家 DiffBlock 渲染(红绿底 + 复制/换行工具栏)、高度可拖拽调节并按位置记忆;**选中 diff 行右键**把行级引用发进对话——`@文件` + 第 N–M 行 + 提交(短哈希/标题/当前分支)或工作区(已暂存/未提交)上下文,删除行标注旧行号,选区定位不到行时发选中片段(截断 600 字符),另有「复制选中引用」 |
107
+ | diff 窗 | 内嵌逐行 diff(红绿底 + **左右分栏 / 自动换行**开关 + 复制工具栏)、高度可拖拽调节并按位置记忆;默认在面板内打开,装了 dsh-better-sidebar 或原生预览座位也不会被顶掉——设置卡「面板放置」里可关掉「在面板内预览差异」,交回侧边栏 diff。**选中 diff 行右键**把行级引用发进对话——`@文件` + 第 N–M 行 + 提交(短哈希/标题/当前分支)或工作区(已暂存/未提交)上下文,删除行标注旧行号,选区定位不到行时发选中片段(截断 600 字符),另有「复制选中引用」 |
108
108
  | 变更列表 | 冲突 / 已暂存 / 更改 / 未跟踪 四组,行内 暂存 / 取消暂存 / 丢弃,分组级「全部暂存 / 全部取消暂存」,点击行看 diff |
109
109
  | 提交框 | 多行提交信息、`Ctrl+Enter`(macOS `Cmd+Enter`)提交、修补上次提交(`--amend`) |
110
110
  | 文件右键 | 显示差异、暂存/取消暂存、丢弃更改、复制路径、发送到对话(`@文件` 引用 chip 或文件路径) |
@@ -128,7 +128,8 @@ dsh plugin --profile web add dsh-better-sidebar
128
128
 
129
129
  ### 兼容性
130
130
 
131
- - `engines.dsh` 与 `@deepseek-ai/dsh` 的 peerDependencies 都声明为 **`>=0.2.2-alpha.2`**:本版跟随 dsh 0.2.2-alpha.2 的会话列表行尾座位与模型徽章字段,pre-release 需要显式写出起点才匹配。范围是**开放下界、不封顶**,之后的 rc / alpha 预发布都算。dsh 会强制执行 peer 里的 `@deepseek-ai/dsh*` 范围(不满足就不加载),`engines` 是给包管理器与排障看的第二份声明。
131
+ - `engines.dsh` 与 `@deepseek-ai/dsh` 的 peerDependencies 都声明为 **`>=0.1.6-alpha.2`**(插件详情页设置卡与面板配置座位的起始版本)。范围是**开放下界、不封顶**,之后的 rc / alpha 预发布都算。dsh 会强制执行 peer 里的 `@deepseek-ai/dsh*` 范围(不满足就不加载),`engines` 是给包管理器与排障看的第二份声明。
132
+ - 新宿主字段一律**能力探测、不写死版本**:0.2.2-alpha.2 起才有的模型徽章数据,宿主提供就显示、没有就保持旧外观;工作树视图只调用本地 `git worktree`,不依赖新宿主 API;宿主不提供行配置(0.1.7 以前)时设置卡显示不可用,内嵌 diff 预览保持默认开启。
132
133
  - 只依赖三样长期契约:宿主半的 `webServer.register({ kind: 'prefix' })` 路由、客户端半的 `window.__ModuleLoader__.load({ id, factory })`、以及客户端的 `slots` 服务。**不引用 `ui-primitives` 图标集**(图标全是自带 SVG),所以宿主删改图标不会波及本插件。
133
134
  - 底座是**可选** peer:没装、装坏了、或它自己要等上游修,都不影响本插件挂到原生右侧栏。
134
135
 
package/README_EN.md CHANGED
@@ -57,7 +57,7 @@ Everything in the panel can be right-clicked into the **current session's compos
57
57
  - **File** (change rows and commit-detail file rows) → a `@path` file reference chip (DSH's native @ grammar, quoted paths included) or just the raw path;
58
58
  - **Selected diff lines**: select some text in a diff window (opened from a commit detail or the working tree), right-click → **Send selected lines to chat**: an `@file` chip plus a line note — `lines N–M (commit 47ad017 "Subject" @ branch)` for a commit, `lines N–M (worktree, staged / uncommitted)` for the working tree. Numbers are reconstructed from the diff rows under the selection against the patch's hunk headers, in **new-file numbering**; a purely deleted span reports OLD numbers and says so. If the selection cannot be pinned to diff rows the menu degrades to **Send selected snippet to chat** (text truncated at 600 characters) instead of inventing numbers, and a **Copy the selection reference** item is always there. With no selection the browser's own menu stays.
59
59
 
60
- Also in place: change-row and detail-file-row icons are tinted by extension (DSH's own FileTypeIcon); diff previews render through DSH's own DiffBlock (red/green rows, copy / wrap toolbar, host-consistent visuals); the diff window's height is draggable and remembered per placement; and the whole panel can live in the **main conversation area** — the Git tab next to Dialogue / Trace, switchable in the plugin settings.
60
+ Also in place: change-row and detail-file-row icons are tinted by extension (DSH's own FileTypeIcon); diff previews render inline (red/green rows, split / wrap switches, copy toolbar) and stay inline by default even when dsh-better-sidebar or the native preview seat is installed — the settings card can hand them back to the sidebar; the diff window's height is draggable and remembered per placement; and the whole panel can live in the **main conversation area** — the Git tab next to Dialogue / Trace, switchable in the plugin settings.
61
61
 
62
62
  ## Where it mounts: two hosts, one panel
63
63
 
@@ -92,7 +92,7 @@ The panel never guesses: it measures itself with a `ResizeObserver` (width ≥ 6
92
92
  - Commit list: IDEA column order — **date → author → graph → refs → subject**; a lane graph whose segments span whole rows (pixel-aligned across rows), a hollow dot for HEAD, refs badges (HEAD green / local blue / remote violet / tag amber, at most three plus `+n`), load more (120 per page).
93
93
  - Commit context menu: details, copy revision, checkout revision, new branch here, new tag, cherry-pick, revert, reset here (keep / discard changes), send to chat (short hash + message + repo).
94
94
  - Commit details: full message, author/date, changed files with +/− and per-file line diff.
95
- - Diff pane: rendered with DSH's own DiffBlock (red/green rows plus a copy / wrap toolbar), draggable height remembered per placement; **select diff rows and right-click** to send a line-level reference into the conversation — an `@file` chip plus the line range with commit (hash / subject / current branch) or worktree (staged / uncommitted) context; deleted rows are labelled as old-numbered, selections that cannot be pinned to rows degrade to the raw snippet (truncated at 600 characters), and a copy-reference item is always available.
95
+ - Diff pane: inline line diff (red/green rows, **split / wrap** switches, copy toolbar), opened inline by default so installing dsh-better-sidebar cannot take those controls away — turn off "Preview diffs in the panel" in the settings card to hand previews back to the sidebar; draggable height remembered per placement; **select diff rows and right-click** to send a line-level reference into the conversation — an `@file` chip plus the line range with commit (hash / subject / current branch) or worktree (staged / uncommitted) context; deleted rows are labelled as old-numbered, selections that cannot be pinned to rows degrade to the raw snippet (truncated at 600 characters), and a copy-reference item is always available.
96
96
  - Changes: conflicts / staged / changes / untracked groups, inline stage / unstage / discard, group-level actions, click for diff.
97
97
  - Commit box: multi-line message, Ctrl+Enter (Cmd+Enter) to commit, amend. The AI commit-message model picker also shows each model's listing badge, localized to the active interface language.
98
98
  - File context menu: show diff, stage / unstage, discard changes, copy path, send to chat (an `@file` reference chip or the raw path).
@@ -115,7 +115,8 @@ Restart `dsh web`, then:
115
115
 
116
116
  ### Compatibility
117
117
 
118
- - `engines.dsh` and the `@deepseek-ai/dsh` peerDependency are both **`>=0.2.2-alpha.2`**: this release follows the 0.2.2-alpha.2 session-row trailing seat and model badge fields, and a prerelease needs an explicit floor to match. The range is an **open floor with no ceiling**, so later rc / alpha prereleases are included. DSH enforces the peer range (an unsatisfied one keeps the plugin from loading); `engines` is the second declaration for package managers and diagnostics.
118
+ - `engines.dsh` and the `@deepseek-ai/dsh` peerDependency are both **`>=0.1.6-alpha.2`** (the plugin detail-page settings card and panel-config seat). The range is an **open floor with no ceiling**, so later rc / alpha prereleases are included. DSH enforces the peer range (an unsatisfied one keeps the plugin from loading); `engines` is the second declaration for package managers and diagnostics.
119
+ - New host fields are **capability-detected, never version-sniffed**: model badge data (0.2.2-alpha.2+) is shown when the host serves it and the old look stays otherwise; the worktree view only calls the local `git worktree`, with no new host API; on hosts without row Config (before 0.1.7) the settings card reports unavailable and the inline diff preview stays on by default.
119
120
  - Three long-lived contracts, nothing else: the host's `webServer.register({ kind: 'prefix' })` route, the client's `window.__ModuleLoader__.load({ id, factory })`, and the client `slots` service. It does **not** import the `ui-primitives` icon set (every icon is an inline SVG), so host icon changes cannot reach it.
120
121
  - better-sidebar is an **optional** peer: absent, broken, or waiting on its own upstream fix, the plugin still mounts in the native right sidebar.
121
122
 
package/dsh.plugin.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-ide-git",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "main": "./src/index.js",
5
5
  "description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
6
6
  "engines": {
7
- "dsh": ">=0.2.2-alpha.2"
7
+ "dsh": ">=0.1.6-alpha.2"
8
8
  },
9
9
  "contributes": {
10
10
  "tools": [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ide-git",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
5
5
  "type": "module",
6
6
  "repository": {
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "engines": {
21
21
  "node": ">=18",
22
- "dsh": ">=0.2.2-alpha.2"
22
+ "dsh": ">=0.1.6-alpha.2"
23
23
  },
24
24
  "dsh": {
25
25
  "bundle": {
@@ -66,7 +66,7 @@
66
66
  "license": "MIT",
67
67
  "peerDependencies": {
68
68
  "@deepseek-ai/cordis": "^4.0.1",
69
- "@deepseek-ai/dsh": ">=0.2.2-alpha.2",
69
+ "@deepseek-ai/dsh": ">=0.1.6-alpha.2",
70
70
  "dsh-better-sidebar": ">=0.12.0"
71
71
  },
72
72
  "peerDependenciesMeta": {
package/src/client.js CHANGED
@@ -315,6 +315,8 @@ window.__ModuleLoader__.load({
315
315
  'settings.placement.tabHint': '关闭后仍可从右侧栏或底部面板打开。',
316
316
  'settings.placement.immersive': '沉浸模式(占满全高,隐藏输入框)',
317
317
  'settings.placement.immersiveHint': '开启后 Git 标签页占满整个会话区高度,输入框自动收起;审批与提问仍会正常显示。',
318
+ 'settings.diff.inline': '在面板内预览差异',
319
+ 'settings.diff.inlineHint': '开启后差异在 Git 面板内打开,可使用左右分栏与自动换行;关闭后交给侧边栏的 diff 预览。',
318
320
  'preview.openFile': '打开文件预览',
319
321
  'lf.added': '+{count}',
320
322
  'lf.deleted': '-{count}',
@@ -681,6 +683,8 @@ window.__ModuleLoader__.load({
681
683
  'settings.placement.tabHint': 'Off, the panel stays reachable from the right sidebar and the bottom panel.',
682
684
  'settings.placement.immersive': 'Immersive mode (full height, hidden composer)',
683
685
  'settings.placement.immersiveHint': 'On, the Git tab fills the whole conversation area and the composer steps aside; approvals and questions still show.',
686
+ 'settings.diff.inline': 'Preview diffs in the panel',
687
+ 'settings.diff.inlineHint': 'On, diffs open inside the Git panel with split view and line wrap. Off, the sidebar\'s diff preview takes over.',
684
688
  'preview.openFile': 'Open in the file preview',
685
689
  'lf.added': '+{count}',
686
690
  'lf.deleted': '-{count}',
@@ -1055,6 +1059,8 @@ window.__ModuleLoader__.load({
1055
1059
  'settings.placement.tabHint': '關閉之後仍可從右側欄或底部面板開啟。',
1056
1060
  'settings.placement.immersive': '沉浸模式(佔滿全高,隱藏輸入框)',
1057
1061
  'settings.placement.immersiveHint': '開啟之後 Git 分頁會佔滿整個會話區高度,輸入框自動收起;審批與提問仍會正常顯示。',
1062
+ 'settings.diff.inline': '喺面板內預覽差異',
1063
+ 'settings.diff.inlineHint': '開啟之後差異喺 Git 面板內開啟,可以用左右分欄同自動換行;關閉之後交畀側邊欄嘅 diff 預覽。',
1058
1064
  'preview.openFile': '開啟檔案預覽',
1059
1065
  'lf.added': '+{count}',
1060
1066
  'lf.deleted': '-{count}',
@@ -1421,6 +1427,8 @@ window.__ModuleLoader__.load({
1421
1427
  'settings.placement.tabHint': '關閉之後仍可從右側欄或底部面板開啟。',
1422
1428
  'settings.placement.immersive': '沉浸模式(佔滿全高,隱藏輸入框)',
1423
1429
  'settings.placement.immersiveHint': '開啟之後 Git 分頁會佔滿整個對話區高度,輸入框自動收起;審核與提問仍會正常顯示。',
1430
+ 'settings.diff.inline': '在面板內預覽差異',
1431
+ 'settings.diff.inlineHint': '開啟後差異在 Git 面板內開啟,可使用左右分欄與自動換行;關閉後交給側邊欄的 diff 預覽。',
1424
1432
  'preview.openFile': '開啟檔案預覽',
1425
1433
  'lf.added': '+{count}',
1426
1434
  'lf.deleted': '-{count}',
@@ -1787,6 +1795,8 @@ window.__ModuleLoader__.load({
1787
1795
  'settings.placement.tabHint': '關閉之後仍可從右側欄或底部面板開啟。',
1788
1796
  'settings.placement.immersive': '沉浸模式(佔滿全高,隱藏輸入框)',
1789
1797
  'settings.placement.immersiveHint': '開啟之後 Git 分頁會佔滿整個會話區高度,輸入框自動收起;審批與提問仍會正常顯示。',
1798
+ 'settings.diff.inline': '在面板內預覽差異',
1799
+ 'settings.diff.inlineHint': '開啟後差異在 Git 面板內開啟,可使用左右分欄與自動換行;關閉後交給側邊欄的 diff 預覽。',
1790
1800
  'preview.openFile': '開啟檔案預覽',
1791
1801
  'lf.added': '+{count}',
1792
1802
  'lf.deleted': '-{count}',
@@ -2153,6 +2163,8 @@ window.__ModuleLoader__.load({
2153
2163
  'settings.placement.tabHint': 'オフにしても右サイドバーと下部パネルから開けます。',
2154
2164
  'settings.placement.immersive': '没入モード(全高表示・入力欄を非表示)',
2155
2165
  'settings.placement.immersiveHint': 'オンにすると Git タブが会話エリアの高さいっぱいに広がり、入力欄は引っ込みます。承認や質問は通常どおり表示されます。',
2166
+ 'settings.diff.inline': 'パネル内で差分をプレビュー',
2167
+ 'settings.diff.inlineHint': 'オンの場合、差分は Git パネル内で開き、左右分割と折り返しを使用できます。オフの場合はサイドバーの差分プレビューに任せます。',
2156
2168
  'preview.openFile': 'ファイルプレビューを開く',
2157
2169
  'lf.added': '+{count}',
2158
2170
  'lf.deleted': '-{count}',
@@ -2519,6 +2531,8 @@ window.__ModuleLoader__.load({
2519
2531
  'settings.placement.tabHint': '꺼도 오른쪽 사이드바와 하단 패널에서 열 수 있습니다.',
2520
2532
  'settings.placement.immersive': '몰입 모드(전체 높이, 입력창 숨김)',
2521
2533
  'settings.placement.immersiveHint': '켜면 Git 탭이 대화 영역 전체 높이를 차지하고 입력창은 숨겨집니다. 승인과 질문은 정상적으로 표시됩니다.',
2534
+ 'settings.diff.inline': '패널에서 차이 미리보기',
2535
+ 'settings.diff.inlineHint': '켜면 차이가 Git 패널 안에서 열리고 좌우 분할과 자동 줄바꿈을 사용할 수 있습니다. 끄면 사이드바의 차이 미리보기로 넘깁니다.',
2522
2536
  'preview.openFile': '파일 미리보기로 열기',
2523
2537
  'lf.added': '+{count}',
2524
2538
  'lf.deleted': '-{count}',
@@ -2885,6 +2899,8 @@ window.__ModuleLoader__.load({
2885
2899
  'settings.placement.tabHint': 'Ausgeschaltet bleibt das Panel über rechte Seitenleiste und unteres Panel erreichbar.',
2886
2900
  'settings.placement.immersive': 'Immersiver Modus (volle Höhe, ausgeblendetes Eingabefeld)',
2887
2901
  'settings.placement.immersiveHint': 'Eingeschaltet füllt der Git-Tab die gesamte Gesprächshöhe und das Eingabefeld weicht zurück; Freigaben und Rückfragen bleiben sichtbar.',
2902
+ 'settings.diff.inline': 'Diffs im Panel anzeigen',
2903
+ 'settings.diff.inlineHint': 'Ein: Diffs öffnen im Git-Panel, mit geteilter Ansicht und Zeilenumbruch. Aus: Die Diff-Vorschau der Seitenleiste übernimmt.',
2888
2904
  'preview.openFile': 'In der Dateivorschau öffnen',
2889
2905
  'lf.added': '+{count}',
2890
2906
  'lf.deleted': '-{count}',
@@ -3251,6 +3267,8 @@ window.__ModuleLoader__.load({
3251
3267
  'settings.placement.tabHint': 'Désactivé, le panneau reste accessible depuis la barre latérale droite et le panneau inférieur.',
3252
3268
  'settings.placement.immersive': 'Mode immersif (pleine hauteur, zone de saisie masquée)',
3253
3269
  'settings.placement.immersiveHint': 'Activé, l’onglet Git occupe toute la hauteur de la conversation et la zone de saisie s’efface ; approbations et questions restent visibles.',
3270
+ 'settings.diff.inline': 'Aperçu des diffs dans le panneau',
3271
+ 'settings.diff.inlineHint': 'Activé, les diffs s’ouvrent dans le panneau Git, avec vue scindée et retour à la ligne. Désactivé, l’aperçu diff de la barre latérale prend le relais.',
3254
3272
  'preview.openFile': 'Ouvrir dans l’aperçu du fichier',
3255
3273
  'lf.added': '+{count}',
3256
3274
  'lf.deleted': '-{count}',
@@ -3617,6 +3635,8 @@ window.__ModuleLoader__.load({
3617
3635
  'settings.placement.tabHint': 'В выключенном виде панель доступна из правой боковой панели и нижней панели.',
3618
3636
  'settings.placement.immersive': 'Полноэкранный режим (во всю высоту, без поля ввода)',
3619
3637
  'settings.placement.immersiveHint': 'Во включённом виде вкладка Git занимает всю высоту области диалога, поле ввода скрывается; подтверждения и вопросы остаются на виду.',
3638
+ 'settings.diff.inline': 'Просмотр различий в панели',
3639
+ 'settings.diff.inlineHint': 'Включено: различия открываются в панели Git с разделённым видом и переносом строк. Выключено: показ переходит к боковой панели.',
3620
3640
  'preview.openFile': 'Открыть в предпросмотре файла',
3621
3641
  'lf.added': '+{count}',
3622
3642
  'lf.deleted': '-{count}',
@@ -3983,6 +4003,8 @@ window.__ModuleLoader__.load({
3983
4003
  'settings.placement.tabHint': 'Desligado, o painel continua disponível na barra lateral direita e no painel inferior.',
3984
4004
  'settings.placement.immersive': 'Modo imersivo (altura total, campo de entrada oculto)',
3985
4005
  'settings.placement.immersiveHint': 'Ativado, a aba Git ocupa toda a altura da conversa e o campo de entrada fica de lado; aprovações e perguntas continuam visíveis.',
4006
+ 'settings.diff.inline': 'Pré-visualizar diferenças no painel',
4007
+ 'settings.diff.inlineHint': 'Ligado, as diferenças abrem no painel Git, com vista dividida e quebra de linha. Desligado, a pré-visualização da barra lateral assume.',
3986
4008
  'preview.openFile': 'Abrir na pré-visualização do arquivo',
3987
4009
  'lf.added': '+{count}',
3988
4010
  'lf.deleted': '-{count}',
@@ -4349,6 +4371,8 @@ window.__ModuleLoader__.load({
4349
4371
  'settings.placement.tabHint': 'Da spento, il pannello resta raggiungibile dalla barra laterale destra e dal pannello inferiore.',
4350
4372
  'settings.placement.immersive': 'Modalità immersiva (altezza piena, input nascosto)',
4351
4373
  'settings.placement.immersiveHint': 'Attiva, la scheda Git occupa tutta l’altezza della conversazione e la barra di input resta nascosta; approvazioni e domande restano visibili.',
4374
+ 'settings.diff.inline': 'Anteprima delle differenze nel pannello',
4375
+ 'settings.diff.inlineHint': 'Attivo: le differenze si aprono nel pannello Git, con vista affiancata e ritorno a capo. Disattivo: subentra l’anteprima della barra laterale.',
4352
4376
  'preview.openFile': 'Apri nell’anteprima del file',
4353
4377
  'lf.added': '+{count}',
4354
4378
  'lf.deleted': '-{count}',
@@ -4715,6 +4739,8 @@ window.__ModuleLoader__.load({
4715
4739
  'settings.placement.tabHint': 'Uitgeschakeld blijft het paneel bereikbaar via de rechterzijbalk en het onderpaneel.',
4716
4740
  'settings.placement.immersive': 'Meeslepende modus (volledige hoogte, verborgen invoerveld)',
4717
4741
  'settings.placement.immersiveHint': 'Aan: het Git-tabblad vult de hele gespreksruimte en het invoerveld wijkt; goedkeuringen en vragen blijven zichtbaar.',
4742
+ 'settings.diff.inline': 'Diffs in het paneel bekijken',
4743
+ 'settings.diff.inlineHint': 'Aan: diffs openen in het Git-paneel, met gesplitste weergave en regelterugloop. Uit: de diff-voorvertoning in de zijbalk neemt het over.',
4718
4744
  'preview.openFile': 'Openen in de bestandsvoorbeeld',
4719
4745
  'lf.added': '+{count}',
4720
4746
  'lf.deleted': '-{count}',
@@ -5081,6 +5107,8 @@ window.__ModuleLoader__.load({
5081
5107
  'settings.placement.tabHint': 'Po wyłączeniu panel pozostaje dostępny z prawego paska bocznego i dolnego panelu.',
5082
5108
  'settings.placement.immersive': 'Tryb immersyjny (pełna wysokość, ukryte pole wpisywania)',
5083
5109
  'settings.placement.immersiveHint': 'Po włączeniu karta Git wypełnia całą wysokość rozmowy, a pole wpisywania się chowa; zatwierdzenia i pytania pozostają widoczne.',
5110
+ 'settings.diff.inline': 'Podgląd różnic w panelu',
5111
+ 'settings.diff.inlineHint': 'Włączone: różnice otwierają się w panelu Git, z widokiem dzielonym i zawijaniem wierszy. Wyłączone: podgląd różnic w pasku bocznym przejmuje.',
5084
5112
  'preview.openFile': 'Otwórz w podglądzie pliku',
5085
5113
  'lf.added': '+{count}',
5086
5114
  'lf.deleted': '-{count}',
@@ -5447,6 +5475,8 @@ window.__ModuleLoader__.load({
5447
5475
  'settings.placement.tabHint': 'Avstängd finns panelen kvar via höger sidopanel och nedre panelen.',
5448
5476
  'settings.placement.immersive': 'Uppslukande läge (full höjd, dolt inmatningsfält)',
5449
5477
  'settings.placement.immersiveHint': 'På: Git-fliken fyller hela konversationsytan och inmatningsfältet träder åt sidan; godkännanden och frågor visas fortfarande.',
5478
+ 'settings.diff.inline': 'Förhandsgranska diffar i panelen',
5479
+ 'settings.diff.inlineHint': 'På: diffar öppnas i Git-panelen med delad vy och radbrytning. Av: sidopanelens diff-förhandsgranskning tar över.',
5450
5480
  'preview.openFile': 'Öppna i filförhandsvisningen',
5451
5481
  'lf.added': '+{count}',
5452
5482
  'lf.deleted': '-{count}',
@@ -5813,6 +5843,8 @@ window.__ModuleLoader__.load({
5813
5843
  'settings.placement.tabHint': 'Kapatıldığında panel sağ kenar çubuğundan ve alt panelden açılabilir.',
5814
5844
  'settings.placement.immersive': 'Odaklanma modu (tam yükseklik, gizli giriş alanı)',
5815
5845
  'settings.placement.immersiveHint': 'Açıkken Git sekmesi tüm konuşma alanını kaplar ve giriş alanı gizlenir; onaylar ve sorular yine de görünür.',
5846
+ 'settings.diff.inline': 'Farkları panelde önizle',
5847
+ 'settings.diff.inlineHint': 'Açık: farklar Git panelinde açılır; bölünmüş görünüm ve satır kaydırma kullanılabilir. Kapalı: kenar çubuğundaki fark önizlemesi devralır.',
5816
5848
  'preview.openFile': 'Dosya önizlemesinde aç',
5817
5849
  'lf.added': '+{count}',
5818
5850
  'lf.deleted': '-{count}',
@@ -6179,6 +6211,8 @@ window.__ModuleLoader__.load({
6179
6211
  'settings.placement.tabHint': 'Dimatikan, panel tetap bisa dibuka dari bilah sisi kanan dan panel bawah.',
6180
6212
  'settings.placement.immersive': 'Mode imersif (tinggi penuh, kolom masukan tersembunyi)',
6181
6213
  'settings.placement.immersiveHint': 'Aktif: tab Git memenuhi seluruh tinggi area percakapan dan kolom masukan mengalah; persetujuan dan pertanyaan tetap tampil.',
6214
+ 'settings.diff.inline': 'Pratinjau diff di panel',
6215
+ 'settings.diff.inlineHint': 'Aktif: diff terbuka di panel Git, dengan tampilan terbagi dan pelipatan baris. Nonaktif: pratinjau diff bilah sisi mengambil alih.',
6182
6216
  'preview.openFile': 'Buka di pratinjau berkas',
6183
6217
  'lf.added': '+{count}',
6184
6218
  'lf.deleted': '-{count}',
@@ -6545,6 +6579,8 @@ window.__ModuleLoader__.load({
6545
6579
  'settings.placement.tabHint': 'Tắt rồi, bảng vẫn mở được từ thanh bên phải và bảng dưới.',
6546
6580
  'settings.placement.immersive': 'Chế độ đắm chìm (chiều cao đầy đủ, ẩn ô nhập)',
6547
6581
  'settings.placement.immersiveHint': 'Bật: thẻ Git chiếm toàn bộ chiều cao vùng hội thoại và ô nhập thu vào; phê duyệt và câu hỏi vẫn hiển thị bình thường.',
6582
+ 'settings.diff.inline': 'Xem trước khác biệt trong bảng',
6583
+ 'settings.diff.inlineHint': 'Bật: khác biệt mở trong bảng Git, có chế độ chia đôi và tự động xuống dòng. Tắt: phần xem trước ở thanh bên đảm nhận.',
6548
6584
  'preview.openFile': 'Mở trong xem trước tệp',
6549
6585
  'lf.added': '+{count}',
6550
6586
  'lf.deleted': '-{count}',
@@ -6911,6 +6947,8 @@ window.__ModuleLoader__.load({
6911
6947
  'settings.placement.tabHint': 'عند الإيقاف تبقى اللوحة متاحة من الشريط الجانبي الأيمن واللوحة السفلية.',
6912
6948
  'settings.placement.immersive': 'الوضع الغامر (ارتفاع كامل، إخفاء صندوق الكتابة)',
6913
6949
  'settings.placement.immersiveHint': 'عند التشغيل يملأ تبويب Git منطقة المحادثة بكامل ارتفاعها ويتراجع صندوق الكتابة؛ تظل الموافقات والأسئلة ظاهرة كالمعتاد.',
6950
+ 'settings.diff.inline': 'معاينة الفروق داخل اللوحة',
6951
+ 'settings.diff.inlineHint': 'عند التشغيل تفتح الفروق داخل لوحة Git مع العرض المنقسم والتفاف الأسطر. عند الإيقاف تتولاها معاينة الفروق في الشريط الجانبي.',
6914
6952
  'preview.openFile': 'افتح في معاينة الملف',
6915
6953
  'lf.added': '+{count}',
6916
6954
  'lf.deleted': '-{count}',
@@ -7277,6 +7315,8 @@ window.__ModuleLoader__.load({
7277
7315
  'settings.placement.tabHint': 'बंद करने पर भी पैनल दाएँ साइडबार और निचले पैनल से खुलता है।',
7278
7316
  'settings.placement.immersive': 'इमर्सिव मोड (पूरी ऊँचाई, छिपा इनपुट बॉक्स)',
7279
7317
  'settings.placement.immersiveHint': 'चालू होने पर Git टैब पूरे संवाद क्षेत्र की ऊँचाई घेर लेता है और इनपुट बॉक्स हट जाता है; अनुमोदन और प्रश्न फिर भी दिखते हैं।',
7318
+ 'settings.diff.inline': 'पैनल में अंतर देखें',
7319
+ 'settings.diff.inlineHint': 'चालू: अंतर Git पैनल में खुलते हैं, स्प्लिट व्यू और लाइन रैप के साथ। बंद: साइडबार का अंतर पूर्वावलोकन संभालता है।',
7280
7320
  'preview.openFile': 'फ़ाइल पूर्वावलोकन में खोलें',
7281
7321
  'lf.added': '+{count}',
7282
7322
  'lf.deleted': '-{count}',
@@ -7643,6 +7683,8 @@ window.__ModuleLoader__.load({
7643
7683
  'settings.placement.tabHint': 'ปิดแล้วยังเปิดแผงจากแถบข้างขวาหรือแผงล่างได้',
7644
7684
  'settings.placement.immersive': 'โหมดดื่มด่ำ (เต็มความสูง, ซ่อนกล่องพิมพ์)',
7645
7685
  'settings.placement.immersiveHint': 'เมื่อเปิด แท็บ Git จะกินพื้นที่สูงทั้งหมดของบทสนทนาและกล่องพิมพ์จะถอยออก; การอนุมัติและคำถามยังแสดงตามปกติ',
7686
+ 'settings.diff.inline': 'ดูตัวอย่างความแตกต่างในแผง',
7687
+ 'settings.diff.inlineHint': 'เปิด: ความแตกต่างเปิดในแผง Git พร้อมมุมมองแยกและตัดบรรทัดอัตโนมัติ ปิด: ตัวอย่างความแตกต่างในแถบข้างจะรับหน้าที่',
7646
7688
  'preview.openFile': 'เปิดในตัวอย่างไฟล์',
7647
7689
  'lf.added': '+{count}',
7648
7690
  'lf.deleted': '-{count}',
@@ -9326,47 +9368,6 @@ window.__ModuleLoader__.load({
9326
9368
  return false
9327
9369
  }
9328
9370
 
9329
- /* One single-file unified diff (what the host `diff` method returns) → the
9330
- one DiffHunk DiffBlock renders, by replaying ` `/`-`/`+` lines back into
9331
- the two sides. No `@@` at all (binary patches, mode-only changes) or a
9332
- pathless header → null, and the caller keeps the hand-drawn rows. */
9333
- function patchToHunk(patch) {
9334
- const text = typeof patch === 'string' ? patch : ''
9335
- if (text === '' || text.indexOf('@@') < 0) return null
9336
- const lines = text.split('\n')
9337
- let path = null
9338
- const oldText = []
9339
- const newText = []
9340
- for (const line of lines) {
9341
- if (line.indexOf('+++ ') === 0) {
9342
- const name = line.slice(4).split('\t')[0]
9343
- if (name !== '/dev/null') path = name.replace(/^b\//, '')
9344
- continue
9345
- }
9346
- if (line.indexOf('--- ') === 0) {
9347
- if (path === null) {
9348
- const name = line.slice(4).split('\t')[0]
9349
- if (name !== '/dev/null') path = name.replace(/^a\//, '')
9350
- }
9351
- continue
9352
- }
9353
- if (line.indexOf('@@') === 0) continue
9354
- if (line.indexOf('+') === 0) { newText.push(line.slice(1)); continue }
9355
- if (line.indexOf('-') === 0) { oldText.push(line.slice(1)); continue }
9356
- if (line.indexOf('\\') === 0) continue
9357
- if (line.indexOf(' ') === 0) { oldText.push(line.slice(1)); newText.push(line.slice(1)); continue }
9358
- // diff --git / index / new file mode / rename from ...: metadata above
9359
- // the first hunk; anything else after it would be malformed.
9360
- continue
9361
- }
9362
- if (path === null || path === '') return null
9363
- // A patch without body lines (mode-only, bare metadata) replays into an
9364
- // empty hunk that DiffBlock draws as nothing at all — keep the hand-drawn
9365
- // rows for those, they at least show what happened.
9366
- if (oldText.length === 0 && newText.length === 0) return null
9367
- return { path: path, oldText: oldText.length === 0 ? null : oldText.join('\n'), newText: newText.join('\n') }
9368
- }
9369
-
9370
9371
  /* ============================== diff ============================== */
9371
9372
 
9372
9373
  function diffLines(patch) {
@@ -9403,13 +9404,13 @@ window.__ModuleLoader__.load({
9403
9404
 
9404
9405
  /* ---- diff selection core
9405
9406
  Pure line-number plumbing for the diff-pane context menu (v0.13.5).
9406
- The host DiffBlock draws rows WITHOUT line-number attributes — a row is
9407
- `<div class={line + kindClass}>{row.text}</div>` (DiffBlock.tsx:204) and
9408
- the `+`/`-`/two-space prefixes live in CSS `::before` content — so the
9409
- menu reconstructs numbers by replaying the ROW KINDS in document order
9410
- from the patch's first hunk header. The hand-drawn rows already carry
9411
- parsed numbers (diffLines above). Kept DOM-free between the markers so
9412
- tests/smoke.mjs can slice the block and drive the real implementation. */
9407
+ For a host DiffBlock body (kept as a forward-compat fallback) rows do
9408
+ not expose numbers, so the menu replays ROW KINDS in document order from
9409
+ the patch's first hunk header; hand-drawn rows carry parsed numbers
9410
+ (diffLines above). The review renderer used today (#22) reads its own
9411
+ printed numbers instead (reviewRowSpanOf, DOM side below). Kept DOM-free
9412
+ between the markers so tests/smoke.mjs can slice the block and drive the
9413
+ real implementation. */
9413
9414
 
9414
9415
  const DIFF_SNIPPET_MAX = 600
9415
9416
 
@@ -9555,13 +9556,13 @@ window.__ModuleLoader__.load({
9555
9556
 
9556
9557
  /* ---------- DOM side of the diff-pane context menu (browser only) ----------
9557
9558
  SSR never calls these: they run inside the pane's onContextMenu, after
9558
- the selection has been captured. The row element under a selection
9559
- endpoint is either a hand-drawn `.dig-diff-line`, or a DiffBlock row —
9560
- a div child of the body whose OWN parent carries the `data-diff`
9561
- marker. `data-diff` is a component attribute (DiffBlock.tsx:199), not a
9562
- hashed CSS-module class, so the lookup does not bet on build-time class
9563
- renaming; the row KIND comes from the `::before` content the kinds are
9564
- painted with, for the same reason. */
9559
+ the selection has been captured. A row under a selection endpoint is
9560
+ either a review row (data-diff-line on .dig-lf-line / .dig-lf-splitLine
9561
+ / .dig-lf-sideLine), a hand-drawn `.dig-diff-line`, or a host DiffBlock
9562
+ row (a div child of the body whose OWN parent carries `data-diff`). The
9563
+ review path reads the printed .dig-lf-number cells; the host path gets
9564
+ its KIND from the `::before` content the rows are painted with. Both
9565
+ rely on component attributes, not hashed CSS-module classes. */
9565
9566
 
9566
9567
  function savedSelectionOf() {
9567
9568
  try {
@@ -9582,6 +9583,15 @@ window.__ModuleLoader__.load({
9582
9583
  if (element.classList !== undefined && element.classList.contains('dig-diff-line') === true) {
9583
9584
  return { kind: 'drawn', row: element, body: element.parentElement }
9584
9585
  }
9586
+ /* The review renderer (LineDiff, used by BOTH the inline pane and the
9587
+ native preview since #22) stamps every row with data-diff-line and
9588
+ carries the real line numbers in .dig-lf-number cells. */
9589
+ if (element.hasAttribute !== undefined && element.hasAttribute('data-diff-line') === true
9590
+ && element.classList !== undefined
9591
+ && (element.classList.contains('dig-lf-line') === true || element.classList.contains('dig-lf-splitLine') === true || element.classList.contains('dig-lf-sideLine') === true)) {
9592
+ const body = element.closest === undefined ? null : element.closest('.dig-lf-body')
9593
+ if (body !== null) return { kind: 'review', row: element, body: body }
9594
+ }
9585
9595
  const parent = element.parentElement
9586
9596
  if (parent !== null && parent.getAttribute !== undefined && parent.hasAttribute('data-diff') === false) {
9587
9597
  const root = parent.parentElement
@@ -9612,6 +9622,49 @@ window.__ModuleLoader__.load({
9612
9622
  return { rows: rows, kinds: kinds }
9613
9623
  }
9614
9624
 
9625
+ /* The review renderer's rows carry their printed numbers already: read
9626
+ them straight off the selected rows (unified: old then new; split with
9627
+ wrap: the two cells; split without wrap: one side per column). This is
9628
+ simpler and more accurate than replaying kinds, and it follows the
9629
+ SPLIT view, where one row is two files' lines. */
9630
+ function reviewRowSpanOf(body, fromRow, toRow) {
9631
+ if (body === null || body === undefined) return null
9632
+ const rows = Array.prototype.slice.call(body.querySelectorAll('[data-diff-line]'))
9633
+ const fromIndex = rows.indexOf(fromRow)
9634
+ const toIndex = rows.indexOf(toRow)
9635
+ if (fromIndex < 0 || toIndex < 0) return null
9636
+ let newFrom = null
9637
+ let newTo = null
9638
+ let oldFrom = null
9639
+ let oldTo = null
9640
+ const read = (node) => {
9641
+ const text = node === undefined || node === null ? '' : String(node.textContent).trim()
9642
+ if (text === '') return null
9643
+ const value = Number.parseInt(text, 10)
9644
+ return Number.isFinite(value) ? value : null
9645
+ }
9646
+ for (let index = Math.min(fromIndex, toIndex); index <= Math.max(fromIndex, toIndex); index += 1) {
9647
+ const row = rows[index]
9648
+ const numbers = row.querySelectorAll('.dig-lf-number')
9649
+ let oldLine = null
9650
+ let newLine = null
9651
+ if (row.classList.contains('dig-lf-sideLine') === true) {
9652
+ const side = row.closest === undefined ? null : row.closest('[data-diff-side]')
9653
+ const value = read(numbers[0])
9654
+ if (side !== null && side.getAttribute('data-diff-side') === 'left') oldLine = value
9655
+ else newLine = value
9656
+ } else {
9657
+ oldLine = read(numbers[0])
9658
+ newLine = read(numbers[1])
9659
+ }
9660
+ if (oldLine !== null) { oldFrom = oldFrom === null ? oldLine : oldFrom; oldTo = oldLine }
9661
+ if (newLine !== null) { newFrom = newFrom === null ? newLine : newFrom; newTo = newLine }
9662
+ }
9663
+ if (newFrom !== null) return { start: newFrom, end: newTo === null ? newFrom : newTo, old: false }
9664
+ if (oldFrom !== null) return { start: oldFrom, end: oldTo === null ? oldFrom : oldTo, old: true }
9665
+ return null
9666
+ }
9667
+
9615
9668
  /* { kind: 'lines', span } for a selection inside ONE diff body, or
9616
9669
  { kind: 'snippet' } when the rows are there but the numbers cannot be
9617
9670
  reconstructed. null (rows missing entirely) means "not a diff
@@ -9623,7 +9676,9 @@ window.__ModuleLoader__.load({
9623
9676
  if (from === null || to === null) return null
9624
9677
  if (from.kind !== to.kind || from.body !== to.body) return { kind: 'snippet', text: saved.text }
9625
9678
  let span = null
9626
- if (from.kind === 'drawn') {
9679
+ if (from.kind === 'review') {
9680
+ span = reviewRowSpanOf(from.body, from.row, to.row)
9681
+ } else if (from.kind === 'drawn') {
9627
9682
  const rows = diffLines(patch)
9628
9683
  const all = from.body === null || from.body === undefined ? [] : Array.prototype.slice.call(from.body.querySelectorAll('.dig-diff-line'))
9629
9684
  const fromIndex = all.indexOf(from.row)
@@ -9645,55 +9700,19 @@ window.__ModuleLoader__.load({
9645
9700
  }
9646
9701
 
9647
9702
 
9648
- /* DiffBlock's own 16-line cap would REPLACE scrolling with a fold button;
9649
- this panel keeps the old contract (render everything, scroll inside the
9650
- .dig-diff-pane max-height), so the cap is pushed out of reach and the
9651
- fold labels below are placeholders that never render at this size. */
9652
- const DIFF_BLOCK_MAX_LINES = 1000000
9653
-
9654
- function diffLabelsOf(t) {
9655
- return {
9656
- codeLabel: t('diff.codeLabel'),
9657
- wrapLabel: t('diff.wrapLabel'),
9658
- unwrapLabel: t('diff.unwrapLabel'),
9659
- copy: t('diff.copy'),
9660
- copied: t('diff.copied'),
9661
- collapseAria: 'Collapse diff',
9662
- expandAria: (hidden) => 'Show ' + String(hidden) + ' more diff lines',
9663
- collapse: 'Show less',
9664
- expand: (hidden) => 'Show ' + String(hidden) + ' more',
9665
- }
9666
- }
9667
-
9703
+ /* The inline pane body and the native preview tab share ONE renderer:
9704
+ LineDiff is the ported official ReviewTab/FileDiff and carries the
9705
+ split / wrap toolbar (issue #22). The former host DiffBlock body only
9706
+ had wrap / copy, so turning the external hand-off off silently dropped
9707
+ the 分栏 switch the user had asked for. One renderer also keeps both
9708
+ diff surfaces visually identical. */
9668
9709
  function DiffBody(props) {
9669
- const rows = useMemo(() => diffLines(props.patch), [props.patch])
9670
- if (props.loading === true) return E('div', { className: 'dig-empty' }, props.t('diff.loading'))
9671
- if (props.binary === true) return E('div', { className: 'dig-empty' }, props.t('diff.binary'))
9672
- if (rows.length === 0) return E('div', { className: 'dig-empty' }, props.t('diff.empty'))
9673
- /* Host-rendered first (v0.14): the ui-primitives diff card brings the
9674
- shared toolbar (copy / wrap / language) and the product's diff colors.
9675
- A patch we cannot replay into a hunk (binary, mode-only) or a host
9676
- without the seed module falls back to the hand-drawn rows below. This
9677
- IS the official diff renderer (the platform seed table ships it, the
9678
- host's own surfaces render diffs through it) — the external preview
9679
- tab uses the same one, so every diff in the product reads alike. */
9680
- if (UIPrimitives !== null && typeof UIPrimitives.DiffBlock === 'function') {
9681
- const hunk = patchToHunk(props.patch)
9682
- if (hunk !== null) {
9683
- return E('div', { className: 'dig-diff-host' },
9684
- E(UIPrimitives.DiffBlock, {
9685
- diffs: [hunk],
9686
- labels: diffLabelsOf(props.t),
9687
- maxLines: DIFF_BLOCK_MAX_LINES,
9688
- className: 'dig-diff-card',
9689
- }))
9690
- }
9691
- }
9692
- return E('div', { className: 'dig-diff' }, rows.map((row, index) => E('div', { key: index, className: 'dig-diff-line dig-diff-' + row.kind },
9693
- E('span', { className: 'dig-diff-gutter' }, row.oldLine === null ? '' : String(row.oldLine)),
9694
- E('span', { className: 'dig-diff-gutter' }, row.newLine === null ? '' : String(row.newLine)),
9695
- E('span', { className: 'dig-diff-text' }, row.text === '' ? ' ' : row.text),
9696
- )))
9710
+ return E(LineDiff, {
9711
+ patch: props.patch,
9712
+ loading: props.loading,
9713
+ t: props.t,
9714
+ onOpenFile: props.onOpenFile,
9715
+ })
9697
9716
  }
9698
9717
 
9699
9718
  /* ---- external preview diff (v0.14.3): a faithful port of the review view ----
@@ -10891,6 +10910,43 @@ window.__ModuleLoader__.load({
10891
10910
  E('span', { className: 'dig-settings-glyph' }, E(Icon, { name: 'branch', size: 13 })),
10892
10911
  E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t('settings.composer.title'))),
10893
10912
  E('div', { className: 'dig-settings-subnote' }, t('settings.composer.hint')))
10913
+ })(),
10914
+ (() => {
10915
+ /* Inline diff preview (issue #22): ON keeps the panel's own diff pane
10916
+ reachable, including its split / wrap switches. OFF restores the
10917
+ external hand-off to dsh-better-sidebar or the native preview seat.
10918
+ Same optimistic-write contract as the three rows above. */
10919
+ const field = 'inlineDiff'
10920
+ const inputId = 'dig-placement-' + field
10921
+ const shown = placementShown(field)
10922
+ const toggle = (event) => {
10923
+ if (readOnly === true) { setNote(t('settings.rail.readonly')); return }
10924
+ const fromControl = event !== undefined && event !== null && event.target !== null
10925
+ && event.target !== undefined && typeof event.target.checked === 'boolean'
10926
+ ? event.target.checked
10927
+ : null
10928
+ const wanted = placementTarget(field, fromControl)
10929
+ intent.current[field] = wanted
10930
+ dirty.current.set(field, wanted)
10931
+ setPending((now) => Object.assign({}, now, { [field]: wanted }))
10932
+ setNote('')
10933
+ bump()
10934
+ scheduleFlush()
10935
+ }
10936
+ return E('div', { key: field },
10937
+ E('div', { className: 'dig-settings-row', 'data-placement-row': field },
10938
+ E('input', {
10939
+ id: inputId,
10940
+ type: 'checkbox',
10941
+ className: 'dig-settings-check',
10942
+ 'aria-labelledby': inputId + '-label',
10943
+ checked: shown,
10944
+ disabled: readOnly === true,
10945
+ onChange: toggle,
10946
+ }),
10947
+ E('span', { className: 'dig-settings-glyph' }, E(Icon, { name: 'diff', size: 13 })),
10948
+ E('span', { id: inputId + '-label', className: 'dig-settings-label', onClick: toggle }, t('settings.diff.inline'))),
10949
+ E('div', { className: 'dig-settings-subnote' }, t('settings.diff.inlineHint')))
10894
10950
  })()),
10895
10951
  /* Section 3 — the AI commit message (issue #6): model, reasoning and the
10896
10952
  prompt template. */
@@ -13722,6 +13778,11 @@ window.__ModuleLoader__.load({
13722
13778
  repoRoot 并进它自己的 git 调用,多仓库工作区不会落错仓库。 */
13723
13779
  const previewDiff = useCallback((seed) => {
13724
13780
  if (repoRoot === null) return false
13781
+ /* Inline-first (issue #22): the switch defaults to ON, so the panel's
13782
+ own diff pane — and its split / wrap controls — is what a click
13783
+ opens. Turning it off hands the preview to the external chain
13784
+ below, which is the only behaviour older releases had. */
13785
+ if (inlineDiffEnabled() === true) return false
13725
13786
  const commitHash = seed.hash !== undefined && seed.hash !== null ? String(seed.hash) : null
13726
13787
  const ref = commitHash !== null
13727
13788
  ? { kind: 'commit', hash: commitHash.slice(0, 7), hashFull: commitHash, subject: seed.subject || '', worktree: repoRoot, repoRoot: repoRoot }
@@ -16355,6 +16416,18 @@ window.__ModuleLoader__.load({
16355
16416
  return value !== false
16356
16417
  }
16357
16418
 
16419
+ /* Inline-first diff preview (issue #22). Default ON: the panel's own diff
16420
+ pane owns the split / wrap switches, so installing dsh-better-sidebar or
16421
+ opening the native preview seat must not silently take that capability
16422
+ away. The settings card flips this field; false restores the external
16423
+ hand-off chain in previewDiff. */
16424
+ function inlineDiffEnabled() {
16425
+ const ready = railFormReady()
16426
+ if (ready === null) return true
16427
+ const value = ready.value !== null && typeof ready.value === 'object' ? ready.value.inlineDiff : undefined
16428
+ return value !== false
16429
+ }
16430
+
16358
16431
  const COMPOSER_POLL_MS = 30000
16359
16432
  const COMPOSER_ERROR_TTL_MS = 6000
16360
16433
  /* Line counts re-run at most this often without a real change: the two
@@ -17201,9 +17274,9 @@ window.__ModuleLoader__.load({
17201
17274
  diff — whatever the store holds — fetched fresh from the host route on
17202
17275
  every seed. The seed carries cwd + repoRoot so a multi-root workspace
17203
17276
  lands on the right repository (the route requires cwd; repoRoot skips
17204
- the rev-parse). Rendered through the SAME DiffBody the inline pane uses,
17205
- so host DiffBlock reuse, the hand-drawn fallback and the binary/empty
17206
- states are all identical. */
17277
+ the rev-parse). Rendered through the SAME LineDiff the inline pane's
17278
+ DiffBody uses, so the header tool set (split / wrap / open file) and
17279
+ the binary/empty states are identical on both surfaces. */
17207
17280
  function NativePreviewPanel(props) {
17208
17281
  const t = props.t
17209
17282
  const [seed, setSeed] = useState(() => previewStore.seed)
package/src/index.js CHANGED
@@ -84,6 +84,11 @@ function railConfigSchema(schema) {
84
84
  back and said "设置没写进去". Any new row-Config field the client writes MUST
85
85
  be declared (and volatile) here, or the write is refused before it lands. */
86
86
  shape.composerStatus = live(schema.boolean().default(true))
87
+ /* Inline diff preview (issue #22): ON by default so the panel's own diff
88
+ pane — with its split / wrap switches — stays reachable even when
89
+ dsh-better-sidebar or the native preview seat is installed. OFF hands the
90
+ preview back to those external tabs, exactly the pre-0.23 behaviour. */
91
+ shape.inlineDiff = live(schema.boolean().default(true))
87
92
  return schema.object(shape)
88
93
  }
89
94