@xzzpig/pi-starline 0.7.1 → 0.7.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/FORK-CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Fork release notes
|
|
2
2
|
|
|
3
|
+
## 0.7.2 — pill text contrast on terminal-owned palettes
|
|
4
|
+
|
|
5
|
+
- Pick the pill text colour from the 256-colour cube (`38;5;16` black, `38;5;231` white) instead of the basic ANSI codes `30`/`97`. The basic codes belong to the terminal theme: herdr renders ANSI black as `#616161`, which left the automatic "dark" text at 1.76:1 on the muted pill background and 2.62:1 on the model pill. The cube's black and white are fixed by the standard, so contrast now holds in any palette (5.4–9.4:1 for the footer's default backgrounds).
|
|
6
|
+
- The change is two constants plus their comment in `extensions/starline/pill.ts`. It covers pill bodies, the thin divider drawn between same-background pills, and the bubble's selected row, all of which share `contrastTextSgr()`. Backgrounds, separators, caps, widths and click targets are unchanged, and an explicit `fg:` in `colors.*` still wins.
|
|
7
|
+
- Backgrounds that land on the terminal's own 16 colours are painted in reverse video (`\x1b[49m` + the palette colour as foreground + `\x1b[7m`): the palette colour stays the background and the text becomes the terminal's default background, a pair the palette is designed to keep readable. Only the terminal knows those RGBs — the standard table turned `48;5;3` into "dark yellow" when herdr paints it `#61AFEE`, which is why the contrast guess cannot be trusted there. Measured on the same pills: 2.62:1 → 8.61:1 (model/cwd) and 3.07:1 → 10.11:1 (cost); known-RGB backgrounds keep the cube black/white and land at 9.12:1. The predicate and SGR builders live in the fork-only `extensions/starline/fork-pill-legibility.ts`, with one seam each in `pill.ts` and `pill-bubble.ts`.
|
|
8
|
+
- Fork coverage lives in `test/fork-pill-contrast.test.ts`; `subtrees/pi-starline.json` records the sync-time re-apply. Drop the constants divergence once upstream stops deriving legibility from the terminal's basic palette, and the reverse-video seam once upstream handles terminal-owned backgrounds itself.
|
|
9
|
+
|
|
3
10
|
## 0.7.1 — Pi cursor compatibility
|
|
4
11
|
|
|
5
12
|
- Restore `underline` and `terminal` editor cursor styles on Pi 1.2 and later while preserving the existing Pi 1.1 cursor path.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fork-only legibility helpers for pill text.
|
|
3
|
+
*
|
|
4
|
+
* A pill's text colour is normally picked from the background's RGB, but the
|
|
5
|
+
* terminal owns the RGB of its own 16 colours: `38;5;3` is whatever the theme
|
|
6
|
+
* paints for palette slot 3 (herdr: `#61AFEE`), not the standard table's olive.
|
|
7
|
+
* Guessing from the standard table turned "light blue" into "dark yellow" and
|
|
8
|
+
* picked white text for a light pill.
|
|
9
|
+
*
|
|
10
|
+
* For those backgrounds the fork paints the text with reverse video instead:
|
|
11
|
+
* the palette colour becomes the background and the terminal's own default
|
|
12
|
+
* background becomes the text. A palette is designed to keep its colours
|
|
13
|
+
* readable against that default background, so the pair stays legible without
|
|
14
|
+
* the program knowing either RGB.
|
|
15
|
+
*
|
|
16
|
+
* See FORK-CHANGELOG.md; `test/fork-pill-contrast.test.ts` covers it.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { toForegroundSgr } from "./style";
|
|
20
|
+
|
|
21
|
+
const DEFAULT_BG = "\x1b[49m";
|
|
22
|
+
const REVERSE = "\x1b[7m";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* True when the background is one of the terminal's own 16 colours, whose RGB
|
|
26
|
+
* only the terminal knows. The 256-colour cube (`48;5;16`+) and truecolor are
|
|
27
|
+
* fixed by the standard, so those stay knowable.
|
|
28
|
+
*/
|
|
29
|
+
export function isTerminalOwnedBackground(background: string): boolean {
|
|
30
|
+
if (/^\x1b\[(4[0-7]|10[0-7])m$/.test(background)) return true;
|
|
31
|
+
const indexed = /^\x1b\[48;5;(\d{1,3})m$/.exec(background);
|
|
32
|
+
return indexed ? Number(indexed[1]) < 16 : false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* True when the spec names a foreground colour itself (`fg:…`). Such a pill
|
|
37
|
+
* keeps the colour the user asked for: reverse video would replace it.
|
|
38
|
+
*/
|
|
39
|
+
export function hasExplicitForeground(spec: string): boolean {
|
|
40
|
+
return /(^|\s)fg:/i.test(spec.trim());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The SGR that paints text on a terminal-owned background, or undefined when
|
|
45
|
+
* the background is knowable (the caller picks a contrast colour instead) or
|
|
46
|
+
* cannot be turned into a foreground colour.
|
|
47
|
+
*/
|
|
48
|
+
export function reverseVideoSgr(background: string): string | undefined {
|
|
49
|
+
if (!isTerminalOwnedBackground(background)) return undefined;
|
|
50
|
+
const foreground = toForegroundSgr(background);
|
|
51
|
+
return foreground ? `${DEFAULT_BG}${foreground}${REVERSE}` : undefined;
|
|
52
|
+
}
|
|
@@ -36,6 +36,7 @@ import type {
|
|
|
36
36
|
PillBubbleHandle,
|
|
37
37
|
PillBubbleSource,
|
|
38
38
|
} from "../../types/pill-api";
|
|
39
|
+
import { reverseVideoSgr } from "./fork-pill-legibility";
|
|
39
40
|
import { requestPointerShape } from "./mouse/pointer-shape";
|
|
40
41
|
import { contrastTextSgr } from "./pill";
|
|
41
42
|
import { resolveBackgroundSgr, type ThemeLike, toForegroundSgr } from "./style";
|
|
@@ -211,7 +212,10 @@ function buildBubble(input: {
|
|
|
211
212
|
const width = Math.max(8, Math.floor(input.width));
|
|
212
213
|
const innerWidth = Math.max(1, width - 4);
|
|
213
214
|
const background = input.background;
|
|
214
|
-
|
|
215
|
+
// A background the terminal owns has no knowable RGB, so its row is painted in
|
|
216
|
+
// reverse video: palette colour behind, terminal default background as text.
|
|
217
|
+
const reverse = input.foreground ? "" : (reverseVideoSgr(background) ?? "");
|
|
218
|
+
const foreground = input.foreground || (reverse ? "" : contrastTextSgr(background));
|
|
215
219
|
// The outline is the pill's colour as a foreground on the terminal's own
|
|
216
220
|
// background; nothing inside the box is filled.
|
|
217
221
|
const line = `${DEFAULT_BG}${toForegroundSgr(background) ?? ""}`;
|
|
@@ -222,7 +226,7 @@ function buildBubble(input: {
|
|
|
222
226
|
// underline covers the words and not the margin around them.
|
|
223
227
|
`${side} ${style}${text}${STYLE_OFF}${" ".repeat(Math.max(0, innerWidth - visibleWidth(text)))} ${side}`;
|
|
224
228
|
const selectedRow = (text: string) =>
|
|
225
|
-
`${side}${background}${foreground} ${text}${" ".repeat(
|
|
229
|
+
`${side}${reverse || background}${foreground} ${text}${" ".repeat(
|
|
226
230
|
Math.max(0, innerWidth - visibleWidth(text)),
|
|
227
231
|
)} ${RESET}${side}`;
|
|
228
232
|
|
|
@@ -13,6 +13,7 @@ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
|
13
13
|
|
|
14
14
|
import type { ColorSource, ColorSpec } from "./config";
|
|
15
15
|
import type { ExtensionStatusSegment } from "./extension-status";
|
|
16
|
+
import { hasExplicitForeground, reverseVideoSgr } from "./fork-pill-legibility";
|
|
16
17
|
import {
|
|
17
18
|
EXTENSION_STATUS_PREFIX,
|
|
18
19
|
EXTENSION_STATUS_SEGMENT,
|
|
@@ -25,9 +26,13 @@ const RESET = "\x1b[0m";
|
|
|
25
26
|
const BOLD = "\x1b[1m";
|
|
26
27
|
const DEFAULT_BG = "\x1b[49m";
|
|
27
28
|
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Text colours are the 256-colour cube's black and white (16/231), whose values
|
|
31
|
+
* the standard fixes. The basic named codes are not fixed: a terminal theme may
|
|
32
|
+
* map ANSI black to a mid grey, which leaves mid-tone pills hard to read.
|
|
33
|
+
*/
|
|
34
|
+
const TEXT_DARK = "\x1b[38;5;16m";
|
|
35
|
+
const TEXT_LIGHT = "\x1b[38;5;231m";
|
|
31
36
|
|
|
32
37
|
/** Catppuccin surface1 — the neutral for segments whose spec names no colour. */
|
|
33
38
|
const NEUTRAL_BG = "\x1b[48;2;69;71;90m";
|
|
@@ -164,6 +169,8 @@ type PreparedPill = {
|
|
|
164
169
|
bg: string;
|
|
165
170
|
fg: string;
|
|
166
171
|
bold: boolean;
|
|
172
|
+
/** Body SGR for a terminal-owned background, which paints text in reverse. */
|
|
173
|
+
reverse: string;
|
|
167
174
|
};
|
|
168
175
|
|
|
169
176
|
const XTERM_CUBE_LEVELS = [0, 95, 135, 175, 215, 255];
|
|
@@ -250,19 +257,25 @@ function prepare(
|
|
|
250
257
|
if (!text || visibleWidth(text) === 0) continue;
|
|
251
258
|
|
|
252
259
|
if (input.keepStyling) {
|
|
253
|
-
pills.push({ key: input.key, text, bg: neutral, fg: "", bold: false });
|
|
260
|
+
pills.push({ key: input.key, text, bg: neutral, fg: "", bold: false, reverse: "" });
|
|
254
261
|
continue;
|
|
255
262
|
}
|
|
256
263
|
|
|
257
264
|
const resolved = resolveColorSpec(theme, source, input.spec);
|
|
258
265
|
const bg = resolved.bg ?? resolveBackgroundSgr(theme, source, input.spec) ?? neutral;
|
|
259
|
-
// An explicit fg: in the spec wins
|
|
266
|
+
// An explicit fg: in the spec wins. A background the terminal owns has no
|
|
267
|
+
// knowable RGB, so reverse video hands both sides to the palette instead of
|
|
268
|
+
// guessing from the standard table.
|
|
269
|
+
const explicitFg = hasExplicitForeground(input.spec);
|
|
270
|
+
const reverse = explicitFg ? "" : (reverseVideoSgr(bg) ?? "");
|
|
271
|
+
const specFg = explicitFg || (resolved.bg && resolved.fg) ? resolved.fg : undefined;
|
|
260
272
|
pills.push({
|
|
261
273
|
key: input.key,
|
|
262
274
|
text,
|
|
263
275
|
bg,
|
|
264
|
-
fg:
|
|
276
|
+
fg: reverse ? "" : (specFg ?? contrastTextSgr(bg)),
|
|
265
277
|
bold: config.bold || resolved.bold,
|
|
278
|
+
reverse,
|
|
266
279
|
});
|
|
267
280
|
}
|
|
268
281
|
|
|
@@ -319,7 +332,7 @@ export function renderPillBar(
|
|
|
319
332
|
// The left cap opens the bar in the first pill's colour, so it belongs to it.
|
|
320
333
|
const startColumn = index === 0 ? capStart : column;
|
|
321
334
|
const bodyStartColumn = column;
|
|
322
|
-
emit(`${pill.bg}${pill.fg}${pill.bold ? BOLD : ""} ${pill.text} ${RESET}`);
|
|
335
|
+
emit(`${pill.reverse || pill.bg}${pill.fg}${pill.bold ? BOLD : ""} ${pill.text} ${RESET}`);
|
|
323
336
|
const bodyEndColumn = column;
|
|
324
337
|
|
|
325
338
|
const next = pills[index + 1];
|
|
@@ -329,7 +342,8 @@ export function renderPillBar(
|
|
|
329
342
|
// default to the same theme colour, which merged them into one blob.
|
|
330
343
|
// Fall back to the thin divider in the text colour, which stays visible.
|
|
331
344
|
if (separator && pill.bg === next.bg) {
|
|
332
|
-
|
|
345
|
+
const dividerFg = pill.reverse ? "" : pill.fg || contrastTextSgr(pill.bg);
|
|
346
|
+
emit(`${pill.reverse || pill.bg}${dividerFg}${THIN_SEPARATOR}${RESET}`);
|
|
333
347
|
} else if (separator) {
|
|
334
348
|
const arrowFg = toForegroundSgr(pill.bg);
|
|
335
349
|
// Without a usable foreground the arrow would be invisible; letting the
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xzzpig/pi-starline",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "A Starship-inspired statusline and Opencode-style TUI for Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/
|
|
9
|
+
"url": "git+https://github.com/xzzpig/pi-extensions.git"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/Andy8647/pi-starline#readme",
|
|
12
12
|
"bugs": {
|