Files
writing-agent/web/theme.js
T
chenmoranzhiandCursor dd0a9e87d8 接通主角绑定、开场封印与游玩辅助,让世界模拟器从创作连续落到本局。
直聘插入按模块回馈,并记录请求骨架便于排查。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 07:40:08 +08:00

197 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 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);
}