ziwei-mcp
Version:
紫微斗数 MCP (Ziwei Doushu Model Context Protocol) - 基于 MCP 协议的紫微斗数命理分析工具
931 lines (811 loc) • 23 kB
JavaScript
/**
* SVG主题管理器
* 负责主题的定义、切换、自定义和动态应用
*/
class SVGThemeManager {
constructor() {
this.themes = new Map();
this.customThemes = new Map();
this.currentTheme = null;
this.themeVariables = new Map();
// 配置
this.config = {
enableTransitions: true,
transitionDuration: 300,
enablePreview: true,
autoSave: true,
};
// 初始化内置主题
this.initializeBuiltinThemes();
}
/**
* 初始化内置主题
*/
initializeBuiltinThemes() {
// 经典主题
this.registerTheme("classic", {
name: "经典主题",
description: "传统紫微斗数配色方案",
category: "builtin",
colors: {
// 基础颜色
primary: "#8B4513", // 棕色
secondary: "#DAA520", // 金色
accent: "#DC143C", // 深红色
background: "#FFF8DC", // 米色
backgroundSecondary: "#F5F5DC", // 米白色
surface: "#FFFFFF", // 白色
// 文本颜色
textPrimary: "#2F4F4F", // 深灰绿
textSecondary: "#696969", // 暗灰色
textAccent: "#8B0000", // 暗红色
// 边框颜色
borderPrimary: "#8B4513", // 棕色
borderSecondary: "#D2B48C", // 浅棕色
borderLight: "#F0E68C", // 浅黄色
// 宫位颜色
palaceBackground: "#FFFAF0", // 花白色
palaceBorder: "#DEB887", // 浅棕色
palaceHover: "#FFF8DC", // 米色
// 星曜颜色
starMajor: "#8B0000", // 暗红色 - 主星
starMinor: "#4682B4", // 钢蓝色 - 辅星
starEvil: "#8B008B", // 暗洋红 - 煞星
starLucky: "#228B22", // 森林绿 - 吉星
// 四化颜色
sihuaLu: "#32CD32", // 酸橙绿 - 化禄
sihuaQuan: "#FF6347", // 番茄红 - 化权
sihuaKe: "#4169E1", // 皇家蓝 - 化科
sihuaJi: "#8B008B", // 暗洋红 - 化忌
// 状态颜色
success: "#228B22", // 森林绿
warning: "#FF8C00", // 暗橙色
error: "#DC143C", // 深红色
info: "#4682B4", // 钢蓝色
},
typography: {
fontFamily: '"Microsoft YaHei", "SimHei", sans-serif',
fontSize: {
title: "18px",
subtitle: "14px",
body: "12px",
caption: "10px",
palaceName: "14px",
starName: "11px",
ganZhi: "10px",
},
fontWeight: {
normal: "400",
medium: "500",
bold: "700",
},
lineHeight: {
tight: "1.2",
normal: "1.4",
loose: "1.6",
},
},
spacing: {
xs: "2px",
sm: "4px",
md: "8px",
lg: "12px",
xl: "16px",
xxl: "24px",
},
borders: {
width: {
thin: "1px",
normal: "2px",
thick: "3px",
},
radius: {
none: "0px",
sm: "2px",
md: "4px",
lg: "8px",
full: "50%",
},
style: "solid",
},
shadows: {
sm: "0 1px 2px rgba(0,0,0,0.1)",
md: "0 2px 4px rgba(0,0,0,0.1)",
lg: "0 4px 8px rgba(0,0,0,0.15)",
},
animations: {
duration: {
fast: "150ms",
normal: "300ms",
slow: "500ms",
},
easing: {
linear: "linear",
easeIn: "ease-in",
easeOut: "ease-out",
easeInOut: "ease-in-out",
},
},
});
// 暗色主题
this.registerTheme("dark", {
name: "暗色主题",
description: "现代暗色配色方案",
category: "builtin",
colors: {
primary: "#BB86FC", // 紫色
secondary: "#03DAC6", // 青色
accent: "#CF6679", // 粉红色
background: "#121212", // 深黑色
backgroundSecondary: "#1E1E1E", // 黑色
surface: "#2D2D2D", // 深灰色
textPrimary: "#FFFFFF", // 白色
textSecondary: "#B3B3B3", // 浅灰色
textAccent: "#BB86FC", // 紫色
borderPrimary: "#444444", // 中灰色
borderSecondary: "#666666", // 浅灰色
borderLight: "#888888", // 更浅灰色
palaceBackground: "#2D2D2D", // 深灰色
palaceBorder: "#444444", // 中灰色
palaceHover: "#3D3D3D", // 稍浅灰色
starMajor: "#FF6B6B", // 红色 - 主星
starMinor: "#4ECDC4", // 青色 - 辅星
starEvil: "#A8E6CF", // 浅绿色 - 煞星
starLucky: "#FFD93D", // 黄色 - 吉星
sihuaLu: "#4CAF50", // 绿色 - 化禄
sihuaQuan: "#FF5722", // 深橙色 - 化权
sihuaKe: "#2196F3", // 蓝色 - 化科
sihuaJi: "#9C27B0", // 紫色 - 化忌
success: "#4CAF50", // 绿色
warning: "#FF9800", // 橙色
error: "#F44336", // 红色
info: "#2196F3", // 蓝色
},
typography: {
fontFamily: '"Microsoft YaHei", "SimHei", sans-serif',
fontSize: {
title: "18px",
subtitle: "14px",
body: "12px",
caption: "10px",
palaceName: "14px",
starName: "11px",
ganZhi: "10px",
},
fontWeight: {
normal: "400",
medium: "500",
bold: "700",
},
lineHeight: {
tight: "1.2",
normal: "1.4",
loose: "1.6",
},
},
spacing: {
xs: "2px",
sm: "4px",
md: "8px",
lg: "12px",
xl: "16px",
xxl: "24px",
},
borders: {
width: {
thin: "1px",
normal: "2px",
thick: "3px",
},
radius: {
none: "0px",
sm: "2px",
md: "4px",
lg: "8px",
full: "50%",
},
style: "solid",
},
shadows: {
sm: "0 1px 2px rgba(255,255,255,0.1)",
md: "0 2px 4px rgba(255,255,255,0.1)",
lg: "0 4px 8px rgba(255,255,255,0.15)",
},
animations: {
duration: {
fast: "150ms",
normal: "300ms",
slow: "500ms",
},
easing: {
linear: "linear",
easeIn: "ease-in",
easeOut: "ease-out",
easeInOut: "ease-in-out",
},
},
});
// 中国风主题
this.registerTheme("chinese", {
name: "中国风主题",
description: "传统中国风配色方案",
category: "builtin",
colors: {
primary: "#C8102E", // 中国红
secondary: "#FFD700", // 金色
accent: "#8B4513", // 棕色
background: "#FDF5E6", // 老花色
backgroundSecondary: "#F5F5DC", // 米色
surface: "#FFFAF0", // 花白色
textPrimary: "#2F4F4F", // 深灰绿
textSecondary: "#696969", // 暗灰色
textAccent: "#8B0000", // 暗红色
borderPrimary: "#8B4513", // 棕色
borderSecondary: "#D2B48C", // 浅棕色
borderLight: "#F0E68C", // 浅黄色
palaceBackground: "#FFFAF0", // 花白色
palaceBorder: "#DEB887", // 浅棕色
palaceHover: "#FFF8DC", // 米色
starMajor: "#C8102E", // 中国红 - 主星
starMinor: "#4682B4", // 钢蓝色 - 辅星
starEvil: "#8B008B", // 暗洋红 - 煞星
starLucky: "#228B22", // 森林绿 - 吉星
sihuaLu: "#32CD32", // 酸橙绿 - 化禄
sihuaQuan: "#FF6347", // 番茄红 - 化权
sihuaKe: "#4169E1", // 皇家蓝 - 化科
sihuaJi: "#8B008B", // 暗洋红 - 化忌
success: "#228B22", // 森林绿
warning: "#FF8C00", // 暗橙色
error: "#DC143C", // 深红色
info: "#4682B4", // 钢蓝色
},
typography: {
fontFamily: '"KaiTi", "SimKai", "Microsoft YaHei", serif',
fontSize: {
title: "20px",
subtitle: "16px",
body: "14px",
caption: "12px",
palaceName: "16px",
starName: "13px",
ganZhi: "12px",
},
fontWeight: {
normal: "400",
medium: "500",
bold: "700",
},
lineHeight: {
tight: "1.3",
normal: "1.5",
loose: "1.7",
},
},
spacing: {
xs: "3px",
sm: "6px",
md: "10px",
lg: "15px",
xl: "20px",
xxl: "30px",
},
borders: {
width: {
thin: "1px",
normal: "2px",
thick: "4px",
},
radius: {
none: "0px",
sm: "3px",
md: "6px",
lg: "12px",
full: "50%",
},
style: "solid",
},
shadows: {
sm: "0 2px 4px rgba(139,69,19,0.1)",
md: "0 4px 8px rgba(139,69,19,0.15)",
lg: "0 8px 16px rgba(139,69,19,0.2)",
},
animations: {
duration: {
fast: "200ms",
normal: "400ms",
slow: "600ms",
},
easing: {
linear: "linear",
easeIn: "ease-in",
easeOut: "ease-out",
easeInOut: "ease-in-out",
},
},
});
// 简约主题
this.registerTheme("minimal", {
name: "简约主题",
description: "现代简约配色方案",
category: "builtin",
colors: {
primary: "#007AFF", // 蓝色
secondary: "#5856D6", // 紫色
accent: "#FF3B30", // 红色
background: "#FFFFFF", // 白色
backgroundSecondary: "#F2F2F7", // 浅灰色
surface: "#FFFFFF", // 白色
textPrimary: "#000000", // 黑色
textSecondary: "#8E8E93", // 灰色
textAccent: "#007AFF", // 蓝色
borderPrimary: "#C7C7CC", // 浅灰色
borderSecondary: "#E5E5EA", // 更浅灰色
borderLight: "#F2F2F7", // 极浅灰色
palaceBackground: "#FFFFFF", // 白色
palaceBorder: "#E5E5EA", // 浅灰色
palaceHover: "#F2F2F7", // 极浅灰色
starMajor: "#007AFF", // 蓝色 - 主星
starMinor: "#5856D6", // 紫色 - 辅星
starEvil: "#FF3B30", // 红色 - 煞星
starLucky: "#34C759", // 绿色 - 吉星
sihuaLu: "#34C759", // 绿色 - 化禄
sihuaQuan: "#FF9500", // 橙色 - 化权
sihuaKe: "#007AFF", // 蓝色 - 化科
sihuaJi: "#FF3B30", // 红色 - 化忌
success: "#34C759", // 绿色
warning: "#FF9500", // 橙色
error: "#FF3B30", // 红色
info: "#007AFF", // 蓝色
},
typography: {
fontFamily:
'"SF Pro Display", "Helvetica Neue", "Microsoft YaHei", sans-serif',
fontSize: {
title: "17px",
subtitle: "13px",
body: "11px",
caption: "9px",
palaceName: "13px",
starName: "10px",
ganZhi: "9px",
},
fontWeight: {
normal: "400",
medium: "500",
bold: "600",
},
lineHeight: {
tight: "1.1",
normal: "1.3",
loose: "1.5",
},
},
spacing: {
xs: "1px",
sm: "2px",
md: "4px",
lg: "8px",
xl: "12px",
xxl: "16px",
},
borders: {
width: {
thin: "0.5px",
normal: "1px",
thick: "2px",
},
radius: {
none: "0px",
sm: "1px",
md: "2px",
lg: "4px",
full: "50%",
},
style: "solid",
},
shadows: {
sm: "0 1px 1px rgba(0,0,0,0.05)",
md: "0 1px 2px rgba(0,0,0,0.05)",
lg: "0 2px 4px rgba(0,0,0,0.1)",
},
animations: {
duration: {
fast: "100ms",
normal: "200ms",
slow: "300ms",
},
easing: {
linear: "linear",
easeIn: "ease-in",
easeOut: "ease-out",
easeInOut: "ease-in-out",
},
},
});
// 设置默认主题
this.setCurrentTheme("classic");
}
/**
* 注册主题
*/
registerTheme(id, themeConfig) {
const theme = {
id: id,
...themeConfig,
registeredAt: new Date().toISOString(),
version: "1.0.0",
};
this.themes.set(id, theme);
console.log(`主题已注册: ${id}`);
}
/**
* 注册自定义主题
*/
registerCustomTheme(id, themeConfig, userId = "default") {
const customTheme = {
id: id,
userId: userId,
...themeConfig,
category: "custom",
createdAt: new Date().toISOString(),
version: "1.0.0",
};
this.customThemes.set(id, customTheme);
if (this.config.autoSave) {
this.saveCustomTheme(id);
}
console.log(`自定义主题已注册: ${id}`);
}
/**
* 获取主题
*/
getTheme(themeId) {
return this.themes.get(themeId) || this.customThemes.get(themeId);
}
/**
* 获取所有主题
*/
getAllThemes() {
const allThemes = [];
// 内置主题
this.themes.forEach((theme) => {
allThemes.push({
id: theme.id,
name: theme.name,
description: theme.description,
category: theme.category,
preview: this.generateThemePreview(theme),
});
});
// 自定义主题
this.customThemes.forEach((theme) => {
allThemes.push({
id: theme.id,
name: theme.name,
description: theme.description,
category: theme.category,
userId: theme.userId,
preview: this.generateThemePreview(theme),
});
});
return allThemes;
}
/**
* 设置当前主题
*/
setCurrentTheme(themeId) {
const theme = this.getTheme(themeId);
if (!theme) {
throw new Error(`主题不存在: ${themeId}`);
}
this.currentTheme = theme;
this.updateThemeVariables(theme);
console.log(`当前主题已切换到: ${themeId}`);
return theme;
}
/**
* 获取当前主题
*/
getCurrentTheme() {
return this.currentTheme;
}
/**
* 更新主题变量
*/
updateThemeVariables(theme) {
this.themeVariables.clear();
// 展平主题配置
this.flattenThemeConfig(theme, "", this.themeVariables);
}
/**
* 展平主题配置
*/
flattenThemeConfig(obj, prefix, variables) {
Object.keys(obj).forEach((key) => {
const value = obj[key];
const fullKey = prefix ? `${prefix}.${key}` : key;
if (
typeof value === "object" &&
value !== null &&
!Array.isArray(value)
) {
this.flattenThemeConfig(value, fullKey, variables);
} else {
variables.set(fullKey, value);
}
});
}
/**
* 获取主题变量
*/
getThemeVariable(path) {
return this.themeVariables.get(path);
}
/**
* 应用主题到SVG数据
*/
applyThemeToData(data, themeId = null) {
const theme = themeId ? this.getTheme(themeId) : this.currentTheme;
if (!theme) {
return data;
}
// 深拷贝数据
const themedData = JSON.parse(JSON.stringify(data));
// 应用主题
themedData.theme = this.buildThemeData(theme);
return themedData;
}
/**
* 构建主题数据
*/
buildThemeData(theme) {
return {
// 基础属性
name: theme.name,
id: theme.id,
// 颜色
background: theme.colors.background,
backgroundSecondary: theme.colors.backgroundSecondary,
surface: theme.colors.surface,
primary: theme.colors.primary,
secondary: theme.colors.secondary,
accent: theme.colors.accent,
// 文本颜色
textColor: theme.colors.textPrimary,
textSecondary: theme.colors.textSecondary,
textAccent: theme.colors.textAccent,
// 边框
borderColor: theme.colors.borderPrimary,
borderSecondary: theme.colors.borderSecondary,
borderWidth: theme.borders.width.normal,
borderRadius: theme.borders.radius.md,
// 宫位
palaceBackground: theme.colors.palaceBackground,
palaceBorder: theme.colors.palaceBorder,
palaceHover: theme.colors.palaceHover,
// 星曜
starMajor: theme.colors.starMajor,
starMinor: theme.colors.starMinor,
starEvil: theme.colors.starEvil,
starLucky: theme.colors.starLucky,
// 四化
sihuaLu: theme.colors.sihuaLu,
sihuaQuan: theme.colors.sihuaQuan,
sihuaKe: theme.colors.sihuaKe,
sihuaJi: theme.colors.sihuaJi,
// 字体
fontFamily: theme.typography.fontFamily,
fontSize: theme.typography.fontSize.body,
titleSize: theme.typography.fontSize.title,
subtitleSize: theme.typography.fontSize.subtitle,
palaceNameSize: theme.typography.fontSize.palaceName,
starNameSize: theme.typography.fontSize.starName,
ganZhiSize: theme.typography.fontSize.ganZhi,
// 间距
spacing: theme.spacing,
// 阴影
shadow: theme.shadows.md,
// 动画
transitionDuration: theme.animations.duration.normal,
transitionEasing: theme.animations.easing.easeInOut,
};
}
/**
* 创建主题变体
*/
createThemeVariant(baseThemeId, variantConfig, variantId) {
const baseTheme = this.getTheme(baseThemeId);
if (!baseTheme) {
throw new Error(`基础主题不存在: ${baseThemeId}`);
}
// 深度合并配置
const variantTheme = this.deepMerge(baseTheme, variantConfig);
variantTheme.id = variantId;
variantTheme.name = variantConfig.name || `${baseTheme.name} 变体`;
variantTheme.baseTheme = baseThemeId;
variantTheme.category = "variant";
this.registerCustomTheme(variantId, variantTheme);
return variantTheme;
}
/**
* 深度合并对象
*/
deepMerge(target, source) {
const result = JSON.parse(JSON.stringify(target));
Object.keys(source).forEach((key) => {
if (
source[key] &&
typeof source[key] === "object" &&
!Array.isArray(source[key])
) {
result[key] = this.deepMerge(result[key] || {}, source[key]);
} else {
result[key] = source[key];
}
});
return result;
}
/**
* 生成主题预览
*/
generateThemePreview(theme) {
return {
colors: {
primary: theme.colors.primary,
secondary: theme.colors.secondary,
background: theme.colors.background,
text: theme.colors.textPrimary,
},
sample: this.generateSampleSVG(theme),
};
}
/**
* 生成示例SVG
*/
generateSampleSVG(theme) {
return `
<svg width="100" height="60" viewBox="0 0 100 60" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="60" fill="${theme.colors.background}" stroke="${theme.colors.borderPrimary}" stroke-width="1"/>
<circle cx="25" cy="30" r="8" fill="${theme.colors.primary}"/>
<rect x="40" y="22" width="16" height="16" fill="${theme.colors.secondary}" rx="2"/>
<text x="70" y="35" font-family="${theme.typography.fontFamily}" font-size="10" fill="${theme.colors.textPrimary}">主题</text>
</svg>
`;
}
/**
* 验证主题配置
*/
validateTheme(themeConfig) {
const required = {
name: "string",
"colors.primary": "string",
"colors.background": "string",
"colors.textPrimary": "string",
"typography.fontFamily": "string",
};
const errors = [];
Object.keys(required).forEach((path) => {
const value = this.getNestedValue(themeConfig, path);
const expectedType = required[path];
if (value === undefined) {
errors.push(`缺少必需属性: ${path}`);
} else if (typeof value !== expectedType) {
errors.push(`属性类型错误: ${path} 应为 ${expectedType}`);
}
});
// 验证颜色格式
this.validateColors(themeConfig.colors, errors);
return {
valid: errors.length === 0,
errors: errors,
};
}
/**
* 验证颜色格式
*/
validateColors(colors, errors) {
if (!colors) return;
const colorRegex =
/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$|^rgb\(|^rgba\(|^hsl\(|^hsla\(/;
Object.keys(colors).forEach((key) => {
const color = colors[key];
if (typeof color === "string" && !colorRegex.test(color)) {
errors.push(`无效的颜色格式: ${key} = ${color}`);
}
});
}
/**
* 获取嵌套值
*/
getNestedValue(obj, path) {
return path.split(".").reduce((current, key) => {
return current && current[key] !== undefined ? current[key] : undefined;
}, obj);
}
/**
* 导出主题
*/
exportTheme(themeId) {
const theme = this.getTheme(themeId);
if (!theme) {
throw new Error(`主题不存在: ${themeId}`);
}
return {
...theme,
exportedAt: new Date().toISOString(),
version: theme.version || "1.0.0",
};
}
/**
* 导入主题
*/
importTheme(themeData, options = {}) {
const validation = this.validateTheme(themeData);
if (!validation.valid) {
throw new Error(`主题验证失败: ${validation.errors.join(", ")}`);
}
const themeId = options.id || themeData.id || `imported_${Date.now()}`;
if (options.asCustom !== false) {
this.registerCustomTheme(themeId, themeData);
} else {
this.registerTheme(themeId, themeData);
}
return themeId;
}
/**
* 删除主题
*/
removeTheme(themeId) {
const removed =
this.themes.delete(themeId) || this.customThemes.delete(themeId);
if (this.currentTheme && this.currentTheme.id === themeId) {
this.setCurrentTheme("classic"); // 回退到默认主题
}
return removed;
}
/**
* 保存自定义主题
*/
saveCustomTheme(themeId) {
const theme = this.customThemes.get(themeId);
if (!theme) return false;
try {
// 这里可以实现实际的保存逻辑
// 例如保存到本地存储或发送到服务器
console.log(`自定义主题已保存: ${themeId}`);
return true;
} catch (error) {
console.error(`保存自定义主题失败: ${themeId}`, error);
return false;
}
}
/**
* 加载自定义主题
*/
loadCustomThemes() {
try {
// 这里可以实现实际的加载逻辑
// 例如从本地存储或服务器加载
console.log("自定义主题已加载");
return true;
} catch (error) {
console.error("加载自定义主题失败", error);
return false;
}
}
/**
* 获取主题统计
*/
getStats() {
return {
totalThemes: this.themes.size + this.customThemes.size,
builtinThemes: this.themes.size,
customThemes: this.customThemes.size,
currentTheme: this.currentTheme ? this.currentTheme.id : null,
themeVariables: this.themeVariables.size,
};
}
}
module.exports = {
SVGThemeManager,
};