Files
oh-my-pi/packages/coding-agent/src/modes/components/segment-track.ts
T
can1357 7e2971b1f6 ux(coding-agent/modes): introduced shared segment track renderer for model and role views
- Added a shared `renderSegmentTrack` helper to render colored segment tracks with a filled active chip style.
- Updated hook selector and role-cycle status-line rendering to use the shared track renderer and aligned role-cycle output.
- Added contrast text-color logic to `Theme` so active chip labels stay legible across fill colors.
2026-05-31 00:53:19 +02:00

53 lines
2.0 KiB
TypeScript

/**
* Shared renderer for a horizontal row of colored "segments" styled after the
* status line: each segment shows in its own accent, the active one is filled
* as a powerline chip (its accent as the background, a luminance-matched label,
* flanked by triangle caps) and the rest are plain colored labels joined by a
* thin separator.
*
* Used by the plan-mode model-tier slider ({@link HookSelectorComponent}) and
* the ctrl+p role-cycle status so both surfaces read identically.
*/
import { type ThemeColor, theme } from "../theme/theme";
export interface TrackSegment {
label: string;
/** Theme color for the segment; defaults to `accent`. */
color?: ThemeColor;
}
const FG_RESET = "\x1b[39m";
const BG_RESET = "\x1b[49m";
/**
* Render `segments` as a colored chip track with `activeIndex` filled. Returns
* a single line of styled text with no surrounding caption or arrows — callers
* frame it as they need.
*/
export function renderSegmentTrack(segments: TrackSegment[], activeIndex: number): string {
// Powerline triangles point *into* the chip so the colored caps merge with
// the filled body: left cap points left, right cap points right.
const capLeft = theme.sep.powerlineRight;
const capRight = theme.sep.powerlineLeft;
const thinSep = theme.fg("statusLineSep", theme.sep.powerlineThin);
let track = "";
segments.forEach((segment, i) => {
if (i > 0) {
// A thin separator reads cleanly only between two plain labels; the chip
// caps already delimit the active segment, so pad around it instead.
track += i === activeIndex || i - 1 === activeIndex ? " " : ` ${thinSep} `;
}
const color = segment.color ?? "accent";
const fg = theme.getFgAnsi(color);
if (i !== activeIndex) {
track += `${fg}${segment.label}${FG_RESET}`;
return;
}
const bg = fg.replace("\x1b[38;", "\x1b[48;");
const label = `${bg}${theme.getContrastFgAnsi(color)}\x1b[1m ${segment.label} \x1b[22m${BG_RESET}`;
track += `${fg}${capLeft}${label}${fg}${capRight}${FG_RESET}`;
});
return track;
}