// 主题预设 // 配色移植自 CCometixLine:cometix(16色精确移植) + 经典调色板设计的 nord/gruvbox/powerline 等 import type { AnsiColor, Config, SegmentConfig, SegmentId, StyleConfig } from "./types.ts"; /** 16 色快捷构造 */ const C = (c16: number): AnsiColor => ({ c16 }); /** RGB 快捷构造 */ const RGB = (r: number, g: number, b: number): AnsiColor => ({ r, g, b }); /** 段定义(紧凑形式) */ interface SegDef { id: SegmentId; icon: [string, string]; // [plain, nerdFont] fg: AnsiColor; // 文本颜色 bg?: AnsiColor; // 背景色(powerline) iconFg?: AnsiColor; // 图标颜色,默认 = fg bold?: boolean; enabled?: boolean; options?: Record; } /** Nerd Font 图标(对齐 CCometixLine 各段图标) */ // 注意:5 位码点必须用 \u{...} 转义。JS 的 \uXXXX 只认 4 位 hex, // 误写 "\uf024b" 会被解析成 U+F024 + 字面 'b',导致图标后多出一个字母 // (这正是 " b pi-cometixLine" 错误的根因)。对齐 CCometixLine theme_*.rs。 const ICONS: Record = { model: ["🤖", "\u{e26d}"], // robot(U+E26D,4位) directory: ["📁", "\u{f024b}"], // folder(U+F024B,5位) git: ["🌿", "\u{f02a2}"], // git-branch(U+F02A2,5位) session: ["🏷️", "\u{f02b}"], // tag(U+F02B,4位) contextWindow: ["⚡️", "\u{f49b}"], // battery(U+F49B;plain 带 VS16 emoji 选择符,对齐 CCometixLine) tokens: ["📊", "\u{f0a9e}"], // chart(U+F0A9E,5位;对齐 CCometixLine usage 段) cache: ["💾", "\u{f1c0}"], // database(U+F1C0,4位;对齐 pi footer 的 R/W/CH) cost: ["💰", "\u{eec1}"], // money(U+EEC1,4位) thinking: ["🧠", "\u{f09d1}"], // md-brain(U+F09D1;Nerd Fonts v3 码点,v2 旧码点 U+F5DC 已移除) }; /** 段顺序(对齐 pi footer 的元素的排布:model • thinking 在前,统计段在后) */ const ORDER: SegmentId[] = [ "model", "thinking", "directory", "git", "session", "contextWindow", "tokens", "cache", "cost", ]; function seg(id: SegmentId, fg: AnsiColor, extra: Partial> = {}): SegDef { return { id, icon: ICONS[id], fg, ...extra }; } function buildSegments(defs: SegDef[]): SegmentConfig[] { return defs.map((d) => ({ id: d.id, enabled: d.enabled ?? true, icon: { plain: d.icon[0], nerdFont: d.icon[1] }, colors: { icon: d.iconFg ?? d.fg, text: d.fg, background: d.bg }, styles: { textBold: d.bold ?? false }, options: d.options ?? {}, })); } /** 组装完整主题:补全缺失段(默认禁用) */ function makeTheme(theme: string, style: StyleConfig, defs: SegDef[]): Config { const map = new Map(defs.map((d) => [d.id, d])); const full = ORDER.map((id) => map.get(id) ?? seg(id, C(7), { enabled: false })); return { style, segments: buildSegments(full), theme }; } // ─── cometix(默认主题,NerdFont + 16 色,精确移植自 CCometixLine) ─────────── export function cometix(): Config { return makeTheme("cometix", { mode: "nerdFont", separator: " | " }, [ seg("model", C(14), { bold: true }), // cyan seg("thinking", C(5), { bold: true }), // purple:常驻显示思考强度 seg("directory", C(10), { iconFg: C(11), bold: true }), // green text, yellow icon seg("git", C(12), { bold: true, options: { show_sha: false } }), // blue seg("session", C(8), { enabled: false }), // gray,默认关闭 seg("contextWindow", C(13), { bold: true }), // magenta seg("tokens", C(14), { enabled: false }), // bright cyan:↑输入 ↓输出 seg("cache", C(6), { bold: true, enabled: false }), // cyan:R/W/CH seg("cost", C(3), { bold: true, enabled: false }), // yellow ]); } // ─── default(Plain emoji + 16 色) ───────────────────────────────────────── export function defaultTheme(): Config { return makeTheme("default", { mode: "plain", separator: " | " }, [ seg("model", C(14), { bold: true }), seg("thinking", C(5), { bold: true }), seg("directory", C(10), { iconFg: C(11), bold: true }), seg("git", C(12), { bold: true, options: { show_sha: false } }), seg("session", C(8), { enabled: false }), seg("contextWindow", C(13), { bold: true }), seg("tokens", C(14), { enabled: false }), seg("cache", C(6), { enabled: false }), seg("cost", C(3), { bold: true, enabled: false }), ]); } // ─── minimal(Plain + 低饱和 + 细分隔符) ──────────────────────────────────── export function minimal(): Config { return makeTheme("minimal", { mode: "plain", separator: " │ " }, [ seg("model", C(7)), seg("thinking", C(7)), seg("directory", C(7)), seg("git", C(7), { options: { show_sha: false } }), seg("session", C(7), { enabled: false }), seg("contextWindow", C(7)), seg("tokens", C(7), { enabled: false }), seg("cache", C(7), { enabled: false }), seg("cost", C(7), { enabled: false }), ]); } // ─── nord(NerdFont + Nord 调色板 RGB) ────────────────────────────────────── export function nord(): Config { const n = (r: number, g: number, b: number) => RGB(r, g, b); return makeTheme("nord", { mode: "nerdFont", separator: " " }, [ seg("model", n(143, 188, 187)), // nord7 cyan seg("thinking", n(180, 142, 182), { bold: true }), // nord15 seg("directory", n(129, 161, 193), { iconFg: n(235, 160, 172) }), // nord9 / nord15 seg("git", n(94, 129, 172), { options: { show_sha: false } }), // nord10 seg("session", n(216, 222, 233), { enabled: false }), // nord6 seg("contextWindow", n(136, 192, 208)), // nord8 seg("tokens", n(143, 188, 187), { enabled: false }), seg("cache", n(235, 203, 139), { enabled: false }), // nord13 seg("cost", n(208, 135, 112), { bold: true, enabled: false }), // nord12 orange ]); } // ─── gruvbox(NerdFont + Gruvbox 调色板 RGB) ───────────────────────────────── export function gruvbox(): Config { const g = (r: number, gg: number, b: number) => RGB(r, gg, b); return makeTheme("gruvbox", { mode: "nerdFont", separator: " | " }, [ seg("model", g(69, 133, 136)), // aqua seg("thinking", g(177, 98, 134), { bold: true }), // purple seg("directory", g(184, 187, 38), { iconFg: g(250, 189, 47) }), // green / yellow seg("git", g(177, 98, 134), { options: { show_sha: false } }), // purple seg("session", g(168, 153, 132), { enabled: false }), // gray seg("contextWindow", g(250, 189, 47)), // yellow seg("tokens", g(69, 133, 136), { enabled: false }), seg("cache", g(142, 192, 124), { enabled: false }), // green seg("cost", g(214, 93, 14), { bold: true, enabled: false }), // orange ]); } // ─── powerline-dark(Powerline 箭头 + 深色背景 RGB) ────────────────────────── export function powerlineDark(): Config { const white = RGB(255, 255, 255); const dark = RGB(29, 29, 29); return makeTheme( "powerline-dark", { mode: "nerdFont", separator: "\ue0b0" }, [ seg("model", white, { bg: RGB(135, 0, 175), bold: true }), // purple seg("thinking", white, { bg: RGB(92, 92, 92), bold: true }), // gray seg("directory", white, { bg: RGB(52, 101, 164), bold: true }), // blue seg("git", white, { bg: RGB(78, 154, 6), bold: true, options: { show_sha: false } }), // green seg("session", white, { bg: RGB(92, 92, 92), enabled: false, bold: true }), // gray seg("contextWindow", dark, { bg: RGB(196, 160, 0), bold: true }), // yellow bg seg("tokens", white, { bg: RGB(92, 92, 92), bold: true, enabled: false }), seg("cache", white, { bg: RGB(0, 120, 130), bold: true, enabled: false }), // teal seg("cost", white, { bg: RGB(204, 36, 29), bold: true, enabled: false }), // red ], ); } // ─── powerline-light(Powerline 箭头 + 浅色背景 RGB) ───────────────────────── export function powerlineLight(): Config { const black = RGB(40, 40, 40); return makeTheme( "powerline-light", { mode: "nerdFont", separator: "\ue0b0" }, [ seg("model", black, { bg: RGB(215, 175, 255), bold: true }), seg("thinking", black, { bg: RGB(200, 200, 255), bold: true }), seg("directory", black, { bg: RGB(135, 175, 255), bold: true }), seg("git", black, { bg: RGB(180, 215, 135), bold: true, options: { show_sha: false } }), seg("session", black, { bg: RGB(220, 220, 220), enabled: false, bold: true }), seg("contextWindow", black, { bg: RGB(255, 215, 135), bold: true }), seg("tokens", black, { bg: RGB(220, 220, 220), bold: true, enabled: false }), seg("cache", black, { bg: RGB(175, 225, 235), bold: true, enabled: false }), // light teal seg("cost", black, { bg: RGB(255, 175, 175), bold: true, enabled: false }), ], ); } // ─── powerline-rose-pine(Rose Pine 调色板) ───────────────────────────────── export function powerlineRosePine(): Config { const dark = RGB(25, 23, 36); const light = RGB(224, 222, 244); return makeTheme( "powerline-rose-pine", { mode: "nerdFont", separator: "\ue0b0" }, [ seg("model", dark, { bg: RGB(196, 167, 231), bold: true }), // iris seg("thinking", light, { bg: RGB(38, 35, 58), bold: true }), // overlay seg("directory", light, { bg: RGB(49, 116, 143), bold: true }), // pine seg("git", dark, { bg: RGB(156, 207, 216), bold: true, options: { show_sha: false } }), // foam seg("session", light, { bg: RGB(82, 79, 103), enabled: false, bold: true }), // highlight high seg("contextWindow", dark, { bg: RGB(246, 193, 119), bold: true }), // gold seg("tokens", light, { bg: RGB(235, 111, 146), bold: true, enabled: false }), // love seg("cache", dark, { bg: RGB(156, 207, 216), bold: true, enabled: false }), // foam seg("cost", dark, { bg: RGB(235, 188, 186), bold: true, enabled: false }), // rose ], ); } // ─── powerline-tokyo-night(Tokyo Night 调色板) ───────────────────────────── export function powerlineTokyoNight(): Config { const dark = RGB(26, 27, 38); return makeTheme( "powerline-tokyo-night", { mode: "nerdFont", separator: "\ue0b0" }, [ seg("model", dark, { bg: RGB(157, 124, 216), bold: true }), // purple seg("thinking", dark, { bg: RGB(187, 154, 247), bold: true }), // magenta seg("directory", dark, { bg: RGB(122, 162, 247), bold: true }), // blue seg("git", dark, { bg: RGB(158, 206, 90), bold: true, options: { show_sha: false } }), // green seg("session", dark, { bg: RGB(255, 158, 100), enabled: false, bold: true }), // orange seg("contextWindow", dark, { bg: RGB(224, 175, 104), bold: true }), // yellow seg("tokens", dark, { bg: RGB(125, 207, 255), bold: true, enabled: false }), // cyan seg("cache", dark, { bg: RGB(115, 218, 202), bold: true, enabled: false }), // teal seg("cost", dark, { bg: RGB(247, 118, 142), bold: true, enabled: false }), // red ], ); } export const THEME_NAMES = [ "cometix", "default", "minimal", "nord", "gruvbox", "powerline-dark", "powerline-light", "powerline-rose-pine", "powerline-tokyo-night", ] as const; export const THEMES: Record Config> = { cometix, default: defaultTheme, minimal, nord, gruvbox, "powerline-dark": powerlineDark, "powerline-light": powerlineLight, "powerline-rose-pine": powerlineRosePine, "powerline-tokyo-night": powerlineTokyoNight, }; /** 取主题(未知主题回退 cometix) */ export function getTheme(name: string): Config { return (THEMES[name] ?? cometix)(); } export function defaultThemeName(): string { return "cometix"; }