@heyhuynhgiabuu/pi-diff 0.1.2 → 0.1.4
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/package.json +1 -1
- package/src/index.ts +65 -37
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -169,6 +169,16 @@ function deriveBgFromFg(fgAnsi: string, intensity: number): string {
|
|
|
169
169
|
return `\x1b[48;2;${r};${g};${b}m`;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/** Mix an accent color into a base color at the given intensity (0.0–1.0).
|
|
173
|
+
* Returns an ANSI 24-bit background escape. Used to derive diff backgrounds
|
|
174
|
+
* that blend with the tool box background (toolSuccessBg). */
|
|
175
|
+
function mixBg(base: { r: number; g: number; b: number }, accent: { r: number; g: number; b: number }, intensity: number): string {
|
|
176
|
+
const r = Math.round(base.r + (accent.r - base.r) * intensity);
|
|
177
|
+
const g = Math.round(base.g + (accent.g - base.g) * intensity);
|
|
178
|
+
const b = Math.round(base.b + (accent.b - base.b) * intensity);
|
|
179
|
+
return `\x1b[48;2;${r};${g};${b}m`;
|
|
180
|
+
}
|
|
181
|
+
|
|
172
182
|
/** Whether auto-derive from theme is still pending (runs lazily on first render). */
|
|
173
183
|
let _autoDerivePending = true;
|
|
174
184
|
|
|
@@ -176,37 +186,44 @@ let _autoDerivePending = true;
|
|
|
176
186
|
let _hasExplicitBgConfig = false;
|
|
177
187
|
|
|
178
188
|
/** Auto-derive all diff background colors from the pi theme's fg diff colors.
|
|
179
|
-
*
|
|
189
|
+
* Reads toolSuccessBg as the base and mixes accent colors into it.
|
|
190
|
+
* Falls back to black (0,0,0) as base if toolSuccessBg is unavailable. */
|
|
180
191
|
function autoDeriveBgFromTheme(theme: any): void {
|
|
181
192
|
if (!theme?.getFgAnsi) return;
|
|
182
193
|
try {
|
|
183
194
|
const fgAdd = theme.getFgAnsi("toolDiffAdded");
|
|
184
195
|
const fgDel = theme.getFgAnsi("toolDiffRemoved");
|
|
196
|
+
const addRgb = parseAnsiRgb(fgAdd);
|
|
197
|
+
const delRgb = parseAnsiRgb(fgDel);
|
|
198
|
+
if (!addRgb || !delRgb) return;
|
|
185
199
|
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
// Read toolSuccessBg as the base background color
|
|
201
|
+
let base = { r: 0, g: 0, b: 0 };
|
|
202
|
+
if (theme.getBgAnsi) {
|
|
203
|
+
try {
|
|
204
|
+
const bgAnsi = theme.getBgAnsi("toolSuccessBg");
|
|
205
|
+
const parsed = parseAnsiRgb(bgAnsi);
|
|
206
|
+
if (parsed) {
|
|
207
|
+
base = parsed;
|
|
208
|
+
BG_BASE = bgAnsi;
|
|
209
|
+
}
|
|
210
|
+
} catch { /* no toolSuccessBg — use black */ }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Line backgrounds — subtle accent mixed into base (8–10%)
|
|
214
|
+
BG_ADD = mixBg(base, addRgb, 0.08);
|
|
215
|
+
BG_DEL = mixBg(base, delRgb, 0.10);
|
|
191
216
|
|
|
192
217
|
// Word-level highlights — more visible (20–22%)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const bgDelW = deriveBgFromFg(fgDel, 0.22);
|
|
196
|
-
if (bgDelW) BG_DEL_W = bgDelW;
|
|
218
|
+
BG_ADD_W = mixBg(base, addRgb, 0.20);
|
|
219
|
+
BG_DEL_W = mixBg(base, delRgb, 0.22);
|
|
197
220
|
|
|
198
221
|
// Gutters — subtler than lines (5–6%)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const bgGD = deriveBgFromFg(fgDel, 0.06);
|
|
202
|
-
if (bgGD) BG_GUTTER_DEL = bgGD;
|
|
222
|
+
BG_GUTTER_ADD = mixBg(base, addRgb, 0.05);
|
|
223
|
+
BG_GUTTER_DEL = mixBg(base, delRgb, 0.06);
|
|
203
224
|
|
|
204
|
-
// Empty filler
|
|
205
|
-
|
|
206
|
-
if (addRgb) {
|
|
207
|
-
const lum = Math.round((addRgb.r * 0.3 + addRgb.g * 0.6 + addRgb.b * 0.1) * 0.04);
|
|
208
|
-
BG_EMPTY = `\x1b[48;2;${lum};${lum};${lum}m`;
|
|
209
|
-
}
|
|
225
|
+
// Empty filler and context — match the base
|
|
226
|
+
BG_EMPTY = BG_BASE;
|
|
210
227
|
|
|
211
228
|
// Rebuild derived constants
|
|
212
229
|
DIVIDER = `${FG_RULE}│${RST}`;
|
|
@@ -383,7 +400,7 @@ const BORDER_BAR = "▌";
|
|
|
383
400
|
/** Generate a dense diagonal stripe fill for empty filler cells.
|
|
384
401
|
* Solid ╱ characters — uniform direction like CSS diagonal hatching. */
|
|
385
402
|
function stripes(w: number, _rowOffset: number): string {
|
|
386
|
-
return FG_STRIPE + "╱".repeat(w) + RST;
|
|
403
|
+
return BG_BASE + FG_STRIPE + "╱".repeat(w) + RST;
|
|
387
404
|
}
|
|
388
405
|
|
|
389
406
|
let DIVIDER = `${FG_RULE}│${RST}`;
|
|
@@ -392,6 +409,7 @@ const ANSI_RE = new RegExp(`${ESC_RE}\\[[0-9;]*m`, "g");
|
|
|
392
409
|
const ANSI_CAPTURE_RE = new RegExp(`${ESC_RE}\\[([^m]*)m`, "g");
|
|
393
410
|
const ANSI_PARAM_CAPTURE_RE = new RegExp(`${ESC_RE}\\[([0-9;]*)m`, "g");
|
|
394
411
|
const BG_DEFAULT = "\x1b[49m"; // reset to terminal default background
|
|
412
|
+
let BG_BASE = BG_DEFAULT; // tool box base bg — updated from theme's toolSuccessBg
|
|
395
413
|
|
|
396
414
|
// ---------------------------------------------------------------------------
|
|
397
415
|
// Theme-aware diff colors
|
|
@@ -407,8 +425,18 @@ interface DiffColors {
|
|
|
407
425
|
let DEFAULT_DIFF_COLORS: DiffColors = { fgAdd: FG_ADD, fgDel: FG_DEL, fgCtx: FG_DIM };
|
|
408
426
|
|
|
409
427
|
/** Resolve diff fg colors from theme (if available), falling back to hardcoded ANSI.
|
|
410
|
-
* On first call with a valid theme, auto-derives bg colors if no explicit config was set.
|
|
428
|
+
* On first call with a valid theme, auto-derives bg colors if no explicit config was set.
|
|
429
|
+
* Always reads toolSuccessBg for BG_BASE (used for context line backgrounds). */
|
|
411
430
|
function resolveDiffColors(theme?: any): DiffColors {
|
|
431
|
+
// Always read toolSuccessBg for BG_BASE (even with explicit config)
|
|
432
|
+
if (theme?.getBgAnsi && BG_BASE === BG_DEFAULT) {
|
|
433
|
+
try {
|
|
434
|
+
const bgAnsi = theme.getBgAnsi("toolSuccessBg");
|
|
435
|
+
const parsed = parseAnsiRgb(bgAnsi);
|
|
436
|
+
if (parsed) BG_BASE = bgAnsi;
|
|
437
|
+
} catch { /* ignore */ }
|
|
438
|
+
}
|
|
439
|
+
|
|
412
440
|
// Auto-derive bg colors from theme on first render (if no explicit preset/overrides)
|
|
413
441
|
if (_autoDerivePending && theme?.getFgAnsi) {
|
|
414
442
|
autoDeriveBgFromTheme(theme);
|
|
@@ -637,7 +665,7 @@ function summarize(a: number, d: number): string {
|
|
|
637
665
|
}
|
|
638
666
|
|
|
639
667
|
function rule(w: number): string {
|
|
640
|
-
return `${FG_RULE}${"─".repeat(w)}${RST}`;
|
|
668
|
+
return `${BG_BASE}${FG_RULE}${"─".repeat(w)}${RST}`;
|
|
641
669
|
}
|
|
642
670
|
|
|
643
671
|
/**
|
|
@@ -954,7 +982,7 @@ async function renderUnified(
|
|
|
954
982
|
bodyBg = "",
|
|
955
983
|
): void {
|
|
956
984
|
const borderFg = sign === "-" ? dc.fgDel : sign === "+" ? dc.fgAdd : "";
|
|
957
|
-
const border = borderFg ? `${borderFg}${BORDER_BAR}${RST}` : `${
|
|
985
|
+
const border = borderFg ? `${borderFg}${BORDER_BAR}${RST}` : `${BG_BASE} `;
|
|
958
986
|
const numFg = borderFg || FG_LNUM;
|
|
959
987
|
const gutter = `${border}${gutterBg}${lnum(num, nw, numFg)}${signFg}${sign}${RST} ${DIVIDER} `;
|
|
960
988
|
const contGutter = `${border}${gutterBg}${" ".repeat(nw + 1)}${RST} ${DIVIDER} `;
|
|
@@ -974,7 +1002,7 @@ async function renderUnified(
|
|
|
974
1002
|
const pad = Math.max(0, totalW - label.length - 2);
|
|
975
1003
|
const half1 = Math.floor(pad / 2),
|
|
976
1004
|
half2 = pad - half1;
|
|
977
|
-
out.push(`${FG_DIM}${"─".repeat(half1)}${label}${"─".repeat(half2)}${RST}`);
|
|
1005
|
+
out.push(`${BG_BASE}${FG_DIM}${"─".repeat(half1)}${label}${"─".repeat(half2)}${RST}`);
|
|
978
1006
|
idx++;
|
|
979
1007
|
continue;
|
|
980
1008
|
}
|
|
@@ -982,7 +1010,7 @@ async function renderUnified(
|
|
|
982
1010
|
// Context line — dimmed, single line number
|
|
983
1011
|
if (l.type === "ctx") {
|
|
984
1012
|
const hl = oldHL[oI] ?? l.content;
|
|
985
|
-
emitRow(l.newNum, " ",
|
|
1013
|
+
emitRow(l.newNum, " ", BG_BASE, dc.fgCtx, `${BG_BASE}${DIM}${hl}`, BG_BASE);
|
|
986
1014
|
oI++;
|
|
987
1015
|
nI++;
|
|
988
1016
|
idx++;
|
|
@@ -1034,7 +1062,7 @@ async function renderUnified(
|
|
|
1034
1062
|
|
|
1035
1063
|
out.push(rule(tw));
|
|
1036
1064
|
if (diff.lines.length > vis.length) {
|
|
1037
|
-
out.push(`${FG_DIM} … ${diff.lines.length - vis.length} more lines${RST}`);
|
|
1065
|
+
out.push(`${BG_BASE}${FG_DIM} … ${diff.lines.length - vis.length} more lines${RST}`);
|
|
1038
1066
|
}
|
|
1039
1067
|
return out.join("\n");
|
|
1040
1068
|
}
|
|
@@ -1120,21 +1148,21 @@ async function renderSplit(
|
|
|
1120
1148
|
if (line.type === "sep") {
|
|
1121
1149
|
const gap = line.newNum;
|
|
1122
1150
|
const label = gap && gap > 0 ? `··· ${gap} lines ···` : "···";
|
|
1123
|
-
const g =
|
|
1124
|
-
return { gutter: g, contGutter: g, bodyRows: [`${FG_DIM}${fit(label, cw)}${RST}`] };
|
|
1151
|
+
const g = `${BG_BASE} ${FG_DIM}${fit("", nw + 2)}${RST}${FG_RULE}│${RST} `;
|
|
1152
|
+
return { gutter: g, contGutter: g, bodyRows: [`${BG_BASE}${FG_DIM}${fit(label, cw)}${RST}`] };
|
|
1125
1153
|
}
|
|
1126
1154
|
|
|
1127
1155
|
const isDel = line.type === "del",
|
|
1128
1156
|
isAdd = line.type === "add";
|
|
1129
|
-
const gBg = isDel ? BG_GUTTER_DEL : isAdd ? BG_GUTTER_ADD :
|
|
1130
|
-
const cBg = isDel ? BG_DEL : isAdd ? BG_ADD :
|
|
1157
|
+
const gBg = isDel ? BG_GUTTER_DEL : isAdd ? BG_GUTTER_ADD : BG_BASE;
|
|
1158
|
+
const cBg = isDel ? BG_DEL : isAdd ? BG_ADD : BG_BASE;
|
|
1131
1159
|
const sFg = isDel ? dc.fgDel : isAdd ? dc.fgAdd : dc.fgCtx;
|
|
1132
1160
|
const sign = isDel ? "-" : isAdd ? "+" : " ";
|
|
1133
1161
|
const num = isDel ? line.oldNum : isAdd ? line.newNum : side === "left" ? line.oldNum : line.newNum;
|
|
1134
1162
|
|
|
1135
1163
|
// Border bar + colored line numbers for changed lines
|
|
1136
1164
|
const borderFg = isDel ? dc.fgDel : isAdd ? dc.fgAdd : "";
|
|
1137
|
-
const border = borderFg ? `${borderFg}${BORDER_BAR}${RST}` : ` ${
|
|
1165
|
+
const border = borderFg ? `${borderFg}${BORDER_BAR}${RST}` : ` ${BG_BASE}`;
|
|
1138
1166
|
const numFg = borderFg || FG_LNUM;
|
|
1139
1167
|
|
|
1140
1168
|
let body: string;
|
|
@@ -1143,7 +1171,7 @@ async function renderSplit(
|
|
|
1143
1171
|
} else if (isDel || isAdd) {
|
|
1144
1172
|
body = `${cBg}${hl}`;
|
|
1145
1173
|
} else {
|
|
1146
|
-
body = `${
|
|
1174
|
+
body = `${BG_BASE}${DIM}${hl}`;
|
|
1147
1175
|
}
|
|
1148
1176
|
|
|
1149
1177
|
const gutter = `${border}${gBg}${lnum(num, nw, numFg)}${sFg}${BOLD}${sign}${RST} ${FG_RULE}│${RST} `;
|
|
@@ -1154,9 +1182,9 @@ async function renderSplit(
|
|
|
1154
1182
|
|
|
1155
1183
|
const out: string[] = [];
|
|
1156
1184
|
// Column headers — "old" / "new" positioned above line numbers
|
|
1157
|
-
const hdrOld = `${" ".repeat(Math.max(0, nw - 2))}${dc.fgDel}${DIM}old${RST}`;
|
|
1158
|
-
const hdrNew = `${" ".repeat(Math.max(0, nw - 2))}${dc.fgAdd}${DIM}new${RST}`;
|
|
1159
|
-
out.push(`${hdrOld}${" ".repeat(Math.max(0, half - nw - 1))}${FG_RULE}┊${RST}${hdrNew}`);
|
|
1185
|
+
const hdrOld = `${BG_BASE}${" ".repeat(Math.max(0, nw - 2))}${dc.fgDel}${DIM}old${RST}`;
|
|
1186
|
+
const hdrNew = `${BG_BASE}${" ".repeat(Math.max(0, nw - 2))}${dc.fgAdd}${DIM}new${RST}`;
|
|
1187
|
+
out.push(`${BG_BASE}${hdrOld}${" ".repeat(Math.max(0, half - nw - 1))}${FG_RULE}┊${RST}${hdrNew}`);
|
|
1160
1188
|
out.push(`${rule(half)}${FG_RULE}┊${RST}${rule(half)}`);
|
|
1161
1189
|
|
|
1162
1190
|
for (const r of vis) {
|
|
@@ -1202,7 +1230,7 @@ async function renderSplit(
|
|
|
1202
1230
|
|
|
1203
1231
|
out.push(`${rule(half)}${FG_RULE}┊${RST}${rule(half)}`);
|
|
1204
1232
|
if (rows.length > vis.length) {
|
|
1205
|
-
out.push(`${FG_DIM} … ${rows.length - vis.length} more lines${RST}`);
|
|
1233
|
+
out.push(`${BG_BASE}${FG_DIM} … ${rows.length - vis.length} more lines${RST}`);
|
|
1206
1234
|
}
|
|
1207
1235
|
return out.join("\n");
|
|
1208
1236
|
}
|