@genee/omp-opsx-addon 0.5.0 → 0.6.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/lib/usage-render.ts +60 -42
- package/package.json +1 -1
package/lib/usage-render.ts
CHANGED
|
@@ -48,13 +48,46 @@ export interface Painter {
|
|
|
48
48
|
header(label: string, brandAnsi: string): string;
|
|
49
49
|
/** Numeric balance text (e.g. "余额 ¥18.06"), text-colored. */
|
|
50
50
|
balance(text: string): string;
|
|
51
|
-
/**
|
|
52
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Waveform layer paint — btop row gradient over the provider's brand hue.
|
|
53
|
+
* `brandAnsi` is the raw brand SGR ('' → accent fallback, like header).
|
|
54
|
+
* Both rows share the hue family with a fixed, theme-independent step:
|
|
55
|
+
* 'bottom' a curated 256-color bright shade of the hue (aixterm bright /
|
|
56
|
+
* bold when the code isn't a mapped 16-color SGR), 'top' the plain brand.
|
|
57
|
+
*/
|
|
58
|
+
wave(brandAnsi: string, layer: 'top' | 'bottom', text: string): string;
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
const pctValue = (p: number): string => `${Math.round(p * 100)}%`;
|
|
56
62
|
const pctColor = (p: number): PainterColor => (p >= 0.85 ? 'error' : p >= 0.6 ? 'warning' : 'success');
|
|
57
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Curated 256-color bottom-row shade per 16-color brand hue (chart only —
|
|
66
|
+
* headers keep the raw brand SGR). The 256 palette's 16–255 range renders as
|
|
67
|
+
* FIXED rgb in every terminal, so the bottom/top step is guaranteed visible
|
|
68
|
+
* regardless of the active 16-color theme. Each shade is anchored on the
|
|
69
|
+
* de-facto standard dark-theme value of that hue (TokyoNight family) and is
|
|
70
|
+
* the SAME ink one purity/brightness step up — same hue direction, no hot
|
|
71
|
+
* magenta/red jumps that would break the "one color" read.
|
|
72
|
+
*/
|
|
73
|
+
const WAVE_SHADES: Record<string, string> = {
|
|
74
|
+
[X]: '38;5;204', // red #ff5f87 (TN #f7768e, denser)
|
|
75
|
+
[G]: '38;5;114', // green #87d787 (TN #9ece6a, denser)
|
|
76
|
+
[BLUE]: '38;5;75', // blue #5fafff (TN #7aa2f7, denser)
|
|
77
|
+
[PURPLE]: '38;5;177', // purple #d787ff (TN #bb9af7, denser)
|
|
78
|
+
[CYAN]: '38;5;81', // cyan #5fd7ff (TN #7dcfff, denser)
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** Aixterm bright variant of a 16-color brand code (`\x1b[3Nm` → `\x1b[9Nm`); undefined when not derivable. */
|
|
82
|
+
const brightVariant = (code: string): string | undefined => {
|
|
83
|
+
const m = code.match(/^\x1b\[3([0-7])m$/);
|
|
84
|
+
return m ? `\x1b[9${m[1]}m` : undefined;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** Bottom-row shade of a raw hue: curated 256 bright shade, aixterm bright, bold as the last step. */
|
|
88
|
+
const bottomShade = (hue: string): string =>
|
|
89
|
+
WAVE_SHADES[hue] ? `\x1b[${WAVE_SHADES[hue]}m` : brightVariant(hue) ?? BOLD + hue;
|
|
90
|
+
|
|
58
91
|
/** Pre-refactor hardcoded ANSI — the RPC/print fallback and the test baseline. */
|
|
59
92
|
export const ansiPainter: Painter = {
|
|
60
93
|
pct: (p) => {
|
|
@@ -65,21 +98,7 @@ export const ansiPainter: Painter = {
|
|
|
65
98
|
sep: ` ${D}│${R} `,
|
|
66
99
|
header: (label, brandAnsi) => c(brandAnsi ? BOLD + brandAnsi : BOLD, label),
|
|
67
100
|
balance: (text) => c(WHITE, text),
|
|
68
|
-
|
|
69
|
-
c(
|
|
70
|
-
color === 'error'
|
|
71
|
-
? X
|
|
72
|
-
: color === 'warning'
|
|
73
|
-
? Y
|
|
74
|
-
: color === 'success'
|
|
75
|
-
? G
|
|
76
|
-
: color === 'dim'
|
|
77
|
-
? D
|
|
78
|
-
: color === 'accent'
|
|
79
|
-
? CYAN
|
|
80
|
-
: WHITE,
|
|
81
|
-
text,
|
|
82
|
-
),
|
|
101
|
+
wave: (brandAnsi, layer, text) => c(layer === 'bottom' ? bottomShade(brandAnsi || CYAN) : brandAnsi || CYAN, text),
|
|
83
102
|
};
|
|
84
103
|
|
|
85
104
|
/** Status colors via the active host theme; brand headers keep raw ANSI. */
|
|
@@ -90,7 +109,12 @@ export function themePainter(theme: PainterTheme): Painter {
|
|
|
90
109
|
sep: ` ${theme.fg('dim', '│')} `,
|
|
91
110
|
header: (label, brandAnsi) => (brandAnsi ? c(brandAnsi, label) : theme.fg('accent', label)),
|
|
92
111
|
balance: (text) => theme.fg('text', text),
|
|
93
|
-
|
|
112
|
+
wave: (brandAnsi, layer, text) =>
|
|
113
|
+
brandAnsi
|
|
114
|
+
? c(layer === 'bottom' ? bottomShade(brandAnsi) : brandAnsi, text)
|
|
115
|
+
: layer === 'bottom'
|
|
116
|
+
? BOLD + theme.fg('accent', text)
|
|
117
|
+
: theme.fg('accent', text),
|
|
94
118
|
};
|
|
95
119
|
}
|
|
96
120
|
|
|
@@ -424,7 +448,7 @@ export function renderUsageReports(
|
|
|
424
448
|
// placeholder strip, keeping the column height stable.
|
|
425
449
|
if (consumptionTracks) {
|
|
426
450
|
const track = consumptionTracks.get(c.provider);
|
|
427
|
-
if (track) c.col.push(...renderConsumptionLines(track.samples, painter, waveCols));
|
|
451
|
+
if (track) c.col.push(...renderConsumptionLines(track.samples, painter, waveCols, providerLabel(c.provider).color));
|
|
428
452
|
}
|
|
429
453
|
if (pad) c.col = c.col.map((l) => padRight(l, blockWidth));
|
|
430
454
|
}
|
|
@@ -469,13 +493,6 @@ export function composeChar(leftRows: number, rightRows: number): string {
|
|
|
469
493
|
return String.fromCharCode(code);
|
|
470
494
|
}
|
|
471
495
|
|
|
472
|
-
/** Named-color band for a stock consumption level (`1 - remaining`): remaining ≥ 50% → success, ≥ 20% → warning, else error. */
|
|
473
|
-
export function consumptionColor(c: number): PainterColor {
|
|
474
|
-
if (c <= 0.5) return 'success';
|
|
475
|
-
if (c <= 0.8) return 'warning';
|
|
476
|
-
return 'error';
|
|
477
|
-
}
|
|
478
|
-
|
|
479
496
|
/** One provider's consumption track feeding its embedded mini chart. */
|
|
480
497
|
export interface ConsumptionTrack {
|
|
481
498
|
/** Per-interval consumption increments (raw values), newest last. */
|
|
@@ -498,12 +515,17 @@ export interface ConsumptionTrack {
|
|
|
498
515
|
* window yields a blank placeholder strip, never `undefined`, so every
|
|
499
516
|
* provider column keeps a stable chart area and the layout never jumps.
|
|
500
517
|
*
|
|
501
|
-
* Color is
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
518
|
+
* Color is a btop-style ROW gradient bound to the vertical position only —
|
|
519
|
+
* never to the data. The chart paints the provider's brand hue in two layers
|
|
520
|
+
* with a fixed step: the bottom line takes a curated 256-color bright shade
|
|
521
|
+
* of the hue (fixed rgb — visible under any 16-color theme; aixterm bright /
|
|
522
|
+
* bold when the code isn't mapped), the top line the plain brand — visibly
|
|
523
|
+
* stronger at the base, never washed out. Sliding samples reshape the
|
|
524
|
+
* envelope while every
|
|
525
|
+
* pixel's color stays put — the per-column intensity heat bands this
|
|
526
|
+
* replaces flipped green/yellow/red as the window moved, reading as flicker.
|
|
527
|
+
* Providers without a curated brand hue fall back to the theme accent.
|
|
528
|
+
* Quota urgency still reads in the usage text ("5h 0%", balance).
|
|
507
529
|
*
|
|
508
530
|
* Geometry unchanged: 1px per sample (half-char step), right-aligned; each
|
|
509
531
|
* character merges its two half-column samples with max and renders both
|
|
@@ -514,6 +536,7 @@ export function renderConsumptionLines(
|
|
|
514
536
|
samples: readonly number[],
|
|
515
537
|
painter: Painter = ansiPainter,
|
|
516
538
|
cols: number = WAVEFORM_COLS,
|
|
539
|
+
brandAnsi = '',
|
|
517
540
|
): string[] {
|
|
518
541
|
const n = samples.length;
|
|
519
542
|
const totalPx = cols * 2;
|
|
@@ -564,14 +587,10 @@ export function renderConsumptionLines(
|
|
|
564
587
|
return plot[m - 1] / max;
|
|
565
588
|
};
|
|
566
589
|
|
|
567
|
-
// Each half-column keeps its OWN interpolated height (two samples share one
|
|
568
|
-
// char, possibly at different heights — the pair-into-one-char geometry);
|
|
569
|
-
// only COLOR is blended (terminals can't half-tint a char, so the char
|
|
570
|
-
// takes the mean intensity of its two half-columns).
|
|
571
|
-
const accentChar = Math.floor((totalPx - 1) / 2);
|
|
572
590
|
// Two-line strip: full scale = 8px (2 Braille rows × 4 dot rows). Each
|
|
573
|
-
// half-column keeps its OWN interpolated height;
|
|
574
|
-
// (
|
|
591
|
+
// half-column keeps its OWN interpolated height; COLOR is ROW-bound only
|
|
592
|
+
// (bottom = 256 bright shade of the brand hue, top = plain brand) and
|
|
593
|
+
// never varies per column.
|
|
575
594
|
const rows = 2;
|
|
576
595
|
const lines = new Array<string>(rows).fill('');
|
|
577
596
|
for (let c = 0; c < cols; c++) {
|
|
@@ -587,10 +606,9 @@ export function renderConsumptionLines(
|
|
|
587
606
|
// never vanish under the dynamic scale.
|
|
588
607
|
const hL = l > 0 ? Math.max(1, Math.round(8 * l)) : 0;
|
|
589
608
|
const hR = r > 0 ? Math.max(1, Math.round(8 * r)) : 0;
|
|
590
|
-
const color = c === accentChar ? 'accent' : consumptionColor((l + r) / 2);
|
|
591
609
|
// Row 0 = top (px 4..8), row 1 = bottom (px 0..4) of each column.
|
|
592
|
-
lines[0] += painter.
|
|
593
|
-
lines[1] += painter.
|
|
610
|
+
lines[0] += painter.wave(brandAnsi, 'top', composeChar(Math.max(0, hL - 4), Math.max(0, hR - 4)));
|
|
611
|
+
lines[1] += painter.wave(brandAnsi, 'bottom', composeChar(Math.min(4, hL), Math.min(4, hR)));
|
|
594
612
|
}
|
|
595
613
|
return lines;
|
|
596
614
|
}
|
package/package.json
CHANGED