Darken warpGreen to nurgle-green + patch Editor for cursor/typing
- Theme: warpGreen #7a8a42 -> #5a6b2e (darker, more "nurgle", still readable for syntaxString / success / toolDiffAdded). - markdown-body-color: extend to also monkey-patch Editor.prototype.render so typed-text and the cursor (reverse-video) inherit the same inkPurple body color instead of falling through to the terminal-default fg. Re-opens the color after \x1b[0m (cursor's full reset) and \x1b[39m (nested theme.fg close) so color survives those breaks within a line. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+46
-29
@@ -1,30 +1,37 @@
|
||||
/**
|
||||
* markdown-body-color — force a theme-aware color on markdown paragraph text.
|
||||
* markdown-body-color — force a theme-aware color on text paths that pi-tui
|
||||
* emits uncoloured, so they stop inheriting the terminal profile's default
|
||||
* foreground color (green on many themes).
|
||||
*
|
||||
* Why: pi-tui's Markdown component only wraps paragraph text in a color when
|
||||
* the caller passes a `defaultTextStyle.color` fn (assistant-message.js does
|
||||
* this for thinking blocks but not for regular assistant content). Without it,
|
||||
* paragraph output is emitted uncoloured and inherits the terminal profile's
|
||||
* foreground color — which on many terminal themes is green.
|
||||
* Two patches:
|
||||
*
|
||||
* Fix: monkey-patch Markdown.prototype.render so that if no defaultTextStyle
|
||||
* is set at render time, we install one that wraps output in the active pi
|
||||
* theme's `text` token color. This leaves thinking blocks and custom-colored
|
||||
* markdowns alone (they already have their own defaultTextStyle).
|
||||
* 1. Markdown.prototype.render — pi-tui's Markdown component only wraps
|
||||
* paragraph text when the caller passes a `defaultTextStyle.color` fn.
|
||||
* pi-coding-agent does this for thinking blocks but not for regular
|
||||
* assistant content. We install a fallback defaultTextStyle before the
|
||||
* original render runs.
|
||||
*
|
||||
* 2. Editor.prototype.render — the Editor emits typed text and the cursor
|
||||
* (via \x1b[7m reverse-video) with no color wrapping at all. We wrap the
|
||||
* whole line in our body color and re-open the color after any `\x1b[0m`
|
||||
* (cursor's full-reset) or `\x1b[39m` (default-fg reset, used by nested
|
||||
* theme.fg wrappers) so the color persists across those breaks.
|
||||
*
|
||||
* Body color defaults to the dark-mechanicus "inkPurple" (#d4c5e8). Override
|
||||
* via PI_MARKDOWN_BODY_COLOR env var.
|
||||
*/
|
||||
|
||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||
import { Markdown } from "@mariozechner/pi-tui";
|
||||
import { Editor, Markdown } from "@mariozechner/pi-tui";
|
||||
|
||||
// Hardcoded fallback — matches themes/dark-mechanicus.json "inkPurple" var.
|
||||
// Override via PI_MARKDOWN_BODY_COLOR env var (hex, e.g. "#d4c5e8").
|
||||
const FALLBACK_HEX = process.env.PI_MARKDOWN_BODY_COLOR ?? "#d4c5e8";
|
||||
|
||||
function hexToRgb(hex: string): [number, number, number] {
|
||||
const m = hex.replace(/^#/, "");
|
||||
const n = parseInt(m.length === 3
|
||||
? m.split("").map((c) => c + c).join("")
|
||||
: m, 16);
|
||||
const n = parseInt(
|
||||
m.length === 3 ? m.split("").map((c) => c + c).join("") : m,
|
||||
16,
|
||||
);
|
||||
return [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];
|
||||
}
|
||||
|
||||
@@ -32,11 +39,14 @@ const [R, G, B] = hexToRgb(FALLBACK_HEX);
|
||||
const OPEN = `\x1b[38;2;${R};${G};${B}m`;
|
||||
const CLOSE = `\x1b[39m`;
|
||||
|
||||
function wrapWithBodyColor(text: string): string {
|
||||
// Re-open the color after any existing `\x1b[39m` (default-fg reset) so
|
||||
// nested styled runs inside a paragraph don't bleed back to terminal fg.
|
||||
const safe = text.split(CLOSE).join(CLOSE + OPEN);
|
||||
return OPEN + safe + CLOSE;
|
||||
// Re-open the body color after any ANSI reset inside a line. Cursor inverse
|
||||
// video uses \x1b[0m (full reset) and theme.fg helpers close with \x1b[39m.
|
||||
function reopenAfterResets(s: string): string {
|
||||
return s.replace(/\x1b\[0m/g, `\x1b[0m${OPEN}`).replace(/\x1b\[39m/g, `\x1b[39m${OPEN}`);
|
||||
}
|
||||
|
||||
function wrap(s: string): string {
|
||||
return OPEN + reopenAfterResets(s) + CLOSE;
|
||||
}
|
||||
|
||||
let patched = false;
|
||||
@@ -45,19 +55,26 @@ export default function (_pi: ExtensionAPI) {
|
||||
if (patched) return;
|
||||
patched = true;
|
||||
|
||||
const proto = Markdown.prototype as any;
|
||||
const originalRender = proto.render;
|
||||
|
||||
proto.render = function (width: number) {
|
||||
// ─ 1. Markdown component ────────────────────────────────────────────
|
||||
const mdProto = Markdown.prototype as any;
|
||||
const origMdRender = mdProto.render;
|
||||
mdProto.render = function (width: number) {
|
||||
if (!this.defaultTextStyle) {
|
||||
this.defaultTextStyle = { color: wrapWithBodyColor };
|
||||
// Force cache invalidation so the first post-patch render colors content.
|
||||
this.defaultTextStyle = { color: wrap };
|
||||
if (typeof this.invalidate === "function") this.invalidate();
|
||||
}
|
||||
return originalRender.call(this, width);
|
||||
return origMdRender.call(this, width);
|
||||
};
|
||||
|
||||
// ─ 2. Editor component ──────────────────────────────────────────────
|
||||
const edProto = Editor.prototype as any;
|
||||
const origEdRender = edProto.render;
|
||||
edProto.render = function (width: number) {
|
||||
const lines: string[] = origEdRender.call(this, width);
|
||||
return lines.map(wrap);
|
||||
};
|
||||
|
||||
console.log(
|
||||
`[markdown-body-color] Default markdown body color = ${FALLBACK_HEX}`,
|
||||
`[markdown-body-color] Body color = ${FALLBACK_HEX} (Markdown + Editor)`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"steel": "#5a5f6a",
|
||||
"gunmetal": "#3d4148",
|
||||
"oldBronze": "#8b6914",
|
||||
"warpGreen": "#7a8a42",
|
||||
"warpGreen": "#5a6b2e",
|
||||
"voidPurple": "#4a2d4d",
|
||||
"cognitorPink": "#c75a8a",
|
||||
|
||||
|
||||
Reference in New Issue
Block a user