Files
2026-07-22 15:27:26 +08:00

400 lines
26 KiB
TypeScript

export const themeTokens = {
appName: "Mecha Game Console",
contentMaxWidth: "1180px",
accent: "#48e6ff",
accentSecondary: "#ff77c8",
narrowBreakpoint: 760
} as const;
export type ThemePaletteId = "mecha-black" | "magical-girl";
export type ThemeBackgroundId = "mecha-grid" | "magic-stage";
export const themePaletteChangeEvent = "platform-web:theme-palette-change";
export interface ThemePaletteChangeDetail {
paletteId: ThemePaletteId;
}
export interface ThemePalette {
id: ThemePaletteId;
label: string;
summary: string;
swatches: string[];
variables: Record<string, string>;
}
export interface ThemeBackgroundPreset {
id: ThemeBackgroundId;
label: string;
summary: string;
preview: string;
variables: Record<string, string>;
}
export interface WorkspaceThemeState {
paletteId: ThemePaletteId;
backgroundPresetId: ThemeBackgroundId;
backgroundImage: string | null;
}
const backgroundStorageKey = "platform-web.theme.background";
const backgroundPresetStorageKey = "platform-web.theme.backgroundPreset";
const paletteStorageKey = "platform-web.theme.palette";
const mechaFrameAccessoryTop =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 132 48'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 31h34l9-12h28l7 8h18' stroke='%2348e6ff' stroke-opacity='.86' stroke-width='2.4'/%3E%3Cpath d='M75 17h16l10 10M25 36h22M100 32h15' stroke='%23ffb84d' stroke-opacity='.68' stroke-width='1.7'/%3E%3Ccircle cx='58' cy='24' r='8' stroke='%239af7ff' stroke-opacity='.58' stroke-width='1.6'/%3E%3Cpath d='M58 15v18M49 24h18' stroke='%239af7ff' stroke-opacity='.34' stroke-width='1.2'/%3E%3Cpath d='M106 10l4 4 6-1-4 4 2 6-6-3-5 4 1-7-5-4 6 1z' stroke='%23ffb84d' stroke-opacity='.58' stroke-width='1.4'/%3E%3C/g%3E%3C/svg%3E\")";
const mechaFrameAccessoryBottom =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 132 56'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 38h30l12-19 15 25h25' stroke='%2348e6ff' stroke-opacity='.78' stroke-width='2.4'/%3E%3Cpath d='M34 38l9-13 8 13M74 37h21' stroke='%23ffb84d' stroke-opacity='.62' stroke-width='1.7'/%3E%3Ccircle cx='100' cy='26' r='15' stroke='%239af7ff' stroke-opacity='.54' stroke-width='1.6'/%3E%3Ccircle cx='100' cy='26' r='7' stroke='%23ffb84d' stroke-opacity='.62' stroke-width='1.4'/%3E%3Cpath d='M84 26h32M100 10v32M89 15l22 22M111 15L89 37' stroke='%239af7ff' stroke-opacity='.28' stroke-width='1.1'/%3E%3Cpath d='M18 20l11-7 16 4-13 9z' stroke='%239af7ff' stroke-opacity='.58' stroke-width='1.5'/%3E%3C/g%3E%3C/svg%3E\")";
const magicalFrameAccessoryHeart =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 48'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M36 38S23 29 23 20c0-7 9-9 13-3 4-6 13-4 13 3 0 9-13 18-13 18z' fill='%23ff77c8' fill-opacity='.12' stroke='%23fff6fb' stroke-width='2'/%3E%3Cpath d='M22 20c-6-4-11-2-15 2 5 0 8 2 11 6-5-1-8 1-10 4 7-1 12 1 17 5M50 20c6-4 11-2 15 2-5 0-8 2-11 6 5-1 8 1 10 4-7-1-12 1-17 5' stroke='%23ffd66d' stroke-opacity='.92' stroke-width='1.7'/%3E%3Cpath d='M36 8l2 4 5 1-4 3 1 5-4-3-4 3 1-5-4-3 5-1z' fill='%23ffd66d' fill-opacity='.22' stroke='%23ffd66d' stroke-width='1.4'/%3E%3C/g%3E%3C/svg%3E\")";
const magicalFrameAccessoryWand =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 48'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 40L45 13' stroke='%23ff77c8' stroke-width='3'/%3E%3Cpath d='M49 5l3 6 7 1-5 5 1 7-6-4-7 4 2-7-6-5 7-1z' fill='%23ffd66d' fill-opacity='.2' stroke='%23ffd66d' stroke-width='1.9'/%3E%3Cpath d='M12 14l1.5 3 3.5.5-2.5 2.5.5 3.5-3-2-3 2 .5-3.5L6 17.5l3.5-.5zM59 29l1.5 3 3.5.5-2.5 2.5.5 3.5-3-2-3 2 .5-3.5-2.5-2.5 3.5-.5z' stroke='%23fff6fb' stroke-opacity='.9' stroke-width='1.3'/%3E%3Cpath d='M15 36l7 7' stroke='%23ffd66d' stroke-opacity='.86' stroke-width='1.6'/%3E%3C/g%3E%3C/svg%3E\")";
const magicalFrameAccessoryMoon =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 48'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M42 8c-9 2-15 10-13 19 2 10 12 15 21 11-6-1-11-6-12-12-2-7 0-13 4-18z' fill='%23ff77c8' fill-opacity='.1' stroke='%23fff6fb' stroke-width='2'/%3E%3Cpath d='M19 19l2.5 5 5.5 1-4 4 1 6-5-3-5 3 1-6-4-4 5.5-1z' fill='%23ffd66d' fill-opacity='.2' stroke='%23ffd66d' stroke-width='1.7'/%3E%3Cpath d='M48 10c5 1 9 4 12 8M50 38c5-2 9-5 12-10' stroke='%23ff77c8' stroke-opacity='.84' stroke-width='1.6'/%3E%3Ccircle cx='59' cy='24' r='2' fill='%23fff6fb'/%3E%3C/g%3E%3C/svg%3E\")";
const magicalFrameAccessoryRibbon =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 48'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M35 22c-7-8-17-9-20-4-3 6 5 12 20 8M37 22c7-8 17-9 20-4 3 6-5 12-20 8' fill='%23ff77c8' fill-opacity='.12' stroke='%23fff6fb' stroke-width='2'/%3E%3Cpath d='M35 25L23 42l13-5 5 5 3-17M37 25l12 17-13-5-5 5-3-17' stroke='%23ff77c8' stroke-width='1.8'/%3E%3Ccircle cx='36' cy='24' r='5' fill='%23ffd66d' fill-opacity='.18' stroke='%23ffd66d' stroke-width='1.6'/%3E%3Cpath d='M10 10l1.5 3 3.5.5-2.5 2.5.5 3.5-3-2-3 2 .5-3.5L4 13.5l3.5-.5zM60 8l1.5 3 3.5.5-2.5 2.5.5 3.5-3-2-3 2 .5-3.5-2.5-2.5 3.5-.5z' stroke='%23ffd66d' stroke-width='1.2'/%3E%3C/g%3E%3C/svg%3E\")";
const magicalFrameAccessoryCrystal =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 48'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M36 5l16 14-6 22H26l-6-22z' fill='%23ff77c8' fill-opacity='.09' stroke='%23fff6fb' stroke-width='2'/%3E%3Cpath d='M20 19h32M36 5l-8 14 8 22 8-22zM26 41l10-22 10 22' stroke='%23ffd66d' stroke-opacity='.88' stroke-width='1.35'/%3E%3Cpath d='M10 16l2 4 4 .5-3 3 .8 4.5-3.8-2.4L6 28l1-4.5-3-3 4-.5zM59 9l1.5 3 3.5.5-2.5 2.5.5 3.5-3-2-3 2 .5-3.5-2.5-2.5 3.5-.5z' stroke='%23ff77c8' stroke-width='1.3'/%3E%3C/g%3E%3C/svg%3E\")";
const magicalFrameAccessoryCircle =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 48'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='36' cy='24' r='18' stroke='%23fff6fb' stroke-width='1.7'/%3E%3Ccircle cx='36' cy='24' r='11' stroke='%23ffd66d' stroke-width='1.4'/%3E%3Cpath d='M36 6v36M18 24h36M23 11l26 26M49 11L23 37' stroke='%23ff77c8' stroke-opacity='.68' stroke-width='1.1'/%3E%3Cpath d='M36 14l3 6 7 1-5 5 1 7-6-4-6 4 1-7-5-5 7-1z' fill='%23ffd66d' fill-opacity='.12' stroke='%23ffd66d' stroke-width='1.2'/%3E%3C/g%3E%3C/svg%3E\")";
const mechaMaterialVariables = {
"--surface": "rgba(5, 12, 18, 0.58)",
"--surface-solid": "rgba(6, 14, 21, 0.88)",
"--surface-raised": "rgba(10, 24, 32, 0.64)",
"--glass-wash": "linear-gradient(145deg, rgba(33, 55, 64, 0.46), rgba(5, 12, 18, 0.42) 46%, rgba(72, 230, 255, 0.08))",
"--shine-sweep": "linear-gradient(112deg, transparent 0 38%, rgba(132, 243, 255, 0.24) 46%, transparent 56%)",
"--glass-tint": "rgba(72, 230, 255, 0.08)",
"--crystal-rim": "rgba(137, 239, 255, 0.64)",
"--moonbeam": "rgba(60, 176, 255, 0.34)",
"--diamond-line": "rgba(119, 237, 255, 0.34)",
"--glass-shadow": "rgba(0, 0, 0, 0.56)",
"--crystal-spark": "rgba(172, 250, 255, 0.92)",
"--sparkle": "rgba(196, 252, 255, 0.96)",
"--sparkle-gold": "rgba(255, 184, 77, 0.82)",
"--corner-sparkle":
"linear-gradient(135deg, rgba(72, 230, 255, 0.72) 0 2px, transparent 2px 100%), linear-gradient(315deg, rgba(255, 184, 77, 0.72) 0 2px, transparent 2px 100%), radial-gradient(circle at 86% 18%, rgba(132, 243, 255, 0.88) 0 1.4px, transparent 2.4px)",
"--jelly-highlight": "linear-gradient(145deg, rgba(132, 243, 255, 0.16), transparent 32%), radial-gradient(circle at 82% 16%, rgba(255, 184, 77, 0.16), transparent 24%)",
"--sugar-dust":
"linear-gradient(90deg, rgba(72, 230, 255, 0.08) 1px, transparent 1px), linear-gradient(180deg, rgba(72, 230, 255, 0.07) 1px, transparent 1px), radial-gradient(circle at 88% 18%, rgba(255, 184, 77, 0.62) 0 1.2px, transparent 2px)",
"--crystal-edge-glow": "0 0 0 1px rgba(72, 230, 255, 0.28), 0 0 22px rgba(72, 230, 255, 0.2), 0 0 44px rgba(0, 0, 0, 0.48)",
"--jelly-inset": "inset 0 1px 0 rgba(180, 248, 255, 0.22), inset 0 -1px 0 rgba(72, 230, 255, 0.16), inset 0 0 24px rgba(72, 230, 255, 0.08)",
"--frosted-edge": "linear-gradient(180deg, rgba(132, 243, 255, 0.18), rgba(7, 12, 18, 0.74) 48%, rgba(255, 184, 77, 0.1))",
"--frosted-surface": "linear-gradient(145deg, rgba(14, 28, 38, 0.62), rgba(5, 12, 18, 0.46) 48%, rgba(72, 230, 255, 0.07))",
"--frame-corner": "linear-gradient(135deg, rgba(72, 230, 255, 0.9) 0 2px, transparent 2px), linear-gradient(315deg, rgba(255, 184, 77, 0.72) 0 2px, transparent 2px)",
"--frame-accent": "linear-gradient(90deg, rgba(72, 230, 255, 0), rgba(72, 230, 255, 0.78), rgba(255, 184, 77, 0.58), rgba(72, 230, 255, 0))",
"--orbital-construct-image": "url(\"/theme/stellar-megastructure.svg\")",
"--orbital-construct-filter":
"drop-shadow(0 0 18px rgba(72, 230, 255, 0.28)) drop-shadow(0 0 34px rgba(255, 184, 77, 0.1))",
"--magic-lightfall":
"linear-gradient(180deg, transparent 0 4%, rgba(154, 247, 255, 0.34) 18%, rgba(72, 230, 255, 0.1) 42%, transparent 74%)",
"--magic-side-rays": "linear-gradient(90deg, rgba(72, 230, 255, 0.3), transparent 72%)",
"--command-ring-sheen":
"radial-gradient(circle at 50% 50%, rgba(154, 247, 255, 0.32), rgba(72, 230, 255, 0.12) 42%, transparent 68%)",
"--frame-accessory-top": mechaFrameAccessoryTop,
"--frame-accessory-bottom": mechaFrameAccessoryBottom,
"--frame-accessory-opacity": "0.22",
"--frame-accessory-size": "44px 16px, 48px 20px",
"--menu-item-bg": "linear-gradient(90deg, rgba(72, 230, 255, 0.14) 0 1px, transparent 1px 100%), linear-gradient(135deg, rgba(17, 31, 43, 0.94), rgba(4, 8, 13, 0.82) 58%, rgba(255, 184, 77, 0.08))",
"--menu-item-active-bg": "linear-gradient(90deg, rgba(72, 230, 255, 0.44) 0 3px, transparent 3px 100%), linear-gradient(135deg, rgba(20, 51, 64, 0.98), rgba(6, 11, 18, 0.9) 52%, rgba(255, 184, 77, 0.18))",
"--menu-glyph-bg": "linear-gradient(135deg, rgba(72, 230, 255, 0.28), rgba(255, 184, 77, 0.14)), repeating-linear-gradient(90deg, rgba(137, 239, 255, 0.18) 0 1px, transparent 1px 6px), rgba(5, 9, 15, 0.9)",
"--menu-title-shadow": "0 0 14px rgba(72, 230, 255, 0.5), 0 0 2px rgba(255, 184, 77, 0.8)",
"--menu-active-outline": "linear-gradient(90deg, rgba(72, 230, 255, 0.95), rgba(255, 184, 77, 0.72), rgba(72, 230, 255, 0.95))",
"--panel-material": "var(--sugar-dust), var(--glass-wash), linear-gradient(135deg, rgba(5, 12, 18, 0.3), rgba(10, 24, 32, 0.22))",
"--panel-shadow": "var(--jelly-inset), inset 0 0 0 1px rgba(119, 237, 255, 0.24), inset 9px 0 0 rgba(72, 230, 255, 0.06), 0 18px 42px rgba(0, 0, 0, 0.42), 0 0 28px rgba(72, 230, 255, 0.11)"
} as const;
const magicalMaterialVariables = {
"--surface": "rgba(88, 18, 52, 0.38)",
"--surface-solid": "rgba(78, 14, 46, 0.7)",
"--surface-raised": "rgba(118, 26, 68, 0.42)",
"--glass-wash": "linear-gradient(180deg, rgba(255, 226, 242, 0.13), rgba(120, 24, 68, 0.22) 56%, rgba(72, 12, 42, 0.18))",
"--shine-sweep": "radial-gradient(circle at 88% 12%, rgba(255, 255, 255, 0.58) 0 2px, transparent 3px)",
"--glass-tint": "rgba(255, 119, 200, 0.1)",
"--crystal-rim": "rgba(255, 236, 250, 0.92)",
"--moonbeam": "rgba(255, 185, 226, 0.76)",
"--diamond-line": "rgba(255, 217, 239, 0.72)",
"--glass-shadow": "rgba(72, 0, 34, 0.36)",
"--crystal-spark": "rgba(255, 246, 253, 0.98)",
"--sparkle": "rgba(255, 255, 255, 0.98)",
"--sparkle-gold": "rgba(255, 221, 117, 0.9)",
"--corner-sparkle":
"radial-gradient(circle at 86% 14%, rgba(255, 255, 255, 0.98) 0 2.4px, transparent 3.2px), radial-gradient(circle at 93% 23%, rgba(255, 221, 117, 0.92) 0 1.8px, transparent 2.8px)",
"--jelly-highlight": "radial-gradient(circle at 18% 14%, rgba(255, 255, 255, 0.72), transparent 30%), linear-gradient(145deg, rgba(255, 255, 255, 0.34), transparent 34%)",
"--sugar-dust":
"radial-gradient(circle at 14% 20%, rgba(255, 255, 255, 0.95) 0 1.4px, transparent 2.2px), radial-gradient(circle at 86% 18%, rgba(255, 221, 117, 0.9) 0 1.6px, transparent 2.6px), radial-gradient(circle at 78% 84%, rgba(255, 119, 200, 0.76) 0 1.8px, transparent 2.8px)",
"--crystal-edge-glow": "0 0 0 1px rgba(255, 217, 239, 0.78), 0 0 18px rgba(255, 119, 200, 0.46), 0 0 34px rgba(255, 221, 117, 0.18)",
"--jelly-inset": "inset 0 1px 0 rgba(255, 255, 255, 0.52), inset 0 -1px 0 rgba(255, 119, 200, 0.28), inset 0 0 20px rgba(255, 255, 255, 0.18)",
"--frosted-edge": "linear-gradient(180deg, rgba(255, 255, 255, 0.58), rgba(255, 119, 200, 0.2) 48%, rgba(255, 221, 117, 0.18))",
"--frosted-surface": "linear-gradient(180deg, rgba(255, 226, 242, 0.12), rgba(118, 24, 66, 0.24) 52%, rgba(72, 12, 42, 0.18))",
"--frame-corner": "radial-gradient(circle at 0 0, rgba(255, 221, 117, 0.9) 0 4px, transparent 5px), radial-gradient(circle at 100% 100%, rgba(255, 119, 200, 0.9) 0 4px, transparent 5px)",
"--frame-accent": "linear-gradient(90deg, rgba(255, 221, 117, 0), rgba(255, 221, 117, 0.86), rgba(255, 119, 200, 0.78), rgba(255, 221, 117, 0))",
"--orbital-construct-image": "url(\"/theme/magical-circle.svg\")",
"--orbital-construct-filter":
"drop-shadow(0 0 18px rgba(255, 119, 200, 0.24)) drop-shadow(0 0 28px rgba(255, 221, 117, 0.16))",
"--magic-lightfall":
"linear-gradient(180deg, transparent 0 6%, rgba(255, 246, 253, 0.32) 18%, rgba(255, 119, 200, 0.12) 48%, transparent 76%)",
"--magic-side-rays":
"linear-gradient(90deg, rgba(255, 246, 253, 0.44), rgba(255, 221, 117, 0.18) 34%, rgba(255, 119, 200, 0.08) 62%, transparent)",
"--command-ring-sheen":
"radial-gradient(circle at 50% 50%, rgba(255, 246, 253, 0.5), rgba(255, 221, 117, 0.18) 42%, transparent 70%)",
"--frame-accessory-top": magicalFrameAccessoryWand,
"--frame-accessory-bottom": magicalFrameAccessoryCircle,
"--frame-accessory-heart": magicalFrameAccessoryHeart,
"--frame-accessory-wand": magicalFrameAccessoryWand,
"--frame-accessory-moon": magicalFrameAccessoryMoon,
"--frame-accessory-ribbon": magicalFrameAccessoryRibbon,
"--frame-accessory-crystal": magicalFrameAccessoryCrystal,
"--frame-accessory-circle": magicalFrameAccessoryCircle,
"--frame-accessory-opacity": "0.78",
"--frame-accessory-size": "68px 44px",
"--menu-item-bg": "radial-gradient(circle at 92% 12%, rgba(255, 255, 255, 0.52) 0 3px, transparent 4px), linear-gradient(180deg, rgba(255, 185, 226, 0.22), rgba(135, 10, 65, 0.42) 68%, rgba(94, 0, 44, 0.48))",
"--menu-item-active-bg": "radial-gradient(circle at 96% 18%, rgba(255, 255, 255, 0.72) 0 3px, transparent 4px), linear-gradient(180deg, rgba(255, 203, 232, 0.34), rgba(177, 22, 86, 0.58) 54%, rgba(105, 0, 48, 0.62))",
"--menu-glyph-bg": "radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.78), transparent 34%), radial-gradient(circle at 72% 80%, rgba(255, 221, 117, 0.42), transparent 30%), linear-gradient(135deg, rgba(255, 119, 200, 0.82), rgba(255, 221, 117, 0.44))",
"--menu-title-shadow": "0 2px 0 rgba(96, 24, 24, 0.86), 1px 0 0 rgba(96, 24, 24, 0.86), -1px 0 0 rgba(96, 24, 24, 0.86), 0 0 12px rgba(255, 221, 117, 0.48)",
"--menu-active-outline": "linear-gradient(90deg, rgba(255, 221, 117, 0.96), rgba(255, 246, 253, 0.92), rgba(255, 119, 200, 0.9), rgba(255, 221, 117, 0.96))",
"--panel-material": "var(--sugar-dust), var(--glass-wash), linear-gradient(180deg, rgba(106, 22, 60, 0.11), rgba(72, 12, 42, 0.09))",
"--panel-shadow": "var(--jelly-inset), inset 0 0 0 1px rgba(255, 217, 239, 0.5), 0 18px 42px rgba(72, 0, 34, 0.28), 0 0 30px rgba(255, 119, 200, 0.2), 0 0 16px rgba(255, 221, 117, 0.1)"
} as const;
export const themePalettes: ThemePalette[] = [
{
id: "mecha-black",
label: "黑色机甲",
summary: "黑钢面板、青蓝扫描线、琥珀能量灯",
swatches: ["#05090f", "#111c28", "#48e6ff", "#ffb84d", "#e7f7ff"],
variables: {
"--ink": "#e7f7ff",
"--ink-soft": "#b9d2dc",
"--ink-faint": "#84a1ad",
"--font-ui": "Inter, \"PingFang SC\", \"Microsoft YaHei\", ui-sans-serif, system-ui, sans-serif",
"--font-display": "\"Bahnschrift\", \"DIN Alternate\", \"PingFang SC\", \"Microsoft YaHei\", ui-sans-serif, system-ui, sans-serif",
"--font-mono": "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
"--title-weight": "760",
"--title-tracking": "0.01em",
"--heading-weight": "780",
"--heading-tracking": "0.005em",
...mechaMaterialVariables,
"--line": "rgba(116, 236, 255, 0.32)",
"--line-strong": "rgba(132, 243, 255, 0.7)",
"--accent": "#48e6ff",
"--accent-deep": "#9af7ff",
"--accent-soft": "rgba(72, 230, 255, 0.16)",
"--teal": "#4fffd7",
"--teal-soft": "rgba(79, 255, 215, 0.14)",
"--pink": "#ff4d6d",
"--pink-soft": "rgba(255, 77, 109, 0.13)",
"--gold": "#ffb84d",
"--gold-soft": "rgba(255, 184, 77, 0.18)",
"--danger": "#ff5a6e",
"--danger-soft": "rgba(255, 90, 110, 0.14)",
"--success": "#4fffd7",
"--success-soft": "rgba(79, 255, 215, 0.18)",
"--background-base": "#05070b",
"--background-glow-1": "rgba(72, 230, 255, 0.16)",
"--background-glow-2": "rgba(255, 184, 77, 0.11)",
"--background-glow-3": "rgba(255, 77, 109, 0.1)",
"--rim-light": "rgba(180, 248, 255, 0.64)",
"--candy-glow": "rgba(72, 230, 255, 0.32)",
"--ultimate-effect-alpha": "0.86",
"--custom-background-overlay": "transparent",
"--code-surface": "#05090f",
"--code-ink": "#dffcff",
"--code-muted": "#87afba"
}
},
{
id: "magical-girl",
label: "魔法少女",
summary: "粉色舞台、金色星框、显眼魔法阵",
swatches: ["#8b0a40", "#ff77c8", "#ffd66d", "#fff6fb", "#8cf0ff"],
variables: {
"--ink": "#fff8fd",
"--ink-soft": "#ffe8f5",
"--ink-faint": "#ffd0e9",
"--font-ui": "Inter, \"PingFang SC\", \"Microsoft YaHei\", ui-rounded, ui-sans-serif, system-ui, sans-serif",
"--font-display": "\"Trebuchet MS\", \"PingFang SC\", \"Microsoft YaHei\", ui-rounded, ui-sans-serif, system-ui, sans-serif",
"--font-mono": "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
"--title-weight": "720",
"--title-tracking": "0",
"--heading-weight": "740",
"--heading-tracking": "0",
...magicalMaterialVariables,
"--line": "rgba(255, 201, 230, 0.42)",
"--line-strong": "rgba(255, 225, 139, 0.76)",
"--accent": "#ff77c8",
"--accent-deep": "#fff6fb",
"--accent-soft": "rgba(255, 119, 200, 0.18)",
"--teal": "#8cf0ff",
"--teal-soft": "rgba(140, 240, 255, 0.16)",
"--pink": "#ff8fd2",
"--pink-soft": "rgba(255, 143, 210, 0.22)",
"--gold": "#ffd66d",
"--gold-soft": "rgba(255, 214, 109, 0.24)",
"--danger": "#ff8a9d",
"--danger-soft": "rgba(255, 138, 157, 0.18)",
"--success": "#8cf0ff",
"--success-soft": "rgba(140, 240, 255, 0.2)",
"--background-base": "#7a0b39",
"--background-glow-1": "rgba(255, 119, 200, 0.42)",
"--background-glow-2": "rgba(255, 214, 109, 0.28)",
"--background-glow-3": "rgba(140, 240, 255, 0.18)",
"--rim-light": "rgba(255, 255, 255, 0.92)",
"--candy-glow": "rgba(255, 119, 200, 0.48)",
"--ultimate-effect-alpha": "0.96",
"--custom-background-overlay": "transparent",
"--code-surface": "#4b092a",
"--code-ink": "#fff4fb",
"--code-muted": "#ffc6e4"
}
}
];
const themePaletteVariableNames = Array.from(new Set(themePalettes.flatMap((palette) => Object.keys(palette.variables))));
export const defaultThemePaletteId: ThemePaletteId = "mecha-black";
export const defaultThemeBackgroundId: ThemeBackgroundId = "mecha-grid";
export const themeBackgroundPresets: ThemeBackgroundPreset[] = [
{
id: "mecha-grid",
label: "机甲格纳库",
summary: "黑钢格栅、雷达圆环、青蓝扫描光",
preview:
"radial-gradient(circle at 76% 18%, rgba(72, 230, 255, 0.86), transparent 18%), repeating-linear-gradient(90deg, rgba(72, 230, 255, 0.34) 0 1px, transparent 1px 18px), linear-gradient(135deg, #05090f, #111c28)",
variables: {
"--workspace-background-pattern-image":
"radial-gradient(circle at 76% 18%, transparent 0 68px, rgba(72, 230, 255, 0.3) 69px 71px, transparent 72px), radial-gradient(circle at 76% 18%, transparent 0 34px, rgba(255, 184, 77, 0.24) 35px 36px, transparent 37px), conic-gradient(from 0deg at 76% 18%, transparent 0 8%, rgba(72, 230, 255, 0.24) 8% 9%, transparent 9% 28%, rgba(72, 230, 255, 0.18) 28% 29%, transparent 29% 100%), linear-gradient(90deg, rgba(72, 230, 255, 0.08) 1px, transparent 1px), linear-gradient(180deg, rgba(72, 230, 255, 0.07) 1px, transparent 1px), linear-gradient(135deg, transparent 0 48%, rgba(255, 184, 77, 0.1) 49% 50%, transparent 51% 100%), radial-gradient(circle at 20% 78%, rgba(255, 77, 109, 0.12), transparent 28%)",
"--workspace-background-pattern-size": "auto, auto, auto, 42px 42px, 42px 42px, 180px 180px, auto",
"--workspace-background-pattern-position": "center, center, center, 0 0, 0 0, 0 0, center",
"--workspace-background-pattern-repeat": "no-repeat, no-repeat, no-repeat, repeat, repeat, repeat, no-repeat",
"--workspace-background-pattern-opacity": "0.92",
"--workspace-background-pattern-filter": "saturate(1.18) contrast(1.08)"
}
},
{
id: "magic-stage",
label: "粉月魔法阵",
summary: "粉色舞台、星星边框、底部大型魔法阵",
preview:
"radial-gradient(circle at 86% 86%, transparent 0 28%, rgba(255, 255, 255, 0.72) 29% 30%, transparent 31%), radial-gradient(circle at 25% 22%, #ff77c8, transparent 34%), linear-gradient(180deg, #f08bb4, #8b0a40)",
variables: {
"--workspace-background-pattern-image":
"radial-gradient(circle, rgba(255, 255, 255, 0.9) 0 1.4px, transparent 2.2px), radial-gradient(circle, rgba(255, 221, 117, 0.86) 0 1.8px, transparent 2.8px), radial-gradient(circle at 88% 84%, transparent 0 112px, rgba(255, 255, 255, 0.28) 113px 115px, transparent 116px), radial-gradient(circle at 88% 84%, transparent 0 72px, rgba(255, 119, 200, 0.28) 73px 75px, transparent 76px), conic-gradient(from 30deg at 88% 84%, transparent 0 8%, rgba(255, 221, 117, 0.18) 8% 9%, transparent 9% 24%, rgba(255, 255, 255, 0.18) 24% 25%, transparent 25% 100%), linear-gradient(180deg, rgba(255, 203, 226, 0.26), rgba(122, 11, 57, 0.12)), radial-gradient(circle at 18% 20%, rgba(255, 143, 210, 0.28), transparent 28%)",
"--workspace-background-pattern-size": "76px 76px, 118px 118px, auto, auto, auto, auto, auto",
"--workspace-background-pattern-position": "12px 18px, 42px 58px, center, center, center, center, center",
"--workspace-background-pattern-repeat": "repeat, repeat, no-repeat, no-repeat, no-repeat, no-repeat, no-repeat",
"--workspace-background-pattern-opacity": "0.96",
"--workspace-background-pattern-filter": "saturate(1.24)"
}
}
];
export function getThemePalette(id: string | null | undefined): ThemePalette {
return themePalettes.find((palette) => palette.id === id) ?? themePalettes[0];
}
export function getThemeBackgroundPreset(id: string | null | undefined): ThemeBackgroundPreset {
return themeBackgroundPresets.find((preset) => preset.id === id) ?? themeBackgroundPresets[0];
}
export function loadThemeState(): WorkspaceThemeState {
if (typeof window === "undefined") {
return { paletteId: defaultThemePaletteId, backgroundPresetId: defaultThemeBackgroundId, backgroundImage: null };
}
try {
return {
paletteId: getThemePalette(window.localStorage.getItem(paletteStorageKey)).id,
backgroundPresetId: getThemeBackgroundPreset(window.localStorage.getItem(backgroundPresetStorageKey)).id,
backgroundImage: window.localStorage.getItem(backgroundStorageKey)
};
} catch {
return { paletteId: defaultThemePaletteId, backgroundPresetId: defaultThemeBackgroundId, backgroundImage: null };
}
}
export function persistThemePalette(paletteId: ThemePaletteId): void {
if (typeof window === "undefined") {
return;
}
try {
window.localStorage.setItem(paletteStorageKey, paletteId);
} catch {
// storage may be unavailable; the palette stays session-only
}
}
export function persistThemeBackgroundPreset(backgroundPresetId: ThemeBackgroundId): void {
if (typeof window === "undefined") {
return;
}
try {
window.localStorage.setItem(backgroundPresetStorageKey, backgroundPresetId);
} catch {
// storage may be unavailable; the preset stays session-only
}
}
export function persistBackgroundImage(dataUrl: string | null): void {
if (typeof window === "undefined") {
return;
}
try {
if (dataUrl) {
window.localStorage.setItem(backgroundStorageKey, dataUrl);
} else {
window.localStorage.removeItem(backgroundStorageKey);
}
} catch {
// storage may be unavailable (private mode); the background stays session-only
}
}
export function applyThemePalette(paletteId: ThemePaletteId): void {
if (typeof document === "undefined") {
return;
}
const palette = getThemePalette(paletteId);
for (const name of themePaletteVariableNames) {
document.documentElement.style.removeProperty(name);
}
for (const [name, value] of Object.entries(palette.variables)) {
document.documentElement.style.setProperty(name, value);
}
document.documentElement.dataset.themePalette = palette.id;
if (typeof window !== "undefined") {
window.dispatchEvent(new CustomEvent<ThemePaletteChangeDetail>(themePaletteChangeEvent, { detail: { paletteId: palette.id } }));
}
}
export function applyThemeBackgroundPreset(backgroundPresetId: ThemeBackgroundId): void {
if (typeof document === "undefined") {
return;
}
const preset = getThemeBackgroundPreset(backgroundPresetId);
for (const [name, value] of Object.entries(preset.variables)) {
document.documentElement.style.setProperty(name, value);
}
document.documentElement.dataset.themeBackground = preset.id;
}
export function applyBackgroundImage(dataUrl: string | null): void {
if (typeof document === "undefined") {
return;
}
if (dataUrl) {
document.documentElement.style.setProperty("--workspace-background-image", `url(${JSON.stringify(dataUrl)})`);
document.documentElement.dataset.customBackground = "true";
} else {
document.documentElement.style.removeProperty("--workspace-background-image");
delete document.documentElement.dataset.customBackground;
}
}