update at 2026-03-03 21:19:52
This commit is contained in:
17
pxterm/packages/shared/src/theme/contrast.test.ts
Normal file
17
pxterm/packages/shared/src/theme/contrast.test.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { contrastRatio, pickBestBackground, pickShellAccentColor } from "./contrast";
|
||||
|
||||
describe("theme contrast", () => {
|
||||
it("计算对比度", () => {
|
||||
expect(contrastRatio("#ffffff", "#000000")).toBeGreaterThan(7);
|
||||
});
|
||||
|
||||
it("自动选择背景", () => {
|
||||
const selected = pickBestBackground("#e6f0ff", "#5bd2ff");
|
||||
expect(selected.startsWith("#")).toBe(true);
|
||||
});
|
||||
|
||||
it("终端强调色取背景和前景之间,并略偏前景", () => {
|
||||
expect(pickShellAccentColor("#192b4d", "#e6f0ff")).toBe("#9ca9bf");
|
||||
});
|
||||
});
|
||||
105
pxterm/packages/shared/src/theme/contrast.ts
Normal file
105
pxterm/packages/shared/src/theme/contrast.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 主题引擎:提供 WCAG 对比度计算与背景自动优化。
|
||||
*/
|
||||
|
||||
export interface RgbColor {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 终端强调色插值系数:
|
||||
* - 0.5 代表正中间;
|
||||
* - >0.5 代表向前景色偏移;
|
||||
* - 当前取 0.64,满足“中间色且略偏前景”的视觉要求。
|
||||
*/
|
||||
export const SHELL_ACCENT_BLEND_T = 0.64;
|
||||
|
||||
export function normalizeHex(input: string, fallback: string): string {
|
||||
return /^#[0-9a-fA-F]{6}$/.test(input) ? input.toLowerCase() : fallback;
|
||||
}
|
||||
|
||||
export function hexToRgb(hex: string): RgbColor {
|
||||
const value = normalizeHex(hex, "#000000");
|
||||
return {
|
||||
r: Number.parseInt(value.slice(1, 3), 16),
|
||||
g: Number.parseInt(value.slice(3, 5), 16),
|
||||
b: Number.parseInt(value.slice(5, 7), 16)
|
||||
};
|
||||
}
|
||||
|
||||
function srgbToLinear(value: number): number {
|
||||
const normalized = value / 255;
|
||||
if (normalized <= 0.03928) {
|
||||
return normalized / 12.92;
|
||||
}
|
||||
return ((normalized + 0.055) / 1.055) ** 2.4;
|
||||
}
|
||||
|
||||
export function luminance(hex: string): number {
|
||||
const rgb = hexToRgb(hex);
|
||||
return 0.2126 * srgbToLinear(rgb.r) + 0.7152 * srgbToLinear(rgb.g) + 0.0722 * srgbToLinear(rgb.b);
|
||||
}
|
||||
|
||||
export function contrastRatio(a: string, b: string): number {
|
||||
const la = luminance(a);
|
||||
const lb = luminance(b);
|
||||
const lighter = Math.max(la, lb);
|
||||
const darker = Math.min(la, lb);
|
||||
return (lighter + 0.05) / (darker + 0.05);
|
||||
}
|
||||
|
||||
export function pickBestBackground(textColor: string, accentColor: string): string {
|
||||
const candidates = [
|
||||
"#0a1325",
|
||||
"#132747",
|
||||
"#102b34",
|
||||
"#2e223b",
|
||||
normalizeHex(accentColor, "#5bd2ff")
|
||||
];
|
||||
|
||||
let best = candidates[0] ?? "#0a1325";
|
||||
let bestScore = 0;
|
||||
for (const candidate of candidates) {
|
||||
const score = contrastRatio(normalizeHex(textColor, "#e6f0ff"), candidate);
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色线性插值:
|
||||
* - t=0 表示返回背景色;
|
||||
* - t=1 表示返回前景色;
|
||||
* - 用于按钮色与终端强调色等“在 bg/text 之间取色”的场景。
|
||||
*/
|
||||
function mixColor(bgColor: string, textColor: string, t: number, fallbackBg: string, fallbackText: string): string {
|
||||
const bg = hexToRgb(normalizeHex(bgColor, fallbackBg));
|
||||
const text = hexToRgb(normalizeHex(textColor, fallbackText));
|
||||
const factor = Number.isFinite(t) ? Math.min(1, Math.max(0, t)) : 0.5;
|
||||
const r = Math.round(bg.r + (text.r - bg.r) * factor);
|
||||
const g = Math.round(bg.g + (text.g - bg.g) * factor);
|
||||
const b = Math.round(bg.b + (text.b - bg.b) * factor);
|
||||
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按钮色自动推导:在背景色和文本色之间取色,偏向文本色一侧(t=0.72)。
|
||||
* 确保按钮与背景有足够对比度,同时色调协调。
|
||||
*/
|
||||
export function pickBtnColor(bgColor: string, textColor: string): string {
|
||||
return mixColor(bgColor, textColor, 0.72, "#192b4d", "#e6f0ff");
|
||||
}
|
||||
|
||||
/**
|
||||
* 终端强调色自动推导:
|
||||
* - 在终端背景色与前景色之间取“中间偏前景”的颜色;
|
||||
* - 目标是避免强调色贴近背景导致识别度不足,同时避免过亮抢占正文层级。
|
||||
*/
|
||||
export function pickShellAccentColor(bgColor: string, textColor: string): string {
|
||||
return mixColor(bgColor, textColor, SHELL_ACCENT_BLEND_T, "#192b4d", "#e6f0ff");
|
||||
}
|
||||
11
pxterm/packages/shared/src/theme/presets.test.ts
Normal file
11
pxterm/packages/shared/src/theme/presets.test.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { pickShellAccentColor } from "./contrast";
|
||||
import { getShellVariant } from "./presets";
|
||||
|
||||
describe("theme presets", () => {
|
||||
it("shell 变体的 cursor 按背景和前景自动推导", () => {
|
||||
const variant = getShellVariant("tide", "dark");
|
||||
expect(variant.cursor).toBe(pickShellAccentColor(variant.bg, variant.text));
|
||||
expect(variant.cursor).toBe("#9ca9bf");
|
||||
});
|
||||
});
|
||||
257
pxterm/packages/shared/src/theme/presets.ts
Normal file
257
pxterm/packages/shared/src/theme/presets.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { pickShellAccentColor } from "./contrast";
|
||||
|
||||
/**
|
||||
* 主题色板预设(按配置计划 §主题实现方案)。
|
||||
*
|
||||
* 每套主题包含:
|
||||
* - palette: 原始色板(按 Figma 顺序)
|
||||
* - dark: dark 变体(bg / text / accent / btn)
|
||||
* - light: light 变体(bg / text / accent / btn)
|
||||
*/
|
||||
|
||||
export type ThemePreset =
|
||||
| "tide"
|
||||
| "暮砂"
|
||||
| "霓潮"
|
||||
| "苔暮"
|
||||
| "焰岩"
|
||||
| "岩陶"
|
||||
| "靛雾"
|
||||
| "绛霓"
|
||||
| "玫蓝"
|
||||
| "珊湾"
|
||||
| "苔荧"
|
||||
| "铜暮"
|
||||
| "炽潮"
|
||||
| "藕夜"
|
||||
| "沙海"
|
||||
| "珀岚"
|
||||
| "炫虹"
|
||||
| "鎏霓"
|
||||
| "珊汐"
|
||||
| "黛苔"
|
||||
| "霜绯";
|
||||
|
||||
export interface ThemeVariant {
|
||||
/** 页面/卡片背景色 */
|
||||
bg: string;
|
||||
/** 正文字体色 */
|
||||
text: string;
|
||||
/** 强调色(链接、输入框聚焦线、开关等) */
|
||||
accent: string;
|
||||
/** 主按钮底色 */
|
||||
btn: string;
|
||||
}
|
||||
|
||||
export interface ThemeDefinition {
|
||||
name: ThemePreset;
|
||||
/** 色板原始顺序 */
|
||||
palette: string[];
|
||||
dark: ThemeVariant;
|
||||
light: ThemeVariant;
|
||||
/** shell 专用 dark 变体(终端背景/前景/光标) */
|
||||
shellDark: { bg: string; text: string; cursor: string };
|
||||
/** shell 专用 light 变体 */
|
||||
shellLight: { bg: string; text: string; cursor: string };
|
||||
}
|
||||
|
||||
export const THEME_PRESETS: Record<ThemePreset, ThemeDefinition> = {
|
||||
tide: {
|
||||
name: "tide",
|
||||
palette: ["#192b4d", "#5bd2ff", "#e6f0ff", "#3D86FF"],
|
||||
dark: { bg: "#192b4d", text: "#e6f0ff", accent: "#5bd2ff", btn: "#3D86FF" },
|
||||
light: { bg: "#e6f0ff", text: "#192b4d", accent: "#3D86FF", btn: "#3D86FF" },
|
||||
shellDark: { bg: "#192b4d", text: "#e6f0ff", cursor: "#5bd2ff" },
|
||||
shellLight: { bg: "#e6f0ff", text: "#192b4d", cursor: "#3D86FF" }
|
||||
},
|
||||
暮砂: {
|
||||
name: "暮砂",
|
||||
palette: ["#F4F1DE", "#EAB69F", "#E07A5F", "#8F5D5D", "#3D405B", "#5F797B", "#81B29A", "#9EB998", "#BABF95", "#F2CC8F"],
|
||||
dark: { bg: "#3D405B", text: "#F4F1DE", accent: "#81B29A", btn: "#E07A5F" },
|
||||
light: { bg: "#F4F1DE", text: "#3D405B", accent: "#E07A5F", btn: "#8F5D5D" },
|
||||
shellDark: { bg: "#3D405B", text: "#F4F1DE", cursor: "#81B29A" },
|
||||
shellLight: { bg: "#F4F1DE", text: "#3D405B", cursor: "#E07A5F" }
|
||||
},
|
||||
霓潮: {
|
||||
name: "霓潮",
|
||||
palette: ["#EF476F", "#F78C6B", "#FFD166", "#83D483", "#06D6A0", "#001914", "#118AB2", "#0F7799", "#0C637F", "#073B4C"],
|
||||
dark: { bg: "#073B4C", text: "#FFD166", accent: "#06D6A0", btn: "#118AB2" },
|
||||
light: { bg: "#FFD166", text: "#073B4C", accent: "#EF476F", btn: "#0F7799" },
|
||||
shellDark: { bg: "#073B4C", text: "#FFD166", cursor: "#06D6A0" },
|
||||
shellLight: { bg: "#FFD166", text: "#073B4C", cursor: "#EF476F" }
|
||||
},
|
||||
苔暮: {
|
||||
name: "苔暮",
|
||||
palette: ["#A8B868", "#798575", "#4A5282", "#393F7C", "#313679", "#282C75", "#3C3C9D", "#4E4CC3", "#645FD4", "#7A71E4"],
|
||||
dark: { bg: "#282C75", text: "#A8B868", accent: "#7A71E4", btn: "#84916c" },
|
||||
light: { bg: "#A8B868", text: "#282C75", accent: "#4E4CC3", btn: "#393F7C" },
|
||||
shellDark: { bg: "#282C75", text: "#A8B868", cursor: "#7A71E4" },
|
||||
shellLight: { bg: "#A8B868", text: "#282C75", cursor: "#4E4CC3" }
|
||||
},
|
||||
焰岩: {
|
||||
name: "焰岩",
|
||||
palette: ["#5F0F40", "#7D092F", "#9A031E", "#CB4721", "#FB8B24", "#EF781C", "#E36414", "#AE5E26", "#795838", "#0F4C5C"],
|
||||
dark: { bg: "#0F4C5C", text: "#FB8B24", accent: "#E36414", btn: "#CB4721" },
|
||||
light: { bg: "#FB8B24", text: "#0F4C5C", accent: "#CB4721", btn: "#9A031E" },
|
||||
shellDark: { bg: "#0F4C5C", text: "#FB8B24", cursor: "#E36414" },
|
||||
shellLight: { bg: "#FB8B24", text: "#0F4C5C", cursor: "#CB4721" }
|
||||
},
|
||||
岩陶: {
|
||||
name: "岩陶",
|
||||
palette: ["#283D3B", "#21585A", "#197278", "#83A8A6", "#EDDDD4", "#D99185", "#E9B5AF", "#9E3A2E", "#772E25"],
|
||||
dark: { bg: "#283D3B", text: "#EDDDD4", accent: "#E9B5AF", btn: "#D99185" },
|
||||
light: { bg: "#EDDDD4", text: "#283D3B", accent: "#D99185", btn: "#9E3A2E" },
|
||||
shellDark: { bg: "#283D3B", text: "#EDDDD4", cursor: "#E9B5AF" },
|
||||
shellLight: { bg: "#EDDDD4", text: "#283D3B", cursor: "#D99185" }
|
||||
},
|
||||
靛雾: {
|
||||
name: "靛雾",
|
||||
palette: ["#292281", "#2D2586", "#31278B", "#382D93", "#3F329B", "#4437A1", "#4A3BA6", "#9D96BA", "#C7C4C4", "#F1F0CD"],
|
||||
dark: { bg: "#292281", text: "#F1F0CD", accent: "#9D96BA", btn: "#b9b6b8" },
|
||||
light: { bg: "#F1F0CD", text: "#292281", accent: "#4A3BA6", btn: "#382D93" },
|
||||
shellDark: { bg: "#292281", text: "#F1F0CD", cursor: "#9D96BA" },
|
||||
shellLight: { bg: "#F1F0CD", text: "#292281", cursor: "#4A3BA6" }
|
||||
},
|
||||
绛霓: {
|
||||
name: "绛霓",
|
||||
palette: ["#F72585", "#B5179E", "#7209B7", "#560BAD", "#480CA8", "#3A0CA3", "#3F37C9", "#4361EE", "#4895EF", "#4CC9F0"],
|
||||
dark: { bg: "#3A0CA3", text: "#4CC9F0", accent: "#4895EF", btn: "#7209B7" },
|
||||
light: { bg: "#4CC9F0", text: "#3A0CA3", accent: "#F72585", btn: "#7209B7" },
|
||||
shellDark: { bg: "#3A0CA3", text: "#4CC9F0", cursor: "#4895EF" },
|
||||
shellLight: { bg: "#4CC9F0", text: "#3A0CA3", cursor: "#F72585" }
|
||||
},
|
||||
玫蓝: {
|
||||
name: "玫蓝",
|
||||
palette: ["#D06A79", "#984B8D", "#5E2BA1", "#482398", "#3D1F94", "#311B90", "#3D39B6", "#4857DC", "#567BE3", "#629FEB"],
|
||||
dark: { bg: "#3D1F94", text: "#629FEB", accent: "#D06A79", btn: "#567BE3" },
|
||||
light: { bg: "#629FEB", text: "#3D1F94", accent: "#D06A79", btn: "#4857DC" },
|
||||
shellDark: { bg: "#3D1F94", text: "#629FEB", cursor: "#567BE3" },
|
||||
shellLight: { bg: "#629FEB", text: "#3D1F94", cursor: "#D06A79" }
|
||||
},
|
||||
珊湾: {
|
||||
name: "珊湾",
|
||||
palette: ["#EC5B57", "#AD635D", "#6C6B64", "#526660", "#45645D", "#37615B", "#3E8983", "#44B0AB", "#4BC8C4", "#52DFDD"],
|
||||
dark: { bg: "#37615B", text: "#52DFDD", accent: "#EC5B57", btn: "#44B0AB" },
|
||||
light: { bg: "#52DFDD", text: "#37615B", accent: "#EC5B57", btn: "#3E8983" },
|
||||
shellDark: { bg: "#37615B", text: "#52DFDD", cursor: "#44B0AB" },
|
||||
shellLight: { bg: "#52DFDD", text: "#37615B", cursor: "#EC5B57" }
|
||||
},
|
||||
苔荧: {
|
||||
name: "苔荧",
|
||||
palette: ["#414731", "#515A23", "#616C15", "#99A32C", "#D1D942", "#C2CB37", "#B3BC2C", "#909636", "#6C6F41", "#252157"],
|
||||
dark: { bg: "#252157", text: "#D1D942", accent: "#99A32C", btn: "#C2CB37" },
|
||||
light: { bg: "#D1D942", text: "#252157", accent: "#909636", btn: "#99A32C" },
|
||||
shellDark: { bg: "#252157", text: "#D1D942", cursor: "#C2CB37" },
|
||||
shellLight: { bg: "#D1D942", text: "#252157", cursor: "#909636" }
|
||||
},
|
||||
铜暮: {
|
||||
name: "铜暮",
|
||||
palette: ["#502939", "#672F2A", "#7E351A", "#B27225", "#E6B030", "#D99F27", "#CB8E1E", "#9F782D", "#72623C", "#1A375A"],
|
||||
dark: { bg: "#1A375A", text: "#E6B030", accent: "#B27225", btn: "#D99F27" },
|
||||
light: { bg: "#E6B030", text: "#1A375A", accent: "#B27225", btn: "#9F782D" },
|
||||
shellDark: { bg: "#1A375A", text: "#E6B030", cursor: "#D99F27" },
|
||||
shellLight: { bg: "#E6B030", text: "#1A375A", cursor: "#B27225" }
|
||||
},
|
||||
炽潮: {
|
||||
name: "炽潮",
|
||||
palette: ["#5B2A28", "#771E1C", "#921211", "#C43133", "#F55054", "#E94347", "#DC363A", "#AA3E40", "#774547", "#125554"],
|
||||
dark: { bg: "#125554", text: "#F55054", accent: "#C43133", btn: "#E94347" },
|
||||
light: { bg: "#F55054", text: "#125554", accent: "#AA3E40", btn: "#DC363A" },
|
||||
shellDark: { bg: "#125554", text: "#F55054", cursor: "#E94347" },
|
||||
shellLight: { bg: "#F55054", text: "#125554", cursor: "#AA3E40" }
|
||||
},
|
||||
藕夜: {
|
||||
name: "藕夜",
|
||||
palette: ["#322F4F", "#433E71", "#554C93", "#98958C", "#DBDD85", "#D8DD7D", "#D5DB74", "#CED56E", "#C8CF67", "#BAC35A"],
|
||||
dark: { bg: "#322F4F", text: "#DBDD85", accent: "#554C93", btn: "#C8CF67" },
|
||||
light: { bg: "#DBDD85", text: "#322F4F", accent: "#D5DB74", btn: "#433E71" },
|
||||
shellDark: { bg: "#322F4F", text: "#DBDD85", cursor: "#C8CF67" },
|
||||
shellLight: { bg: "#DBDD85", text: "#322F4F", cursor: "#554C93" }
|
||||
},
|
||||
沙海: {
|
||||
name: "沙海",
|
||||
palette: ["#2B3B51", "#355971", "#3F7690", "#91A483", "#E2D075", "#E4C66F", "#E4BD69", "#E0B464", "#DBAA5F", "#D19654"],
|
||||
dark: { bg: "#2B3B51", text: "#E2D075", accent: "#3F7690", btn: "#DBAA5F" },
|
||||
light: { bg: "#E2D075", text: "#2B3B51", accent: "#355971", btn: "#D19654" },
|
||||
shellDark: { bg: "#2B3B51", text: "#E2D075", cursor: "#DBAA5F" },
|
||||
shellLight: { bg: "#E2D075", text: "#2B3B51", cursor: "#3F7690" }
|
||||
},
|
||||
珀岚: {
|
||||
name: "珀岚",
|
||||
palette: ["#274D4C", "#2B7171", "#2F9595", "#8B9395", "#E79094", "#EC878A", "#EF7D7F", "#EC7578", "#E86D6F", "#E15D5F"],
|
||||
dark: { bg: "#274D4C", text: "#E79094", accent: "#2F9595", btn: "#EC7578" },
|
||||
light: { bg: "#E79094", text: "#274D4C", accent: "#2B7171", btn: "#E15D5F" },
|
||||
shellDark: { bg: "#274D4C", text: "#E79094", cursor: "#EC7578" },
|
||||
shellLight: { bg: "#E79094", text: "#274D4C", cursor: "#2F9595" }
|
||||
},
|
||||
炫虹: {
|
||||
name: "炫虹",
|
||||
palette: ["#FFBE0B", "#FD8A09", "#FB5607", "#FD2B3B", "#FF006E", "#C11CAD", "#A22ACD", "#8338EC", "#5F5FF6", "#3A86FF"],
|
||||
dark: { bg: "#8338EC", text: "#FFBE0B", accent: "#FF006E", btn: "#3A86FF" },
|
||||
light: { bg: "#FFBE0B", text: "#8338EC", accent: "#FD2B3B", btn: "#5F5FF6" },
|
||||
shellDark: { bg: "#8338EC", text: "#FFBE0B", cursor: "#3A86FF" },
|
||||
shellLight: { bg: "#FFBE0B", text: "#8338EC", cursor: "#FF006E" }
|
||||
},
|
||||
鎏霓: {
|
||||
name: "鎏霓",
|
||||
palette: ["#F3D321", "#E7B019", "#DC8C10", "#D67039", "#D05460", "#A2529A", "#8C51B8", "#7550D5", "#5F5FE3", "#476CEF"],
|
||||
dark: { bg: "#7550D5", text: "#F3D321", accent: "#A2529A", btn: "#476CEF" },
|
||||
light: { bg: "#F3D321", text: "#7550D5", accent: "#D67039", btn: "#5F5FE3" },
|
||||
shellDark: { bg: "#7550D5", text: "#F3D321", cursor: "#476CEF" },
|
||||
shellLight: { bg: "#F3D321", text: "#7550D5", cursor: "#A2529A" }
|
||||
},
|
||||
珊汐: {
|
||||
name: "珊汐",
|
||||
palette: ["#FB5860", "#F74046", "#F2292C", "#F23433", "#F23E39", "#B86E68", "#9C867F", "#7F9E96", "#5FB4AE", "#3DCAC5"],
|
||||
dark: { bg: "#7F9E96", text: "#FB5860", accent: "#3DCAC5", btn: "#F23E39" },
|
||||
light: { bg: "#FB5860", text: "#7F9E96", accent: "#F2292C", btn: "#5FB4AE" },
|
||||
shellDark: { bg: "#7F9E96", text: "#FB5860", cursor: "#3DCAC5" },
|
||||
shellLight: { bg: "#FB5860", text: "#7F9E96", cursor: "#F2292C" }
|
||||
},
|
||||
黛苔: {
|
||||
name: "黛苔",
|
||||
palette: ["#2F2E3B", "#353159", "#3A3376", "#908EA6", "#E7E8D6", "#BEC388", "#949D3A", "#788031", "#5B6127"],
|
||||
dark: { bg: "#2F2E3B", text: "#E7E8D6", accent: "#788031", btn: "#949D3A" },
|
||||
light: { bg: "#E7E8D6", text: "#2F2E3B", accent: "#788031", btn: "#5B6127" },
|
||||
shellDark: { bg: "#2F2E3B", text: "#E7E8D6", cursor: "#949D3A" },
|
||||
shellLight: { bg: "#E7E8D6", text: "#2F2E3B", cursor: "#788031" }
|
||||
},
|
||||
霜绯: {
|
||||
name: "霜绯",
|
||||
palette: ["#293B3B", "#235959", "#1D7575", "#84A6A6", "#ECD7D8", "#D58A8A", "#BD3C3D", "#993333", "#732829"],
|
||||
dark: { bg: "#293B3B", text: "#ECD7D8", accent: "#1D7575", btn: "#BD3C3D" },
|
||||
light: { bg: "#ECD7D8", text: "#293B3B", accent: "#993333", btn: "#BD3C3D" },
|
||||
shellDark: { bg: "#293B3B", text: "#ECD7D8", cursor: "#BD3C3D" },
|
||||
shellLight: { bg: "#ECD7D8", text: "#293B3B", cursor: "#993333" }
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 按主题名和模式获取 UI 变体颜色。
|
||||
*/
|
||||
export function getThemeVariant(preset: ThemePreset, mode: "dark" | "light"): ThemeVariant {
|
||||
const def = THEME_PRESETS[preset] ?? THEME_PRESETS["tide"];
|
||||
return mode === "dark" ? def.dark : def.light;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按主题名和模式获取 Shell 变体颜色。
|
||||
*/
|
||||
export function getShellVariant(
|
||||
preset: ThemePreset,
|
||||
mode: "dark" | "light"
|
||||
): { bg: string; text: string; cursor: string } {
|
||||
const def = THEME_PRESETS[preset] ?? THEME_PRESETS["tide"];
|
||||
const base = mode === "dark" ? def.shellDark : def.shellLight;
|
||||
return {
|
||||
bg: base.bg,
|
||||
text: base.text,
|
||||
// 终端强调色统一按“背景与前景之间、略偏前景”的规则实时推导。
|
||||
cursor: pickShellAccentColor(base.bg, base.text)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有可用主题预设名称列表。
|
||||
*/
|
||||
export const THEME_PRESET_NAMES: ThemePreset[] = Object.keys(THEME_PRESETS) as ThemePreset[];
|
||||
Reference in New Issue
Block a user