@herbertgao/pi-extensions 2026.9.18 → 2026.9.20

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.
Files changed (226) hide show
  1. package/README.md +20 -28
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
  5. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +75 -40
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +42 -41
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
  32. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
  33. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
  34. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
  35. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
  36. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
  37. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
  38. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
  39. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +47 -10
  45. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
  46. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
  47. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +145 -190
  48. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
  49. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
  50. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
  51. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
  52. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
  53. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
  54. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
  55. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
  56. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
  57. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
  58. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
  59. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
  60. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
  61. package/node_modules/@herbertgao/pi-cc-extensions/package.json +8 -8
  62. package/node_modules/pi-antigravity/README.md +29 -6
  63. package/node_modules/pi-antigravity/package.json +12 -7
  64. package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
  65. package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
  66. package/node_modules/pi-antigravity/src/client/client.ts +1 -1
  67. package/node_modules/pi-antigravity/src/image/image.ts +4 -4
  68. package/node_modules/pi-antigravity/src/index.ts +203 -13
  69. package/node_modules/pi-antigravity/src/search/index.ts +1 -0
  70. package/node_modules/pi-antigravity/src/search/search.ts +325 -0
  71. package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
  72. package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
  73. package/node_modules/pi-antigravity/src/types/types.ts +5 -12
  74. package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
  75. package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
  76. package/node_modules/pi-typesafe/README.md +2 -0
  77. package/node_modules/pi-typesafe/package.json +2 -1
  78. package/node_modules/pi-web-access/CHANGELOG.md +72 -0
  79. package/node_modules/pi-web-access/README.md +34 -13
  80. package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
  81. package/node_modules/pi-web-access/curator-page.ts +5 -1
  82. package/node_modules/pi-web-access/curator-server.ts +2 -0
  83. package/node_modules/pi-web-access/dist/index.js +1337 -1009
  84. package/node_modules/pi-web-access/exa.ts +20 -52
  85. package/node_modules/pi-web-access/extract.ts +41 -4
  86. package/node_modules/pi-web-access/gemini-search.ts +12 -4
  87. package/node_modules/pi-web-access/index.ts +29 -37
  88. package/node_modules/pi-web-access/openai-search.ts +35 -7
  89. package/node_modules/pi-web-access/package.json +6 -5
  90. package/node_modules/pi-web-access/page-query.ts +2 -2
  91. package/node_modules/pi-web-access/query-rewrite.ts +2 -3
  92. package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
  93. package/node_modules/pi-web-access/storage.ts +48 -77
  94. package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
  95. package/node_modules/pi-web-access/summary-review.ts +3 -4
  96. package/node_modules/pi-web-access/tavily.ts +67 -20
  97. package/node_modules/pi-web-access/tool-activation.ts +53 -33
  98. package/node_modules/pi-web-access/utils.ts +1 -1
  99. package/node_modules/pi-web-access/you.ts +164 -0
  100. package/node_modules/pi-web-access/zai.ts +161 -0
  101. package/package.json +7 -11
  102. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
  103. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
  104. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
  105. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
  106. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
  107. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
  108. package/node_modules/pi-footer/CHANGELOG.md +0 -145
  109. package/node_modules/pi-footer/LICENSE +0 -21
  110. package/node_modules/pi-footer/README.md +0 -354
  111. package/node_modules/pi-footer/package.json +0 -68
  112. package/node_modules/pi-footer/src/cache.ts +0 -106
  113. package/node_modules/pi-footer/src/colors.ts +0 -237
  114. package/node_modules/pi-footer/src/config.ts +0 -202
  115. package/node_modules/pi-footer/src/event-widgets.ts +0 -41
  116. package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
  117. package/node_modules/pi-footer/src/git.ts +0 -135
  118. package/node_modules/pi-footer/src/index.ts +0 -268
  119. package/node_modules/pi-footer/src/metrics.ts +0 -141
  120. package/node_modules/pi-footer/src/presets.ts +0 -383
  121. package/node_modules/pi-footer/src/render.ts +0 -114
  122. package/node_modules/pi-footer/src/separators.ts +0 -52
  123. package/node_modules/pi-footer/src/types.ts +0 -122
  124. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
  125. package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
  126. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
  127. package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
  128. package/node_modules/pi-footer/src/ui/events.ts +0 -35
  129. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
  130. package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
  131. package/node_modules/pi-footer/src/ui/fields.ts +0 -252
  132. package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
  133. package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
  134. package/node_modules/pi-footer/src/ui/layout.ts +0 -17
  135. package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
  136. package/node_modules/pi-footer/src/ui/model.ts +0 -64
  137. package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
  138. package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
  139. package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
  140. package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
  141. package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
  142. package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
  143. package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
  144. package/node_modules/pi-footer/src/ui/screen.ts +0 -243
  145. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
  146. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
  147. package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
  148. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
  149. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
  150. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
  151. package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
  152. package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
  153. package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
  154. package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
  155. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
  156. package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
  157. package/node_modules/pi-footer/src/ui/theme.ts +0 -48
  158. package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
  159. package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
  160. package/node_modules/pi-footer/src/ui.ts +0 -66
  161. package/node_modules/pi-footer/src/widgets/context.ts +0 -28
  162. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
  163. package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
  164. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
  165. package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
  166. package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
  167. package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
  168. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
  169. package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
  170. package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
  171. package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
  172. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
  173. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
  174. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
  175. package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
  176. package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
  177. package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
  178. package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
  179. package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
  180. package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
  181. package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
  182. package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
  183. package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
  184. package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
  185. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
  186. package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
  187. package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
  188. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
  189. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
  190. package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
  191. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
  192. package/node_modules/pi-footer/src/widgets/options.ts +0 -122
  193. package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
  194. package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
  195. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
  196. package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
  197. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
  198. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
  199. package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
  200. package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
  201. package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
  202. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
  203. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
  204. package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
  205. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
  206. package/node_modules/pi-footer/src/widgets/store.ts +0 -26
  207. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
  208. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
  209. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
  210. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
  211. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
  212. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
  213. package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
  214. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
  215. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
  216. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
  217. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
  218. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
  219. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
  220. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
  221. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
  222. package/node_modules/pi-footer/src/widgets/types.ts +0 -181
  223. package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
  224. package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
  225. package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
  226. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
