直聘插入按模块回馈,并记录请求骨架便于排查。 Co-authored-by: Cursor <cursoragent@cursor.com>
197 lines
5.4 KiB
JavaScript
197 lines
5.4 KiB
JavaScript
/** Color themes + UI font scale — aligned with llm_workflow_engine ThemeToggle. */
|
||
|
||
export const COLOR_THEMES = [
|
||
{
|
||
id: "paper",
|
||
name: "仿纸暖黄",
|
||
description: "经典护眼,像 Kindle",
|
||
icon: "📖",
|
||
},
|
||
{
|
||
id: "modern",
|
||
name: "现代清爽",
|
||
description: "干净明快,适合网文",
|
||
icon: "✨",
|
||
},
|
||
{
|
||
id: "dark",
|
||
name: "夜间暗色",
|
||
description: "黑底灰字,夜间必备",
|
||
icon: "🌙",
|
||
},
|
||
{
|
||
id: "green",
|
||
name: "豆沙绿",
|
||
description: "自然疗愈,绿色经典",
|
||
icon: "🍃",
|
||
},
|
||
];
|
||
|
||
/** 全局字号:百分比(100 = 默认),+/- 步进调节 */
|
||
export const UI_FONT_SCALE_MIN = 80;
|
||
export const UI_FONT_SCALE_MAX = 150;
|
||
export const UI_FONT_SCALE_STEP = 5;
|
||
export const UI_FONT_SCALE_DEFAULT = 100;
|
||
|
||
const LEGACY_FONT_SCALE_IDS = {
|
||
sm: 88,
|
||
md: 100,
|
||
lg: 112,
|
||
xl: 125,
|
||
};
|
||
|
||
/**
|
||
* @deprecated 选壳已从主题菜单移除;壳气质跟呈现壳本身。保留常量以免旧引用报错。
|
||
*/
|
||
export const PRESENT_CHROMES = [
|
||
{ id: "default", name: "默认", description: "跟随壳本身" },
|
||
{ id: "messenger", name: "讯息", description: "气泡对话感" },
|
||
{ id: "book", name: "书页", description: "阅读排版感" },
|
||
{ id: "terminal", name: "终端", description: "等宽终端感" },
|
||
];
|
||
|
||
const STORAGE_KEY = "wa-color-theme";
|
||
const FONT_SCALE_STORAGE_KEY = "wa-ui-font-scale";
|
||
const CHROME_STORAGE_KEY = "wa-present-chrome";
|
||
const THEME_IDS = new Set(COLOR_THEMES.map((t) => t.id));
|
||
const CHROME_IDS = new Set(PRESENT_CHROMES.map((t) => t.id));
|
||
const DEFAULT_THEME = "modern";
|
||
const DEFAULT_CHROME = "default";
|
||
|
||
export function normalizeColorTheme(id) {
|
||
const v = String(id || "").trim();
|
||
return THEME_IDS.has(v) ? v : DEFAULT_THEME;
|
||
}
|
||
|
||
export function getStoredColorTheme() {
|
||
try {
|
||
return normalizeColorTheme(localStorage.getItem(STORAGE_KEY));
|
||
} catch {
|
||
return DEFAULT_THEME;
|
||
}
|
||
}
|
||
|
||
export function themeById(id) {
|
||
const tid = normalizeColorTheme(id);
|
||
return COLOR_THEMES.find((t) => t.id === tid) || COLOR_THEMES[1];
|
||
}
|
||
|
||
export function applyColorTheme(id) {
|
||
const theme = normalizeColorTheme(id);
|
||
document.documentElement.setAttribute("data-color-theme", theme);
|
||
document.documentElement.setAttribute("data-theme", theme);
|
||
try {
|
||
localStorage.setItem(STORAGE_KEY, theme);
|
||
} catch {
|
||
/* ignore quota / private mode */
|
||
}
|
||
return theme;
|
||
}
|
||
|
||
export function initColorTheme() {
|
||
return applyColorTheme(getStoredColorTheme());
|
||
}
|
||
|
||
function clampFontPercent(n) {
|
||
const stepped = Math.round(n / UI_FONT_SCALE_STEP) * UI_FONT_SCALE_STEP;
|
||
return Math.min(
|
||
UI_FONT_SCALE_MAX,
|
||
Math.max(UI_FONT_SCALE_MIN, stepped),
|
||
);
|
||
}
|
||
|
||
/** 解析为百分比整数;兼容旧档 sm/md/lg/xl 与 0.x~2 倍缩放 */
|
||
export function normalizeUiFontScalePercent(raw) {
|
||
if (raw == null) return UI_FONT_SCALE_DEFAULT;
|
||
const key = String(raw).trim();
|
||
if (Object.prototype.hasOwnProperty.call(LEGACY_FONT_SCALE_IDS, key)) {
|
||
return LEGACY_FONT_SCALE_IDS[key];
|
||
}
|
||
let n = Number(key);
|
||
if (!Number.isFinite(n)) return UI_FONT_SCALE_DEFAULT;
|
||
if (n > 0 && n <= 2.5) n = Math.round(n * 100);
|
||
return clampFontPercent(n);
|
||
}
|
||
|
||
export function getStoredUiFontScale() {
|
||
try {
|
||
return normalizeUiFontScalePercent(localStorage.getItem(FONT_SCALE_STORAGE_KEY));
|
||
} catch {
|
||
return UI_FONT_SCALE_DEFAULT;
|
||
}
|
||
}
|
||
|
||
/** @deprecated 用 getStoredUiFontScale / applyUiFontScale(percent) */
|
||
export function fontScaleById(id) {
|
||
const percent = normalizeUiFontScalePercent(id);
|
||
return { id: String(percent), scale: percent / 100, name: `${percent}`, description: "" };
|
||
}
|
||
|
||
export function applyUiFontScale(raw) {
|
||
const percent = normalizeUiFontScalePercent(raw);
|
||
const root = document.documentElement;
|
||
root.setAttribute("data-ui-font-scale", String(percent));
|
||
root.style.setProperty("--ui-font-scale", String(percent / 100));
|
||
try {
|
||
localStorage.setItem(FONT_SCALE_STORAGE_KEY, String(percent));
|
||
} catch {
|
||
/* ignore */
|
||
}
|
||
return percent;
|
||
}
|
||
|
||
export function bumpUiFontScale(deltaSteps) {
|
||
const cur = getStoredUiFontScale();
|
||
const steps = Number(deltaSteps);
|
||
const delta = Number.isFinite(steps) ? steps : 0;
|
||
return applyUiFontScale(cur + delta * UI_FONT_SCALE_STEP);
|
||
}
|
||
|
||
export function initUiFontScale() {
|
||
return applyUiFontScale(getStoredUiFontScale());
|
||
}
|
||
|
||
/** @deprecated */
|
||
export function normalizePresentChrome(id) {
|
||
const v = String(id || "").trim();
|
||
return CHROME_IDS.has(v) ? v : DEFAULT_CHROME;
|
||
}
|
||
|
||
/** @deprecated */
|
||
export function getStoredPresentChrome() {
|
||
try {
|
||
return normalizePresentChrome(localStorage.getItem(CHROME_STORAGE_KEY));
|
||
} catch {
|
||
return DEFAULT_CHROME;
|
||
}
|
||
}
|
||
|
||
/** @deprecated */
|
||
export function chromeById(id) {
|
||
const cid = normalizePresentChrome(id);
|
||
return PRESENT_CHROMES.find((t) => t.id === cid) || PRESENT_CHROMES[0];
|
||
}
|
||
|
||
/** @deprecated 菜单已移除;调用仍把属性钉为 default,避免旧存档残留 */
|
||
export function applyPresentChrome(_id) {
|
||
const chrome = DEFAULT_CHROME;
|
||
document.documentElement.setAttribute("data-present-chrome", chrome);
|
||
try {
|
||
localStorage.setItem(CHROME_STORAGE_KEY, chrome);
|
||
} catch {
|
||
/* ignore */
|
||
}
|
||
document.querySelectorAll(".present-shell").forEach((el) => {
|
||
for (const cls of [...el.classList]) {
|
||
if (cls.startsWith("present-tone--")) el.classList.remove(cls);
|
||
}
|
||
el.classList.add(`present-tone--${chrome}`);
|
||
});
|
||
return chrome;
|
||
}
|
||
|
||
/** @deprecated */
|
||
export function initPresentChrome() {
|
||
return applyPresentChrome(DEFAULT_CHROME);
|
||
}
|