@pi-unipi/unipi 2.6.0 → 2.6.2
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/CHANGELOG.md +19 -0
- package/package.json +21 -22
- package/packages/ask-user/package.json +2 -2
- package/packages/ask-user/settings-tui.ts +18 -34
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +2 -15
- package/packages/autocomplete/src/provider.ts +3 -17
- package/packages/btw/extensions/btw.ts +18 -45
- package/packages/btw/package.json +2 -2
- package/packages/compactor/README.md +2 -2
- package/packages/compactor/package.json +3 -3
- package/packages/compactor/skills/compactor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-detail/SKILL.md +3 -5
- package/packages/compactor/skills/compactor-doctor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-stats/SKILL.md +1 -1
- package/packages/compactor/src/commands/index.ts +47 -78
- package/packages/compactor/src/compaction/brief.ts +161 -90
- package/packages/compactor/src/compaction/build-sections.ts +3 -4
- package/packages/compactor/src/compaction/compact-args.ts +86 -0
- package/packages/compactor/src/compaction/cut.ts +270 -28
- package/packages/compactor/src/compaction/drill-down.ts +261 -0
- package/packages/compactor/src/compaction/format-recall.ts +96 -0
- package/packages/compactor/src/compaction/format.ts +8 -3
- package/packages/compactor/src/compaction/hooks.ts +248 -72
- package/packages/compactor/src/compaction/merge.ts +34 -4
- package/packages/compactor/src/compaction/rank.ts +270 -0
- package/packages/compactor/src/compaction/recall-scope.ts +28 -0
- package/packages/compactor/src/compaction/search-entries.ts +333 -96
- package/packages/compactor/src/compaction/skill-collapse.ts +35 -0
- package/packages/compactor/src/compaction/summarize.ts +37 -6
- package/packages/compactor/src/compaction/token-estimate.ts +104 -0
- package/packages/compactor/src/compaction/touched-files.ts +35 -0
- package/packages/compactor/src/config/manager.ts +2 -27
- package/packages/compactor/src/config/presets.ts +0 -2
- package/packages/compactor/src/config/schema.ts +2 -16
- package/packages/compactor/src/executor/executor.ts +6 -15
- package/packages/compactor/src/executor/runtime.ts +2 -12
- package/packages/compactor/src/index.ts +4 -121
- package/packages/compactor/src/info-screen.ts +3 -10
- package/packages/compactor/src/security/evaluator.ts +0 -53
- package/packages/compactor/src/security/policy.ts +7 -8
- package/packages/compactor/src/session/db.ts +0 -6
- package/packages/compactor/src/tools/ctx-execute-file.ts +0 -5
- package/packages/compactor/src/tools/register.ts +27 -50
- package/packages/compactor/src/tools/vcc-recall.ts +86 -48
- package/packages/compactor/src/tui/settings-overlay.ts +20 -40
- package/packages/compactor/src/types.ts +43 -100
- package/packages/core/constants.ts +1 -28
- package/packages/core/events.ts +1 -83
- package/packages/core/index.ts +1 -0
- package/packages/core/model-cache.ts +18 -12
- package/packages/core/package.json +3 -1
- package/packages/core/tui-overlay.ts +89 -0
- package/packages/core/tui-width.ts +18 -0
- package/packages/core/utils.ts +43 -0
- package/packages/footer/package.json +2 -2
- package/packages/footer/src/help.ts +2 -2
- package/packages/footer/src/index.ts +9 -10
- package/packages/footer/src/presets.ts +1 -27
- package/packages/footer/src/registry/index.ts +0 -23
- package/packages/footer/src/rendering/icons.ts +0 -13
- package/packages/footer/src/rendering/renderer.ts +5 -40
- package/packages/footer/src/rendering/separators.ts +0 -10
- package/packages/footer/src/rendering/theme.ts +1 -15
- package/packages/footer/src/segments/compactor.ts +7 -14
- package/packages/footer/src/segments/core.ts +15 -54
- package/packages/footer/src/segments/kanboard.ts +5 -5
- package/packages/footer/src/segments/mcp.ts +5 -5
- package/packages/footer/src/segments/memory.ts +4 -4
- package/packages/footer/src/segments/notify.ts +2 -2
- package/packages/footer/src/segments/ralph.ts +3 -3
- package/packages/footer/src/segments/status-ext.ts +1 -1
- package/packages/footer/src/segments/workflow.ts +3 -3
- package/packages/footer/src/tps-tracker.ts +0 -8
- package/packages/footer/src/tui/settings-tui.ts +14 -31
- package/packages/footer/src/types.ts +0 -15
- package/packages/image/package.json +2 -2
- package/packages/image/src/register-providers.ts +0 -11
- package/packages/image/src/tools.ts +0 -3
- package/packages/image/src/tui/model-selector.ts +36 -71
- package/packages/info-screen/config.ts +0 -16
- package/packages/info-screen/core-groups.ts +0 -10
- package/packages/info-screen/index.ts +22 -13
- package/packages/info-screen/package.json +2 -2
- package/packages/info-screen/registry.ts +1 -72
- package/packages/info-screen/tui/info-overlay.ts +85 -100
- package/packages/info-screen/usage-parser.ts +12 -47
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/input-shortcuts/src/index.ts +3 -3
- package/packages/input-shortcuts/src/registers.ts +0 -8
- package/packages/input-shortcuts/src/settings-overlay.ts +4 -13
- package/packages/input-shortcuts/src/undo-redo.ts +0 -12
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/package.json +2 -2
- package/packages/mcp/src/bridge/client.ts +0 -15
- package/packages/mcp/src/config/manager.ts +0 -38
- package/packages/mcp/src/config/schema.ts +0 -27
- package/packages/mcp/src/config/sync.ts +0 -29
- package/packages/mcp/src/index.ts +1 -1
- package/packages/mcp/src/tui/add-overlay.ts +6 -6
- package/packages/mcp/src/types.ts +0 -6
- package/packages/memory/embedding.ts +0 -31
- package/packages/memory/mempalace.ts +0 -16
- package/packages/memory/package.json +4 -8
- package/packages/memory/storage.ts +56 -753
- package/packages/milestone/hooks.ts +1 -6
- package/packages/milestone/milestone.ts +2 -41
- package/packages/milestone/package.json +2 -2
- package/packages/notify/README.md +1 -0
- package/packages/notify/commands.ts +61 -3
- package/packages/notify/events.ts +0 -10
- package/packages/notify/package.json +2 -2
- package/packages/notify/settings.ts +0 -19
- package/packages/notify/tui/gotify-setup.ts +108 -143
- package/packages/notify/tui/ntfy-setup.ts +137 -172
- package/packages/notify/tui/recap-model-selector.ts +107 -101
- package/packages/notify/tui/settings-overlay.ts +94 -117
- package/packages/notify/tui/telegram-setup.ts +48 -79
- package/packages/notify/types.ts +0 -2
- package/packages/ralph/index.ts +11 -35
- package/packages/ralph/package.json +3 -3
- package/packages/ralph/ralph-loop.ts +3 -44
- package/packages/subagents/package.json +2 -1
- package/packages/subagents/src/agent-manager.ts +0 -51
- package/packages/subagents/src/agent-runner.ts +6 -80
- package/packages/subagents/src/conversation-viewer.ts +1 -19
- package/packages/subagents/src/core-compat.ts +8 -25
- package/packages/subagents/src/custom-agents.ts +0 -1
- package/packages/subagents/src/index.ts +3 -48
- package/packages/subagents/src/types.ts +0 -11
- package/packages/subagents/src/widget.ts +5 -5
- package/packages/unipi/bundled.js +4468 -7769
- package/packages/unipi/index.ts +6 -3
- package/packages/updater/package.json +2 -2
- package/packages/updater/src/changelog.ts +1 -16
- package/packages/updater/src/index.ts +0 -5
- package/packages/updater/src/installer.ts +4 -26
- package/packages/updater/src/markdown.ts +6 -152
- package/packages/updater/src/settings.ts +0 -15
- package/packages/updater/src/tui/changelog-overlay.ts +42 -234
- package/packages/updater/src/tui/list-detail-overlay.ts +260 -0
- package/packages/updater/src/tui/readme-overlay.ts +53 -210
- package/packages/updater/src/tui/settings-overlay.ts +13 -26
- package/packages/updater/src/version.ts +2 -31
- package/packages/utility/package.json +2 -4
- package/packages/utility/src/commands.ts +4 -5
- package/packages/utility/src/herdr-sync.ts +136 -0
- package/packages/utility/src/index.ts +11 -68
- package/packages/utility/src/lifecycle/cleanup.ts +2 -16
- package/packages/utility/src/{diff/settings.ts → settings.ts} +42 -80
- package/packages/utility/src/tools/env.ts +0 -0
- package/packages/utility/src/tui/name-badge-state.ts +18 -2
- package/packages/utility/src/tui/name-badge.ts +5 -4
- package/packages/utility/src/tui/util-settings-tui.ts +41 -224
- package/packages/web-api/package.json +2 -6
- package/packages/web-api/src/engine/dependencies.ts +13 -134
- package/packages/web-api/src/engine/extract.ts +9 -99
- package/packages/web-api/src/engine/format.ts +0 -75
- package/packages/web-api/src/engine/types.ts +0 -39
- package/packages/web-api/src/index.ts +1 -2
- package/packages/web-api/src/providers/base.ts +0 -10
- package/packages/web-api/src/providers/duckduckgo.ts +0 -1
- package/packages/web-api/src/providers/firecrawl.ts +0 -9
- package/packages/web-api/src/providers/jina-reader.ts +0 -9
- package/packages/web-api/src/providers/jina-search.ts +0 -9
- package/packages/web-api/src/providers/perplexity.ts +0 -9
- package/packages/web-api/src/providers/registry.ts +0 -80
- package/packages/web-api/src/providers/serpapi.ts +0 -9
- package/packages/web-api/src/providers/tavily.ts +0 -9
- package/packages/web-api/src/providers/wigolo-client.ts +0 -30
- package/packages/web-api/src/providers/wigolo.ts +0 -1
- package/packages/web-api/src/settings.ts +17 -77
- package/packages/web-api/src/tools.ts +21 -24
- package/packages/web-api/src/tui/settings-dialog.ts +0 -1
- package/packages/workflow/commands.ts +38 -122
- package/packages/workflow/index.ts +1 -5
- package/packages/workflow/package.json +2 -2
- package/packages/cocoindex/README.md +0 -94
- package/packages/cocoindex/bridge.ts +0 -842
- package/packages/cocoindex/commands.ts +0 -175
- package/packages/cocoindex/index.ts +0 -48
- package/packages/cocoindex/installer.ts +0 -397
- package/packages/cocoindex/package.json +0 -49
- package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -88
- package/packages/cocoindex/tools.ts +0 -157
- package/packages/compactor/src/display/diff-renderer.ts +0 -281
- package/packages/compactor/src/display/line-width-safety.ts +0 -28
- package/packages/compactor/src/display/render-utils.ts +0 -52
- package/packages/compactor/src/display/thinking-label.ts +0 -18
- package/packages/compactor/src/display/tool-overrides.ts +0 -136
- package/packages/compactor/src/tools/compact.ts +0 -20
- package/packages/subagents/src/file-lock.ts +0 -102
- package/packages/utility/src/cache/ttl-cache.ts +0 -311
- package/packages/utility/src/diff/highlighter.ts +0 -352
- package/packages/utility/src/diff/parser.ts +0 -191
- package/packages/utility/src/diff/renderer.ts +0 -395
- package/packages/utility/src/diff/theme.ts +0 -316
- package/packages/utility/src/diff/wrapper.ts +0 -339
- package/packages/utility/src/display/capabilities.ts +0 -214
- package/packages/utility/src/display/width.ts +0 -226
- package/packages/utility/src/tools/batch.ts +0 -229
- package/packages/utility/src/tui/badge-settings-tui.ts +0 -388
- package/packages/utility/src/tui/badge-settings.ts +0 -67
- package/packages/utility/src/tui/settings-inspector.ts +0 -303
- package/packages/web-api/src/providers/llm-summarize.ts +0 -71
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/utility — Diff Renderer
|
|
3
|
-
*
|
|
4
|
-
* ANSI diff rendering: split (side-by-side) and unified (stacked) views.
|
|
5
|
-
* Includes ANSI utilities, background injection, and adaptive wrapping.
|
|
6
|
-
*
|
|
7
|
-
* IMPORTANT: All output lines must be truncated to terminal width
|
|
8
|
-
* to prevent TUI crashes. Use fit() or truncateToTermWidth()
|
|
9
|
-
* before pushing lines.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { ParsedDiff, DiffLine } from "./parser.js";
|
|
13
|
-
import type { DiffColors } from "./theme.js";
|
|
14
|
-
import { hexToBgAnsi, hexToFgAnsi } from "./theme.js";
|
|
15
|
-
import { hlBlock, detectLanguage } from "./highlighter.js";
|
|
16
|
-
import { truncateToWidth as piTruncateToWidth, visibleWidth as piVisibleWidth } from "@earendil-works/pi-tui";
|
|
17
|
-
|
|
18
|
-
// ─── Constants ──────────────────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
/** Maximum preview lines shown inline */
|
|
21
|
-
export const MAX_PREVIEW_LINES = 60;
|
|
22
|
-
|
|
23
|
-
/** Maximum total render lines */
|
|
24
|
-
export const MAX_RENDER_LINES = 150;
|
|
25
|
-
|
|
26
|
-
/** Minimum terminal width for split view */
|
|
27
|
-
export const SPLIT_MIN_WIDTH = 150;
|
|
28
|
-
|
|
29
|
-
/** Minimum code column width in split view */
|
|
30
|
-
export const SPLIT_MIN_CODE_WIDTH = 60;
|
|
31
|
-
|
|
32
|
-
// ─── ANSI Utilities ─────────────────────────────────────────────────────────────
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Strip all ANSI escape sequences from a string.
|
|
36
|
-
*/
|
|
37
|
-
export function strip(s: string): string {
|
|
38
|
-
// eslint-disable-next-line no-control-regex
|
|
39
|
-
return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Get the visible width of a string (excluding ANSI escapes).
|
|
44
|
-
* Delegates to pi-tui's ANSI-aware implementation for correctness.
|
|
45
|
-
*/
|
|
46
|
-
export function visibleWidth(s: string): number {
|
|
47
|
-
return piVisibleWidth(s);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Fit a string to a target width, padding or truncating as needed.
|
|
52
|
-
* Uses pi-tui's ANSI-aware truncation for correctness.
|
|
53
|
-
*/
|
|
54
|
-
export function fit(s: string, targetWidth: number): string {
|
|
55
|
-
const vw = visibleWidth(s);
|
|
56
|
-
if (vw === targetWidth) return s;
|
|
57
|
-
if (vw > targetWidth) {
|
|
58
|
-
return piTruncateToWidth(s, targetWidth, "");
|
|
59
|
-
}
|
|
60
|
-
// Pad
|
|
61
|
-
return s + " ".repeat(targetWidth - vw);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Wrap an ANSI string to a maximum width.
|
|
66
|
-
* Returns an array of wrapped lines.
|
|
67
|
-
*/
|
|
68
|
-
export function wrapAnsi(s: string, maxWidth: number): string[] {
|
|
69
|
-
if (visibleWidth(s) <= maxWidth) return [s];
|
|
70
|
-
|
|
71
|
-
const lines: string[] = [];
|
|
72
|
-
let current = "";
|
|
73
|
-
let currentWidth = 0;
|
|
74
|
-
let ansiState = "";
|
|
75
|
-
|
|
76
|
-
// Process character by character
|
|
77
|
-
let i = 0;
|
|
78
|
-
while (i < s.length) {
|
|
79
|
-
if (s[i] === "\x1b") {
|
|
80
|
-
// Collect ANSI sequence
|
|
81
|
-
let seq = "\x1b";
|
|
82
|
-
i++;
|
|
83
|
-
while (i < s.length && s[i] !== "m") {
|
|
84
|
-
seq += s[i];
|
|
85
|
-
i++;
|
|
86
|
-
}
|
|
87
|
-
if (i < s.length) {
|
|
88
|
-
seq += "m";
|
|
89
|
-
i++;
|
|
90
|
-
}
|
|
91
|
-
ansiState = seq;
|
|
92
|
-
current += seq;
|
|
93
|
-
} else {
|
|
94
|
-
const char = s[i];
|
|
95
|
-
const code = char.codePointAt(0)!;
|
|
96
|
-
const charWidth = (code >= 0x4e00 && code <= 0x9fff) || code > 0xffff ? 2 : 1;
|
|
97
|
-
|
|
98
|
-
if (currentWidth + charWidth > maxWidth) {
|
|
99
|
-
lines.push(current + "\x1b[0m");
|
|
100
|
-
current = ansiState;
|
|
101
|
-
currentWidth = 0;
|
|
102
|
-
}
|
|
103
|
-
current += char;
|
|
104
|
-
currentWidth += charWidth;
|
|
105
|
-
i++;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (strip(current).length > 0) {
|
|
110
|
-
lines.push(current);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return lines.length > 0 ? lines : [""];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Get the current ANSI state (active escape sequences) at the end of a string.
|
|
118
|
-
*/
|
|
119
|
-
export function ansiState(s: string): string {
|
|
120
|
-
// Find the last reset or the last escape sequence
|
|
121
|
-
const lastReset = s.lastIndexOf("\x1b[0m");
|
|
122
|
-
const lastEsc = s.lastIndexOf("\x1b[");
|
|
123
|
-
|
|
124
|
-
if (lastEsc < 0) return "";
|
|
125
|
-
// If reset is at the end (or after last escape), state is cleared
|
|
126
|
-
if (lastReset >= lastEsc) return "";
|
|
127
|
-
|
|
128
|
-
// Extract the sequence
|
|
129
|
-
const match = s.substring(lastEsc).match(/^\x1b\[[0-9;]*[a-zA-Z]/);
|
|
130
|
-
return match ? match[0] : "";
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Format a line number with fixed width.
|
|
135
|
-
*/
|
|
136
|
-
export function lnum(n: number | null, width: number = 4): string {
|
|
137
|
-
if (n === null) return " ".repeat(width);
|
|
138
|
-
return n.toString().padStart(width, " ");
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Generate a stripe pattern for alternating rows.
|
|
143
|
-
*/
|
|
144
|
-
export function stripes(line: string, even: boolean): string {
|
|
145
|
-
const dim = "\x1b[2m";
|
|
146
|
-
const reset = "\x1b[0m";
|
|
147
|
-
return even ? `${dim}·${reset} ${line}` : ` ${line}`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// ─── Terminal Width ─────────────────────────────────────────────────────────────
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Get terminal width, with fallback.
|
|
154
|
-
*/
|
|
155
|
-
export function termW(): number {
|
|
156
|
-
try {
|
|
157
|
-
return process.stdout.columns || 80;
|
|
158
|
-
} catch {
|
|
159
|
-
return 80;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Truncate a line to terminal width using pi-tui's ANSI-aware truncation.
|
|
165
|
-
* Prevents TUI crash when rendered lines exceed terminal width.
|
|
166
|
-
* Accounts for rendering overhead from Box nesting (~6 chars).
|
|
167
|
-
*/
|
|
168
|
-
export function truncateToTermWidth(line: string): string {
|
|
169
|
-
const maxW = Math.max(20, termW() - 6);
|
|
170
|
-
if (piVisibleWidth(line) <= maxW) return line;
|
|
171
|
-
return piTruncateToWidth(line, maxW, "…");
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ─── Background Injection ───────────────────────────────────────────────────────
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Composite diff background colors under Shiki syntax foregrounds.
|
|
178
|
-
*
|
|
179
|
-
* Takes an ANSI-highlighted line and injects a background color
|
|
180
|
-
* at the specified character ranges.
|
|
181
|
-
*
|
|
182
|
-
* @param ansiLine - ANSI-highlighted line from Shiki
|
|
183
|
-
* @param ranges - Character ranges to apply background to [{start, end}]
|
|
184
|
-
* @param baseBg - Base background color for the line
|
|
185
|
-
* @param hlBg - Highlight background color for the ranges
|
|
186
|
-
*/
|
|
187
|
-
export function injectBg(
|
|
188
|
-
ansiLine: string,
|
|
189
|
-
ranges: Array<{ start: number; end: number }>,
|
|
190
|
-
baseBg: string,
|
|
191
|
-
hlBg: string,
|
|
192
|
-
): string {
|
|
193
|
-
if (ranges.length === 0) {
|
|
194
|
-
return `${hexToBgAnsi(baseBg)}${ansiLine}\x1b[0m`;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// For simplicity, apply the highlight background to the whole line
|
|
198
|
-
// if any ranges are specified. Character-level injection is complex
|
|
199
|
-
// and would require parsing the ANSI stream.
|
|
200
|
-
return `${hexToBgAnsi(hlBg)}${ansiLine}\x1b[0m`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// ─── Adaptive Wrap ──────────────────────────────────────────────────────────────
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Wrap rows adaptively, preserving line structure.
|
|
207
|
-
* Returns the wrapped rows with line continuation indicators.
|
|
208
|
-
*/
|
|
209
|
-
export function adaptiveWrapRows(rows: string[], maxWidth: number): string[] {
|
|
210
|
-
const result: string[] = [];
|
|
211
|
-
for (const row of rows) {
|
|
212
|
-
if (visibleWidth(row) <= maxWidth) {
|
|
213
|
-
result.push(row);
|
|
214
|
-
} else {
|
|
215
|
-
const wrapped = wrapAnsi(row, maxWidth);
|
|
216
|
-
for (let i = 0; i < wrapped.length; i++) {
|
|
217
|
-
result.push(i === 0 ? wrapped[i] : ` ${wrapped[i]}`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
return result;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// ─── Split View Heuristic ───────────────────────────────────────────────────────
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Determine whether to use split (side-by-side) view.
|
|
228
|
-
* Falls back to unified on narrow terminals or when wrap ratio is too high.
|
|
229
|
-
*
|
|
230
|
-
* @param diff - The parsed diff
|
|
231
|
-
* @param tw - Terminal width
|
|
232
|
-
* @param max - Maximum render lines
|
|
233
|
-
*/
|
|
234
|
-
export function shouldUseSplit(diff: ParsedDiff, tw: number, max: number): boolean {
|
|
235
|
-
// Too narrow for split
|
|
236
|
-
if (tw < SPLIT_MIN_WIDTH) return false;
|
|
237
|
-
|
|
238
|
-
// Calculate code column widths
|
|
239
|
-
const codeWidth = Math.floor((tw - 6) / 2); // 6 chars for padding/line numbers
|
|
240
|
-
if (codeWidth < SPLIT_MIN_CODE_WIDTH) return false;
|
|
241
|
-
|
|
242
|
-
// Check if any lines would wrap in split mode
|
|
243
|
-
const maxContentWidth = Math.max(
|
|
244
|
-
...diff.lines
|
|
245
|
-
.filter((l) => l.type !== "hunk")
|
|
246
|
-
.map((l) => l.content.length),
|
|
247
|
-
0,
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
// If lines are too long, unified is better
|
|
251
|
-
if (maxContentWidth > codeWidth * 1.5) return false;
|
|
252
|
-
|
|
253
|
-
return true;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// ─── Renderers ──────────────────────────────────────────────────────────────────
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Render a diff in unified (stacked single-column) view.
|
|
260
|
-
*
|
|
261
|
-
* @param diff - Parsed diff
|
|
262
|
-
* @param language - Shiki language for syntax highlighting
|
|
263
|
-
* @param max - Maximum lines to render
|
|
264
|
-
* @param dc - Diff colors
|
|
265
|
-
*/
|
|
266
|
-
export function renderUnified(
|
|
267
|
-
diff: ParsedDiff,
|
|
268
|
-
language: string,
|
|
269
|
-
max: number,
|
|
270
|
-
dc: DiffColors,
|
|
271
|
-
): string {
|
|
272
|
-
const lines: string[] = [];
|
|
273
|
-
const totalLines = Math.min(diff.lines.length, max);
|
|
274
|
-
const truncated = diff.lines.length > max;
|
|
275
|
-
|
|
276
|
-
const addBg = hexToBgAnsi(dc.addBg);
|
|
277
|
-
const remBg = hexToBgAnsi(dc.remBg);
|
|
278
|
-
const addFg = hexToFgAnsi(dc.addFg);
|
|
279
|
-
const remFg = hexToFgAnsi(dc.remFg);
|
|
280
|
-
const hunkFg = hexToFgAnsi(dc.hunkFg);
|
|
281
|
-
const headerFg = hexToFgAnsi(dc.headerFg);
|
|
282
|
-
const reset = "\x1b[0m";
|
|
283
|
-
|
|
284
|
-
for (let i = 0; i < totalLines; i++) {
|
|
285
|
-
const line = diff.lines[i];
|
|
286
|
-
|
|
287
|
-
switch (line.type) {
|
|
288
|
-
case "hunk":
|
|
289
|
-
lines.push(truncateToTermWidth(`${hunkFg}${line.content}${reset}`));
|
|
290
|
-
break;
|
|
291
|
-
case "add":
|
|
292
|
-
lines.push(truncateToTermWidth(`${addBg}${addFg}+${reset}${addBg} ${lnum(null, 4)} ${lnum(line.newLine, 4)} │ ${line.content}${reset}`));
|
|
293
|
-
break;
|
|
294
|
-
case "remove":
|
|
295
|
-
lines.push(truncateToTermWidth(`${remBg}${remFg}-${reset}${remBg} ${lnum(line.oldLine, 4)} ${lnum(null, 4)} │ ${line.content}${reset}`));
|
|
296
|
-
break;
|
|
297
|
-
case "context":
|
|
298
|
-
lines.push(truncateToTermWidth(` ${headerFg}${lnum(line.oldLine, 4)} ${lnum(line.newLine, 4)}${reset} │ ${line.content}`));
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (truncated) {
|
|
304
|
-
lines.push(`${headerFg}... (${diff.lines.length - max} more lines)${reset}`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return lines.join("\n");
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Render a diff in split (side-by-side) view.
|
|
312
|
-
* Auto-falls back to unified on narrow terminals.
|
|
313
|
-
*
|
|
314
|
-
* @param diff - Parsed diff
|
|
315
|
-
* @param language - Shiki language for syntax highlighting
|
|
316
|
-
* @param max - Maximum lines to render
|
|
317
|
-
* @param dc - Diff colors
|
|
318
|
-
*/
|
|
319
|
-
export function renderSplit(
|
|
320
|
-
diff: ParsedDiff,
|
|
321
|
-
language: string,
|
|
322
|
-
max: number,
|
|
323
|
-
dc: DiffColors,
|
|
324
|
-
): string {
|
|
325
|
-
const tw = termW();
|
|
326
|
-
|
|
327
|
-
// Auto-fallback to unified
|
|
328
|
-
if (!shouldUseSplit(diff, tw, max)) {
|
|
329
|
-
return renderUnified(diff, language, max, dc);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const lines: string[] = [];
|
|
333
|
-
const codeWidth = Math.floor((tw - 12) / 2); // 12 chars for line nums, separators, padding
|
|
334
|
-
const totalLines = Math.min(diff.lines.length, max);
|
|
335
|
-
const truncated = diff.lines.length > max;
|
|
336
|
-
|
|
337
|
-
const addBg = hexToBgAnsi(dc.addBg);
|
|
338
|
-
const remBg = hexToBgAnsi(dc.remBg);
|
|
339
|
-
const addFg = hexToFgAnsi(dc.addFg);
|
|
340
|
-
const remFg = hexToFgAnsi(dc.remFg);
|
|
341
|
-
const hunkFg = hexToFgAnsi(dc.hunkFg);
|
|
342
|
-
const headerFg = hexToFgAnsi(dc.headerFg);
|
|
343
|
-
const reset = "\x1b[0m";
|
|
344
|
-
|
|
345
|
-
// Group lines into left (old) and right (new) columns
|
|
346
|
-
// Build the left and right columns for each visual line
|
|
347
|
-
const leftLines: string[] = [];
|
|
348
|
-
const rightLines: string[] = [];
|
|
349
|
-
|
|
350
|
-
for (let i = 0; i < totalLines; i++) {
|
|
351
|
-
const line = diff.lines[i];
|
|
352
|
-
|
|
353
|
-
switch (line.type) {
|
|
354
|
-
case "hunk": {
|
|
355
|
-
// Hunk header spans both columns
|
|
356
|
-
const padded = ` ${hunkFg}${line.content}${reset}`;
|
|
357
|
-
const halfWidth = Math.floor(tw / 2);
|
|
358
|
-
leftLines.push(fit(padded, halfWidth));
|
|
359
|
-
rightLines.push("");
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
case "add": {
|
|
363
|
-
// Left: empty; Right: added line
|
|
364
|
-
leftLines.push(`${headerFg}${" ".repeat(codeWidth)}${reset}`);
|
|
365
|
-
rightLines.push(`${addBg}${addFg}+${reset}${addBg} ${lnum(line.newLine, 4)} │ ${fit(line.content, codeWidth - 8)}${reset}`);
|
|
366
|
-
break;
|
|
367
|
-
}
|
|
368
|
-
case "remove": {
|
|
369
|
-
// Left: removed line; Right: empty
|
|
370
|
-
leftLines.push(`${remBg}${remFg}-${reset}${remBg} ${lnum(line.oldLine, 4)} │ ${fit(line.content, codeWidth - 8)}${reset}`);
|
|
371
|
-
rightLines.push(`${headerFg}${" ".repeat(codeWidth)}${reset}`);
|
|
372
|
-
break;
|
|
373
|
-
}
|
|
374
|
-
case "context": {
|
|
375
|
-
// Both columns show the same context line
|
|
376
|
-
leftLines.push(`${headerFg}${lnum(line.oldLine, 4)}${reset} │ ${fit(line.content, codeWidth - 7)}`);
|
|
377
|
-
rightLines.push(`${headerFg}${lnum(line.newLine, 4)}${reset} │ ${fit(line.content, codeWidth - 7)}`);
|
|
378
|
-
break;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Combine left and right columns
|
|
384
|
-
for (let i = 0; i < leftLines.length; i++) {
|
|
385
|
-
const left = leftLines[i];
|
|
386
|
-
const right = rightLines[i];
|
|
387
|
-
lines.push(truncateToTermWidth(`${left} │ ${right}`));
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (truncated) {
|
|
391
|
-
lines.push(`${headerFg}... (${diff.lines.length - max} more lines)${reset}`);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return lines.join("\n");
|
|
395
|
-
}
|
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @pi-unipi/utility — Diff Theme System
|
|
3
|
-
*
|
|
4
|
-
* Color presets, resolution chain, and hex ↔ ANSI conversion for diff rendering.
|
|
5
|
-
*
|
|
6
|
-
* Resolution chain: env vars → per-color overrides → preset → auto-derive → hardcoded
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { readDiffSettings, type DiffSettings } from "./settings.js";
|
|
10
|
-
|
|
11
|
-
// ─── Types ──────────────────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
/** Diff color configuration */
|
|
14
|
-
export interface DiffColors {
|
|
15
|
-
/** Background for added lines */
|
|
16
|
-
addBg: string;
|
|
17
|
-
/** Foreground for added line content */
|
|
18
|
-
addFg: string;
|
|
19
|
-
/** Background for removed lines */
|
|
20
|
-
remBg: string;
|
|
21
|
-
/** Foreground for removed line content */
|
|
22
|
-
remFg: string;
|
|
23
|
-
/** Background for added word-level highlights */
|
|
24
|
-
addWordBg: string;
|
|
25
|
-
/** Background for removed word-level highlights */
|
|
26
|
-
remWordBg: string;
|
|
27
|
-
/** Hunk header foreground */
|
|
28
|
-
hunkFg: string;
|
|
29
|
-
/** Header info foreground */
|
|
30
|
-
headerFg: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Diff theme preset */
|
|
34
|
-
export interface DiffPreset {
|
|
35
|
-
name: string;
|
|
36
|
-
description: string;
|
|
37
|
-
colors: DiffColors;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** An ANSI color code (e.g. "\x1b[38;2;255;0;0m") */
|
|
41
|
-
type AnsiColor = string;
|
|
42
|
-
|
|
43
|
-
// ─── Built-in Presets ───────────────────────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
const PRESETS: Record<string, DiffPreset> = {
|
|
46
|
-
default: {
|
|
47
|
-
name: "default",
|
|
48
|
-
description: "Classic green/red diff colors",
|
|
49
|
-
colors: {
|
|
50
|
-
addBg: "#1a3a1a",
|
|
51
|
-
addFg: "#b5e8b5",
|
|
52
|
-
remBg: "#3a1a1a",
|
|
53
|
-
remFg: "#e8b5b5",
|
|
54
|
-
addWordBg: "#2d5a2d",
|
|
55
|
-
remWordBg: "#5a2d2d",
|
|
56
|
-
hunkFg: "#8888ff",
|
|
57
|
-
headerFg: "#888888",
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
midnight: {
|
|
61
|
-
name: "midnight",
|
|
62
|
-
description: "Deep blue-tinted diff colors",
|
|
63
|
-
colors: {
|
|
64
|
-
addBg: "#0a2a3a",
|
|
65
|
-
addFg: "#a5d8e8",
|
|
66
|
-
remBg: "#3a0a1a",
|
|
67
|
-
remFg: "#e8a5c5",
|
|
68
|
-
addWordBg: "#1a4a5a",
|
|
69
|
-
remWordBg: "#5a1a3a",
|
|
70
|
-
hunkFg: "#6688cc",
|
|
71
|
-
headerFg: "#666688",
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
subtle: {
|
|
75
|
-
name: "subtle",
|
|
76
|
-
description: "Muted, low-contrast diff colors",
|
|
77
|
-
colors: {
|
|
78
|
-
addBg: "#1e2e1e",
|
|
79
|
-
addFg: "#a0b8a0",
|
|
80
|
-
remBg: "#2e1e1e",
|
|
81
|
-
remFg: "#b8a0a0",
|
|
82
|
-
addWordBg: "#2a3a2a",
|
|
83
|
-
remWordBg: "#3a2a2a",
|
|
84
|
-
hunkFg: "#7777aa",
|
|
85
|
-
headerFg: "#777777",
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
neon: {
|
|
89
|
-
name: "neon",
|
|
90
|
-
description: "High-contrast vivid diff colors",
|
|
91
|
-
colors: {
|
|
92
|
-
addBg: "#003300",
|
|
93
|
-
addFg: "#66ff66",
|
|
94
|
-
remBg: "#330000",
|
|
95
|
-
remFg: "#ff6666",
|
|
96
|
-
addWordBg: "#005500",
|
|
97
|
-
remWordBg: "#550000",
|
|
98
|
-
hunkFg: "#6666ff",
|
|
99
|
-
headerFg: "#999999",
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// ─── Preset Access ──────────────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Get a diff preset by name. Falls back to "default" if not found.
|
|
108
|
-
*/
|
|
109
|
-
export function getPreset(name: string): DiffPreset {
|
|
110
|
-
return PRESETS[name] ?? PRESETS.default;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get all available preset names.
|
|
115
|
-
*/
|
|
116
|
-
export function getPresetNames(): string[] {
|
|
117
|
-
return Object.keys(PRESETS);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Get all presets with their metadata.
|
|
122
|
-
*/
|
|
123
|
-
export function getAllPresets(): DiffPreset[] {
|
|
124
|
-
return Object.values(PRESETS);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ─── Hex ↔ ANSI Conversion ──────────────────────────────────────────────────────
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Parse a hex color string (#RRGGBB or #RGB) to [r, g, b].
|
|
131
|
-
*/
|
|
132
|
-
export function hexToRgb(hex: string): [number, number, number] {
|
|
133
|
-
let h = hex.replace(/^#/, "");
|
|
134
|
-
if (h.length === 3) {
|
|
135
|
-
h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
|
136
|
-
}
|
|
137
|
-
const r = parseInt(h.substring(0, 2), 16);
|
|
138
|
-
const g = parseInt(h.substring(2, 4), 16);
|
|
139
|
-
const b = parseInt(h.substring(4, 6), 16);
|
|
140
|
-
return [r, g, b];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Convert [r, g, b] to hex string (#RRGGBB).
|
|
145
|
-
*/
|
|
146
|
-
export function rgbToHex(r: number, g: number, b: number): string {
|
|
147
|
-
const toHex = (n: number) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
|
|
148
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Convert a hex color to ANSI 24-bit foreground escape.
|
|
153
|
-
*/
|
|
154
|
-
export function hexToFgAnsi(hex: string): AnsiColor {
|
|
155
|
-
const [r, g, b] = hexToRgb(hex);
|
|
156
|
-
return `\x1b[38;2;${r};${g};${b}m`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Convert a hex color to ANSI 24-bit background escape.
|
|
161
|
-
*/
|
|
162
|
-
export function hexToBgAnsi(hex: string): AnsiColor {
|
|
163
|
-
const [r, g, b] = hexToRgb(hex);
|
|
164
|
-
return `\x1b[48;2;${r};${g};${b}m`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Extract hex color from an ANSI 24-bit foreground escape sequence.
|
|
169
|
-
* Returns null if not a 24-bit color.
|
|
170
|
-
*/
|
|
171
|
-
export function ansiFgToHex(ansi: string): string | null {
|
|
172
|
-
const match = ansi.match(/\x1b\[38;2;(\d+);(\d+);(\d+)m/);
|
|
173
|
-
if (!match) return null;
|
|
174
|
-
return rgbToHex(parseInt(match[1]), parseInt(match[2]), parseInt(match[3]));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Extract hex color from an ANSI 24-bit background escape sequence.
|
|
179
|
-
* Returns null if not a 24-bit color.
|
|
180
|
-
*/
|
|
181
|
-
export function ansiBgToHex(ansi: string): string | null {
|
|
182
|
-
const match = ansi.match(/\x1b\[48;2;(\d+);(\d+);(\d+)m/);
|
|
183
|
-
if (!match) return null;
|
|
184
|
-
return rgbToHex(parseInt(match[1]), parseInt(match[2]), parseInt(match[3]));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ─── Color Resolution ───────────────────────────────────────────────────────────
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Load the diff configuration from settings.
|
|
191
|
-
*/
|
|
192
|
-
export function loadDiffConfig(): DiffSettings {
|
|
193
|
-
return readDiffSettings();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Mix a foreground color with a background color at a given ratio.
|
|
198
|
-
* Used for auto-deriving diff backgrounds from pi theme accents.
|
|
199
|
-
*/
|
|
200
|
-
export function mixColors(fg: string, bg: string, ratio: number): string {
|
|
201
|
-
const [fr, fg_, fb] = hexToRgb(fg);
|
|
202
|
-
const [br, bg_, bb] = hexToRgb(bg);
|
|
203
|
-
const r = Math.round(fr * ratio + br * (1 - ratio));
|
|
204
|
-
const g = Math.round(fg_ * ratio + bg_ * (1 - ratio));
|
|
205
|
-
const b = Math.round(fb * ratio + bb * (1 - ratio));
|
|
206
|
-
return rgbToHex(r, g, b);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Auto-derive diff background colors from a pi theme.
|
|
211
|
-
* Mixes accent/success/error colors with a base background.
|
|
212
|
-
*/
|
|
213
|
-
export function autoDeriveBgFromTheme(theme: any): DiffColors | null {
|
|
214
|
-
try {
|
|
215
|
-
// Try to get theme colors from pi's Theme object
|
|
216
|
-
const baseBg = theme?.colors?.customMessageBg || theme?.colors?.background || "#1a1a2e";
|
|
217
|
-
const successColor = theme?.colors?.toolSuccess || theme?.colors?.success || "#22c55e";
|
|
218
|
-
const errorColor = theme?.colors?.toolError || theme?.colors?.error || "#ef4444";
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
addBg: mixColors(successColor, baseBg, 0.15),
|
|
222
|
-
addFg: mixColors(successColor, "#ffffff", 0.7),
|
|
223
|
-
remBg: mixColors(errorColor, baseBg, 0.15),
|
|
224
|
-
remFg: mixColors(errorColor, "#ffffff", 0.7),
|
|
225
|
-
addWordBg: mixColors(successColor, baseBg, 0.25),
|
|
226
|
-
remWordBg: mixColors(errorColor, baseBg, 0.25),
|
|
227
|
-
hunkFg: "#8888ff",
|
|
228
|
-
headerFg: "#888888",
|
|
229
|
-
};
|
|
230
|
-
} catch {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Check if a value looks like a hex color.
|
|
237
|
-
*/
|
|
238
|
-
function isHexColor(v: string): boolean {
|
|
239
|
-
return /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(v);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Resolve diff colors using the full chain:
|
|
244
|
-
* env vars → per-color overrides → preset → auto-derive → hardcoded
|
|
245
|
-
*
|
|
246
|
-
* @param theme - Optional pi Theme object for auto-derivation
|
|
247
|
-
*/
|
|
248
|
-
export function resolveDiffColors(theme?: any): DiffColors {
|
|
249
|
-
const config = loadDiffConfig();
|
|
250
|
-
const preset = getPreset(config.theme);
|
|
251
|
-
|
|
252
|
-
// Start with preset colors
|
|
253
|
-
let colors = { ...preset.colors };
|
|
254
|
-
|
|
255
|
-
// Layer: auto-derive from pi theme (if available)
|
|
256
|
-
if (theme) {
|
|
257
|
-
const derived = autoDeriveBgFromTheme(theme);
|
|
258
|
-
if (derived) {
|
|
259
|
-
// Auto-derived colors fill in where preset has defaults
|
|
260
|
-
colors = {
|
|
261
|
-
addBg: colors.addBg || derived.addBg,
|
|
262
|
-
addFg: colors.addFg || derived.addFg,
|
|
263
|
-
remBg: colors.remBg || derived.remBg,
|
|
264
|
-
remFg: colors.remFg || derived.remFg,
|
|
265
|
-
addWordBg: colors.addWordBg || derived.addWordBg,
|
|
266
|
-
remWordBg: colors.remWordBg || derived.remWordBg,
|
|
267
|
-
hunkFg: colors.hunkFg || derived.hunkFg,
|
|
268
|
-
headerFg: colors.headerFg || derived.headerFg,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Layer: environment variable overrides
|
|
274
|
-
const envMap: Record<string, keyof DiffColors> = {
|
|
275
|
-
DIFF_ADD_BG: "addBg",
|
|
276
|
-
DIFF_ADD_FG: "addFg",
|
|
277
|
-
DIFF_REM_BG: "remBg",
|
|
278
|
-
DIFF_REM_FG: "remFg",
|
|
279
|
-
DIFF_ADD_WORD_BG: "addWordBg",
|
|
280
|
-
DIFF_REM_WORD_BG: "remWordBg",
|
|
281
|
-
DIFF_HUNK_FG: "hunkFg",
|
|
282
|
-
DIFF_HEADER_FG: "headerFg",
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
for (const [envKey, colorKey] of Object.entries(envMap)) {
|
|
286
|
-
const envVal = process.env[envKey];
|
|
287
|
-
if (envVal && isHexColor(envVal)) {
|
|
288
|
-
colors[colorKey] = envVal;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Layer: per-color overrides from settings (if we add diffColors to settings later)
|
|
293
|
-
// Currently deferred — spec says "Out of Scope"
|
|
294
|
-
|
|
295
|
-
return colors;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Apply the diff palette to create ANSI escape functions.
|
|
300
|
-
* Returns an object with helper functions for coloring diff output.
|
|
301
|
-
*/
|
|
302
|
-
export function applyDiffPalette(theme?: any) {
|
|
303
|
-
const colors = resolveDiffColors(theme);
|
|
304
|
-
|
|
305
|
-
return {
|
|
306
|
-
colors,
|
|
307
|
-
addBg: (s: string) => `${hexToBgAnsi(colors.addBg)}${s}\x1b[0m`,
|
|
308
|
-
addFg: (s: string) => `${hexToFgAnsi(colors.addFg)}${s}\x1b[0m`,
|
|
309
|
-
remBg: (s: string) => `${hexToBgAnsi(colors.remBg)}${s}\x1b[0m`,
|
|
310
|
-
remFg: (s: string) => `${hexToFgAnsi(colors.remFg)}${s}\x1b[0m`,
|
|
311
|
-
addWordBg: (s: string) => `${hexToBgAnsi(colors.addWordBg)}${s}\x1b[0m`,
|
|
312
|
-
remWordBg: (s: string) => `${hexToBgAnsi(colors.remWordBg)}${s}\x1b[0m`,
|
|
313
|
-
hunkFg: (s: string) => `${hexToFgAnsi(colors.hunkFg)}${s}\x1b[0m`,
|
|
314
|
-
headerFg: (s: string) => `${hexToFgAnsi(colors.headerFg)}${s}\x1b[0m`,
|
|
315
|
-
};
|
|
316
|
-
}
|