@@ -0,0 +1,324 @@
1
+ import { normalizeCodeWhitespace } from "./diff-text.ts";
2
+ import type {
3
+ DiffLineEntry,
4
+ DiffMetaEntry,
5
+ DiffOmissionEntry,
6
+ ParsedDiffEntry,
7
+ } from "./diff-parse.ts";
8
+
9
+ export interface DiffSpan {
10
+ start: number;
11
+ end: number;
12
+ }
13
+
14
+ export interface SplitDiffRow {
15
+ left?: DiffLineEntry;
16
+ right?: DiffLineEntry;
17
+ omission?: DiffOmissionEntry;
18
+ meta?: DiffMetaEntry;
19
+ hunkIndex: number | null;
20
+ }
21
+
22
+ const MAX_INLINE_DIFF_LINE_LENGTH = 700;
23
+
24
+ function tokenizeInlineDiff(input: string): Array<{ value: string; start: number; end: number }> {
25
+ if (!input) {
26
+ return [];
27
+ }
28
+
29
+ const tokens: Array<{ value: string; start: number; end: number }> = [];
30
+ const pattern = /(\s+|[A-Za-z0-9_]+|[^A-Za-z0-9_\s])/g;
31
+ let match: RegExpExecArray | null;
32
+
33
+ while ((match = pattern.exec(input)) !== null) {
34
+ const value = match[0] ?? "";
35
+ if (!value) {
36
+ continue;
37
+ }
38
+ tokens.push({
39
+ value,
40
+ start: match.index,
41
+ end: match.index + value.length,
42
+ });
43
+ }
44
+
45
+ if (tokens.length === 0 && input.length > 0) {
46
+ tokens.push({ value: input, start: 0, end: input.length });
47
+ }
48
+
49
+ return tokens;
50
+ }
51
+
52
+ export function mergeSpans(spans: DiffSpan[]): DiffSpan[] {
53
+ if (spans.length <= 1) {
54
+ return spans;
55
+ }
56
+
57
+ const sorted = [...spans].sort((a, b) => a.start - b.start);
58
+ const merged: DiffSpan[] = [sorted[0]];
59
+
60
+ for (let index = 1; index < sorted.length; index++) {
61
+ const current = sorted[index];
62
+ const previous = merged[merged.length - 1];
63
+ if (!current || !previous) {
64
+ continue;
65
+ }
66
+
67
+ if (current.start <= previous.end) {
68
+ previous.end = Math.max(previous.end, current.end);
69
+ continue;
70
+ }
71
+
72
+ merged.push({ ...current });
73
+ }
74
+
75
+ return merged;
76
+ }
77
+
78
+ function tokensToDiffSpans(
79
+ text: string,
80
+ tokens: Array<{ value: string; start: number; end: number }>,
81
+ changedIndexes: Set<number>,
82
+ ): DiffSpan[] {
83
+ if (tokens.length === 0 || changedIndexes.size === 0) {
84
+ return [];
85
+ }
86
+
87
+ const spans: DiffSpan[] = [];
88
+ let start: number | null = null;
89
+ let end = -1;
90
+
91
+ for (let index = 0; index < tokens.length; index++) {
92
+ if (!changedIndexes.has(index)) {
93
+ if (start !== null && end > start) {
94
+ spans.push({ start, end });
95
+ start = null;
96
+ end = -1;
97
+ }
98
+ continue;
99
+ }
100
+
101
+ const token = tokens[index];
102
+ if (!token) {
103
+ continue;
104
+ }
105
+
106
+ if (start === null) {
107
+ start = token.start;
108
+ end = token.end;
109
+ } else {
110
+ end = token.end;
111
+ }
112
+ }
113
+
114
+ if (start !== null && end > start) {
115
+ spans.push({ start, end });
116
+ }
117
+
118
+ const trimmed: DiffSpan[] = [];
119
+ for (const span of spans) {
120
+ let spanStart = span.start;
121
+ let spanEnd = span.end;
122
+
123
+ while (spanStart < spanEnd && /\s/.test(text[spanStart] ?? "")) {
124
+ spanStart++;
125
+ }
126
+ while (spanEnd > spanStart && /\s/.test(text[spanEnd - 1] ?? "")) {
127
+ spanEnd--;
128
+ }
129
+ if (spanEnd > spanStart) {
130
+ trimmed.push({ start: spanStart, end: spanEnd });
131
+ }
132
+ }
133
+
134
+ return mergeSpans(trimmed);
135
+ }
136
+
137
+ function computeInlineDiffSpans(
138
+ leftLine: string,
139
+ rightLine: string,
140
+ ): { left: DiffSpan[]; right: DiffSpan[] } {
141
+ if (leftLine === rightLine) {
142
+ return { left: [], right: [] };
143
+ }
144
+ if (
145
+ leftLine.length > MAX_INLINE_DIFF_LINE_LENGTH ||
146
+ rightLine.length > MAX_INLINE_DIFF_LINE_LENGTH
147
+ ) {
148
+ return { left: [], right: [] };
149
+ }
150
+
151
+ const leftTokens = tokenizeInlineDiff(leftLine);
152
+ const rightTokens = tokenizeInlineDiff(rightLine);
153
+ const leftCount = leftTokens.length;
154
+ const rightCount = rightTokens.length;
155
+
156
+ if (leftCount === 0 || rightCount === 0) {
157
+ return {
158
+ left: leftLine.trim().length > 0 ? [{ start: 0, end: leftLine.length }] : [],
159
+ right: rightLine.trim().length > 0 ? [{ start: 0, end: rightLine.length }] : [],
160
+ };
161
+ }
162
+
163
+ const table: number[][] = Array.from({ length: leftCount + 1 }, () =>
164
+ Array<number>(rightCount + 1).fill(0),
165
+ );
166
+
167
+ for (let leftIndex = 1; leftIndex <= leftCount; leftIndex++) {
168
+ const leftToken = leftTokens[leftIndex - 1];
169
+ for (let rightIndex = 1; rightIndex <= rightCount; rightIndex++) {
170
+ const rightToken = rightTokens[rightIndex - 1];
171
+ if (leftToken?.value === rightToken?.value) {
172
+ table[leftIndex][rightIndex] = (table[leftIndex - 1]?.[rightIndex - 1] ?? 0) + 1;
173
+ } else {
174
+ const top = table[leftIndex - 1]?.[rightIndex] ?? 0;
175
+ const side = table[leftIndex]?.[rightIndex - 1] ?? 0;
176
+ table[leftIndex][rightIndex] = Math.max(top, side);
177
+ }
178
+ }
179
+ }
180
+
181
+ const changedLeft = new Set<number>();
182
+ const changedRight = new Set<number>();
183
+ let leftCursor = leftCount;
184
+ let rightCursor = rightCount;
185
+
186
+ while (leftCursor > 0 && rightCursor > 0) {
187
+ const leftToken = leftTokens[leftCursor - 1];
188
+ const rightToken = rightTokens[rightCursor - 1];
189
+ if (leftToken?.value === rightToken?.value) {
190
+ leftCursor--;
191
+ rightCursor--;
192
+ continue;
193
+ }
194
+
195
+ const top = table[leftCursor - 1]?.[rightCursor] ?? 0;
196
+ const side = table[leftCursor]?.[rightCursor - 1] ?? 0;
197
+ if (top >= side) {
198
+ changedLeft.add(leftCursor - 1);
199
+ leftCursor--;
200
+ } else {
201
+ changedRight.add(rightCursor - 1);
202
+ rightCursor--;
203
+ }
204
+ }
205
+
206
+ while (leftCursor > 0) {
207
+ changedLeft.add(leftCursor - 1);
208
+ leftCursor--;
209
+ }
210
+ while (rightCursor > 0) {
211
+ changedRight.add(rightCursor - 1);
212
+ rightCursor--;
213
+ }
214
+
215
+ return {
216
+ left: tokensToDiffSpans(leftLine, leftTokens, changedLeft),
217
+ right: tokensToDiffSpans(rightLine, rightTokens, changedRight),
218
+ };
219
+ }
220
+
221
+ function collectConsecutiveLineEntries(
222
+ entries: ParsedDiffEntry[],
223
+ startIndex: number,
224
+ lineKind: DiffLineEntry["lineKind"],
225
+ ): { collected: DiffLineEntry[]; nextIndex: number } {
226
+ const collected: DiffLineEntry[] = [];
227
+ let index = startIndex;
228
+ while (index < entries.length) {
229
+ const candidate = entries[index];
230
+ if (!candidate || candidate.kind !== "line" || candidate.lineKind !== lineKind) {
231
+ break;
232
+ }
233
+ collected.push(candidate);
234
+ index++;
235
+ }
236
+ return { collected, nextIndex: index };
237
+ }
238
+
239
+ export function buildSplitRows(entries: ParsedDiffEntry[]): SplitDiffRow[] {
240
+ const rows: SplitDiffRow[] = [];
241
+ let index = 0;
242
+
243
+ while (index < entries.length) {
244
+ const entry = entries[index];
245
+ if (!entry) {
246
+ break;
247
+ }
248
+
249
+ if (entry.kind === "omission") {
250
+ rows.push({ omission: entry, hunkIndex: entry.hunkIndex || null });
251
+ index++;
252
+ continue;
253
+ }
254
+
255
+ if (entry.kind !== "line") {
256
+ rows.push({ meta: entry, hunkIndex: entry.hunkIndex || null });
257
+ index++;
258
+ continue;
259
+ }
260
+
261
+ if (entry.lineKind === "remove") {
262
+ const removedResult = collectConsecutiveLineEntries(entries, index, "remove");
263
+ const removed = removedResult.collected;
264
+ const addedResult = collectConsecutiveLineEntries(entries, removedResult.nextIndex, "add");
265
+ const added = addedResult.collected;
266
+ index = addedResult.nextIndex;
267
+
268
+ const pairCount = Math.max(removed.length, added.length);
269
+ for (let pairIndex = 0; pairIndex < pairCount; pairIndex++) {
270
+ const left = removed[pairIndex];
271
+ const right = added[pairIndex];
272
+ rows.push({
273
+ left,
274
+ right,
275
+ hunkIndex: left?.hunkIndex ?? right?.hunkIndex ?? null,
276
+ });
277
+ }
278
+ continue;
279
+ }
280
+
281
+ if (entry.lineKind === "add") {
282
+ rows.push({ right: entry, hunkIndex: entry.hunkIndex || null });
283
+ index++;
284
+ continue;
285
+ }
286
+
287
+ rows.push({ left: entry, right: entry, hunkIndex: entry.hunkIndex || null });
288
+ index++;
289
+ }
290
+
291
+ return rows;
292
+ }
293
+
294
+ export function getCellLineNumber(line: DiffLineEntry, side: "left" | "right"): number | null {
295
+ if (side === "left") {
296
+ return line.oldLineNumber ?? (line.lineKind === "context" ? line.newLineNumber : null);
297
+ }
298
+ return line.newLineNumber ?? (line.lineKind === "context" ? line.oldLineNumber : null);
299
+ }
300
+
301
+ export function buildInlineHighlightMap(rows: SplitDiffRow[]): WeakMap<DiffLineEntry, DiffSpan[]> {
302
+ const highlights = new WeakMap<DiffLineEntry, DiffSpan[]>();
303
+
304
+ for (const row of rows) {
305
+ if (!row.left || !row.right) {
306
+ continue;
307
+ }
308
+ if (row.left.lineKind !== "remove" || row.right.lineKind !== "add") {
309
+ continue;
310
+ }
311
+
312
+ const leftText = normalizeCodeWhitespace(row.left.content);
313
+ const rightText = normalizeCodeWhitespace(row.right.content);
314
+ const inline = computeInlineDiffSpans(leftText, rightText);
315
+ if (inline.left.length > 0) {
316
+ highlights.set(row.left, inline.left);
317
+ }
318
+ if (inline.right.length > 0) {
319
+ highlights.set(row.right, inline.right);
320
+ }
321
+ }
322
+
323
+ return highlights;
324
+ }