MonkeyCode 自定义主题与扩展系统:打造专属于你的 AI 编程环境
引言
"每个开发者都是独一无二的——你的工具应该适应你,而不是让你去适应工具。"
在 AI 编程助手领域,"一刀切"的体验早已无法满足开发者的多样化需求。有的开发者喜欢深色背景护眼,有的偏爱明亮界面提神;有的需要极简界面专注编码,有的希望信息密度最大化;有的团队有严格的代码规范,有的个人开发者追求极致自由。
MonkeyCode 作为完全开源(Apache License 2.0)的 AI 编程助手,从设计之初就将高度可定制性作为核心架构原则。本文将深入剖析 MonkeyCode 的主题系统、扩展机制、以及如何让每一位开发者都能打造出最适合自己的 AI 编程环境。
🎯 核心信息
- GitHub 仓库: https://github.com/monkeycode-ai/monkeycode
- 开源协议: Apache License 2.0
- 主题系统版本: v4.1 (支持 CSS 变量 + JSON Schema)
- 扩展 API: 完整的 Plugin SDK + Webhook 系统
- 欢迎贡献主题和扩展!
一、主题系统架构
1.1 主题架构总览
┌─────────────────────────────────────────────────────────────┐
│ MonkeyCode 主题系统架构 │
│ │
│ ══════════════════════════════════════════════════════ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 主题定义层 (Theme Definition) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ 内置主题 │ │ 社区主题 │ │ 自定义 │ │ │
│ │ │ (8款) │ │ (50+款) │ │ 用户主题 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼───────────────────────────────┐ │
│ │ 主题引擎层 (Theme Engine) │ │
│ │ │ │
│ │ • CSS Variables Resolver │ │
│ │ • Color Palette Generator │ │
│ │ • Syntax Highlighting Mapper │ │
│ │ • Icon Set Manager │ │
│ │ • Font Configuration │ │
│ │ • Layout Template Engine │ │
│ └─────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼───────────────────────────────┐ │
│ │ 渲染层 (Rendering) │ │
│ │ │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │编辑器 │ │面板 │ │状态栏 │ │通知 │ │ │
│ │ │主题 │ │主题 │ │主题 │ │主题 │ │ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
╚═══════════════════════════════════════════════════════════╝
1.2 主题 Schema 定义
// ===== packages/theme/src/schema/theme-schema.ts =====
/**
* MonkeyCode 主题 Schema 定义
*
* 使用 JSON Schema 定义完整的主题配置,
* 支持类型检查、自动补全、验证和迁移
*/
import { z } from 'zod';
// === 颜色定义 ===
export const ColorTokenSchema = z.object({
// 核心颜色
primary: z.string(), // 主色调
secondary: z.string(), // 辅助色
accent: z.string(), // 强调色
// 背景色
background: z.object({
primary: z.string(), // 主背景
secondary: z.string(), // 次级背景(侧边栏等)
tertiary: z.string(), // 三级背景(卡片、弹窗)
elevated: z.string(), // 浮起元素(按钮、输入框)
}),
// 前景色(文字)
foreground: z.object({
primary: z.string(), // 主文字
secondary: z.string(), // 次要文字
muted: z.string(), // 弱化文字(占位符、提示)
disabled: z.string(), // 禁用文字
link: z.string(), // 链接颜色
}),
// 边框
border: z.object({
default: z.string(), // 默认边框
subtle: z.string(), // 微妙边框
focus: z.string(), // 聚焦边框
active: z.string(), // 激活边框
}),
// 状态色
status: z.object({
success: z.string(),
warning: z.string(),
error: z.string(),
info: z.string(),
}),
// 语法高亮(代码着色)
syntax: z.object({
comment: z.string(),
string: z.string(),
keyword: z.string(),
function: z.string(),
variable: z.string(),
type: z.string(),
number: z.string(),
operator: z.string(),
punctuation: z.string(),
class: z.string(),
constant: z.string(),
decorator: z.string(),
property: z.string(),
parameter: z.string(),
regexp: z.string(),
tag: z.string(),
attributeName: z.string(),
attributeValue: z.string(),
}),
});
// === 字体定义 ===
export const FontConfigSchema = z.object({
// 主字体
fontFamily: z.object({
mono: z.string().default("'JetBrains Mono', 'Fira Code', Consolas, monospace"),
sans: z.string().default("-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"),
serif: z.string().default("'Georgia', 'Times New Roman', serif"),
}),
// 字号
fontSize: z.object({
xs: z.number().default(11), // 极小
sm: z.number().default(12), // 小
base: z.number().default(14), // 基准
lg: z.number().default(16), // 大
xl: z.number().default(18), // 特大
'2xl': z.number().default(24), // 标题
'3xl': z.number().default(30), // 大标题
}),
// 行高
lineHeight: z.object({
tight: z.number().default(1.25),
normal: z.number().default(1.5),
relaxed: z.number().default(1.75),
}),
// 字重
fontWeight: z.object({
normal: z.number().default(400),
medium: z.number().default(500),
semibold: z.number().default(600),
bold: z.number().default(700),
}),
// 字间距
letterSpacing: z.object({
tight: z.string().default('-0.02em'),
normal: z.string().default('0'),
wide: z.string().default('0.05em'),
}),
// 特性
features: z.array(z.object({
feature: z.string(), // 如 "liga", "calt"
setting: z.string(), // 如 "on", "off"
})).optional(),
});
// === 间距系统 ===
export const SpacingSchema = z.object({
unit: z.number().default(4), // 基础单位 (px)
scale: z.object({
'0': z.number().default(0),
'1': z.number().default(4),
'2': z.number().default(8),
'3': z.number().default(12),
'4': z.number().default(16),
'5': z.number().default(20),
'6': z.number().default(24),
'8': z.number().default(32),
'10': z.number().default(40),
'12': z.number().default(48),
'16': z.number().default(64),
}),
borderRadius: z.object({
none: z.string().default('0'),
sm: z.string().default('4px'),
md: z.string().default('8px'),
lg: z.string().default('12px'),
xl: z.string().default('16px'),
full: z.string().default('9999px'),
}),
});
// === 完整主题 Schema ===
export const ThemeSchema = z.object({
// 元数据
metadata: z.object({
name: z.string(),
version: z.string(),
author: z.string(),
description: z.string().optional(),
displayName: z.string(),
// 兼容性
engineVersion: z.string().default('^4.0'),
// 分类标签
tags: z.array(z.enum([
'dark', 'light', 'high-contrast',
'minimal', 'colorful', 'pastel',
'retro', 'futuristic', 'nature',
'professional', 'playful',
])).optional(),
}),
// 类型
type: z.enum(['dark', 'light']).default('dark'),
// 颜色
colors: ColorTokenSchema,
// 字体
fonts: FontConfigSchema,
// 间距
spacing: SpacingSchema,
// UI 组件覆盖
components: z.record(z.any()).optional(),
// 自定义 CSS
customCSS: z.string().optional(),
// 继承关系
extends: z.string().optional(), // 继承自哪个主题
});
export type Theme = z.infer<typeof ThemeSchema>;
export type ColorTokens = z.infer<typeof ColorTokenSchema>;
1.3 内置主题展示
// ===== packages/theme/src/built-in-themes.ts =====
/**
* MonkeyCode 内置主题集合
*
* 提供 8 款精心设计的开箱即用主题
*/
import { Theme } from './schema';
export const builtInThemes: Record<string, Theme> = {
// === 深色主题 ===
'midnight-blue': {
metadata: {
name: 'midnight-blue',
version: '4.1.0',
author: 'MonkeyCode Team',
displayName: '🌙 午夜蓝',
description: '经典的深蓝色调,适合长时间编程',
tags: ['dark', 'professional'],
},
type: 'dark',
colors: {
primary: '#6C9BD3',
secondary: '#7B9FCF',
accent: '#5B8DD1',
background: {
primary: '#0D1117',
secondary: '#161B22',
tertiary: '#21262D',
elevated: '#30363D',
},
foreground: {
primary: '#E6EDF3',
secondary: '#B1BCC8',
muted: '#656D76',
disabled: '#484F58',
link: '#58A6FF',
},
border: {
default: '#30363D',
subtle: '#21262D',
focus: '#388BFF',
active: '#58A6FF',
},
status: {
success: '#3FB950',
warning: '#D29922',
error: '#F85149',
info: '#58A6FF',
},
syntax: {
comment: '#8B949E',
string: '#A5D6FF',
keyword: '#FF7B72',
function: '#D2A8FF',
variable: '#FFA657',
type: '#79C0FF',
number: '#79C0FF',
operator: '#FF7B72',
punctuation: '#E6EDF3',
class: '#7EE787',
constant: '#79C0FF',
decorator: '#D2A8FF',
property: '#79C0FF',
parameter: '#FFA657',
regexp: '#A5D6FF',
tag: '#7EE787',
attributeName: '#79C0FF',
attributeValue: '#A5D6FF',
},
},
fonts: { /* ... */ },
spacing: { /* ... */ },
},
'forest-green': {
metadata: {
name: 'forest-green',
version: '4.1.0',
author: 'MonkeyCode Team',
displayName: '🌲 森林绿',
description: '护眼的绿色调,灵感来自自然森林',
tags: ['dark', 'nature'],
},
type: 'dark',
colors: {
primary: '#7FD962',
secondary: '#68BC52',
accent: '#98E37D',
background: {
primary: '#1A2421',
secondary: '#222E29',
tertiary: '#2D3D36',
elevated: '#3A4C43',
},
foreground: {
primary: '#E0EDE6',
secondary: '#A8C4B5',
muted: '#6B8A77',
disabled: '#4A6357',
link: '#7FD962',
},
syntax: {
comment: '#5C7A6B',
string: '#B5E8A0',
keyword: '#F28B82',
function: '#D4A8FF',
variable: '#FFCB6B',
type: '#82DFFF',
// ... 其他语法颜色
},
// ... 其他颜色配置
},
fonts: { /* ... */ },
spacing: { /* ... */ },
},
'sunset-orange': {
metadata: {
name: 'sunset-orange',
version: '4.1.0',
author: 'MonkeyCode Team',
displayName: '🌅 日落橙',
description: '温暖的橙色系,充满活力与创造力',
tags: ['dark', 'colorful'],
},
type: 'dark',
colors: {
primary: '#FF9940',
secondary: '#E88A35',
accent: '#FFAD60',
background: {
primary: '#1E1814',
secondary: '#2A211B',
tertiary: '#382D26',
elevated: '#483B31',
},
foreground: {
primary: '#F5EDE6',
secondary: '#C4B5A8',
muted: '#8A7A6B',
disabled: '#5E5247',
link: '#FF9940',
},
syntax: {
comment: '#7A6B5C',
string: '#FFE0B2',
keyword: '#FF8A80',
function: '#EA80FC',
variable: '#FFD54F',
type: '#80DEEA',
// ...
},
// ...
},
fonts: { /* ... */ },
spacing: { /* ... */ },
},
// === 浅色主题 ===
'cloud-white': {
metadata: {
name: 'cloud-white',
version: '4.1.0',
author: 'MonkeyCode Team',
displayName: '☁️ 云白',
description: '清爽明亮的白色主题,适合白天使用',
tags: ['light', 'minimal'],
},
type: 'light',
colors: {
primary: '#0969DA',
secondary: '#0550AE',
accent: '#218BFF',
background: {
primary: '#FFFFFF',
secondary: '#F6F8FA',
tertiary: '#EAEEF2',
elevated: '#FFFFFF',
},
foreground: {
primary: '#1F2328',
secondary: '#656D76',
muted: '#8C959F',
disabled: '#B1BCC8',
link: '#0969DA',
},
syntax: {
comment: '#6E7781',
string: '#0A3069',
keyword: '#CF222E',
function: '#8250DF',
variable: '#953800',
type: '#0550AE',
// ...
},
// ...
},
fonts: { /* ... */ },
spacing: { /* ... */ },
},
// ... 更多内置主题
};
二、扩展系统架构
2.1 扩展 API 设计
// ===== packages/extensions/src/api/plugin-api.ts =====
/**
* MonkeyCode 扩展 API
*
* 提供完整的插件开发接口,
* 让第三方开发者可以扩展 MonkeyCode 的功能
*/
import { EventEmitter } from 'events';
import { z } from 'zod';
// === 扩展元数据 ===
export interface ExtensionManifest {
// 基本信息
id: string; // 唯一标识: com.example.my-plugin
name: string;
version: string;
description: string;
// 作者信息
author: {
name: string;
email?: string;
url?: string;
};
// 兼容性
engineVersion: string; // 语义化版本范围: ^4.0.0
// 入口点
main: string; // 入口文件路径
// 权限声明
permissions: ExtensionPermission[];
// 依赖
dependencies?: Record<string, string>;
// 激活事件
activationEvents: ActivationEvent[];
// 贡献点(扩展点注册)
contributes?: ContributionPoints;
}
// === 权限类型 ===
export enum ExtensionPermission {
// 文件系统
READ_FILES = 'fs:read', // 读取文件
WRITE_FILES = 'fs:write', // 写入文件
// 编辑器
EDIT_DOCUMENT = 'editor:edit', // 编辑文档
SHOW_NOTIFICATION = 'ui:notification', // 显示通知
// 网络
NETWORK_REQUEST = 'network:request', // 网络请求
// AI 功能
AI_COMPLETION = 'ai:completion', // AI 补全
AI_CHAT = 'ai:chat', // AI 对话
// 配置
READ_CONFIG = 'config:read', // 读取配置
WRITE_CONFIG = 'config:write', // 写入配置
// Shell
EXECUTE_SHELL = 'shell:execute', // 执行 Shell 命令
// 存储
KEY_VALUE_STORE = 'storage:kvp', // 键值存储
SECRET_STORAGE = 'storage:secret', // 安全存储
}
// === 扩展上下文 API ===
export class ExtensionContext extends EventEmitter {
private _secrets: SecretStorage;
private _globalState: Memento;
private _workspaceState: Memento;
/** 扩展绝对路径 */
readonly extensionPath: string;
/** 扩展存储目录 */
readonly storagePath: string;
/** 全局状态(跨工作区持久化) */
get globalState(): Memento {
return this._globalState;
}
/** 工作区状态 */
get workspaceState(): Memento {
return this._workspaceState;
}
/** 安全存储(如 API 密钥) */
get secrets(): SecretStorage {
return this._secrets;
}
/**
* 订阅命令
*/
registerCommand(
commandId: string,
handler: (...args: any[]) => any,
thisArg?: any,
): Disposable {
return commands.registerCommand(
`${this.extension.id}.${commandId}`,
handler,
thisArg,
);
}
/**
* 注册语言特性
*/
registerCompletionItemProvider(
selector: DocumentSelector,
provider: CompletionItemProvider,
triggerCharacters?: string[],
): Disposable {
return languages.registerCompletionItemProvider(selector, provider, triggerCharacters);
}
/**
* 注册代码操作
*/
registerCodeActionsProvider(
selector: DocumentSelector,
provider: CodeActionsProvider,
metadata?: CodeActionMetadata,
): Disposable {
return languages.registerCodeActionsProvider(selector, provider, metadata);
}
/**
* 注册悬停提示
*/
registerHoverProvider(
selector: DocumentSelector,
provider: HoverProvider,
): Disposable {
return languages.registerHoverProvider(selector, provider);
}
/**
* 注册自定义视图
*/
registerWebviewViewProvider(
viewId: string,
provider: WebviewViewProvider,
): Disposable {
return window.registerWebviewViewProvider(viewId, provider);
}
/**
* 创建输出通道
*/
createOutputChannel(name: string): OutputChannel {
return window.createOutputChannel(`[${this.extension.name}] ${name}`);
}
/**
* 显示进度条
*/
withProgress<R>(
options: ProgressOptions,
task: (
progress: Progress<{ message?: string; increment?: number }>,
token: CancellationToken,
) => Thenable<R>,
): Thenable<R> {
return window.withProgress(options, task);
}
/**
* 显示信息消息
*/
showInformationMessage(
message: string,
...items: string[]
): Thenable<string | undefined> {
return window.showInformationMessage(message, ...items);
}
/**
* 访问 AI 引擎
*/
get ai(): AIExtensionAPI {
return new AIExtensionAPI(this);
}
/**
* 访问主题 API
*/
get theme(): ThemeExtensionAPI {
return new ThemeExtensionAPI(this);
}
}
2.2 扩展贡献点
// ===== packages/extensions/src/api/contribution-points.ts =====
/**
* 扩展贡献点 (Contribution Points)
*
* 定义扩展可以注入功能的各个位置
*/
export interface ContributionPoints {
// === 命令 ===
commands?: Array<{
command: string;
title: string;
category?: string;
icon?: string;
keybinding?: string;
when?: string; // 条件表达式
}>;
// === 菜单 ===
menus?: Record<string, Array<{
command: string;
alt?: string; // Alt 点击时执行的命令
when?: string;
group?: string;
}>>;
// === 快捷键 ===
keybindings?: Array<{
command: string;
key: string;
mac?: string; // macOS 专用快捷键
when?: string;
}>;
// === 语言支持 ===
languages?: Array<{
id: string;
aliases?: string[];
extensions: string[];
configuration?: LanguageConfiguration;
}>;
// === 语法高亮 ===
grammars?: Array<{
language: string;
scopeName: string;
path: string;
injectTo?: string[];
}>;
// === 代码片段 ===
snippets?: Array<{
language: string;
path: string;
}>;
// === 主题 ===
themes?: Array<{
label: string;
uiTheme: 'vs-dark' | 'vs-light' | 'hc-black' | 'hc-light';
path: string;
}>;
// === 图标主题 ===
iconThemes?: Array<{
id: string;
label: string;
path: string;
}>;
// === 自定义视图 ===
views?: {
[viewContainerId]: Array<{
type: string;
id: string;
name: string;
icon?: string;
when?: string;
}>;
};
// === 设置 ===
configuration?: {
title: string;
properties: Record<string, JSONSchema>;
};
// === AI 增强 ===
aiEnhancements?: Array<{
id: string;
type: 'completion' | 'chat' | 'review' | 'refactor';
languages?: string[];
priority: number;
handler: string; // 处理器模块路径
}>;
// === Webhook ===
webhooks?: Array<{
event: string;
url: string;
headers?: Record<string, string>;
secret?: string;
}>;
// === 外部工具集成 ===
externalTools?: Array<{
id: string;
name: string;
command: string;
args?: string[];
env?: Record<string, string>;
}>;
}
2.3 扩展开发示例
// ===== example-extension/src/extension.ts =====
/**
* MonkeyCode 扩展开发示例
*
* 一个完整的扩展实现:
* 为 SQL 文件添加表结构智能补全
*/
import * as vscode from 'vscode';
import { ExtensionContext, ExtensionManifest } from '@monkeycode/extensions';
const MANIFEST: ExtensionManifest = {
id: 'com.monkeycode.sql-intellisense',
name: 'SQL IntelliSense',
version: '1.0.0',
description: '为 SQL 文件提供数据库感知的智能补全',
author: { name: 'MonkeyCode Community' },
engineVersion: '^4.0.0',
main: './out/extension.js',
permissions: [
ExtensionPermission.READ_CONFIG,
ExtensionPermission.AI_COMPLETION,
],
activationEvents: [
'onLanguage:sql',
'onLanguage:mysql',
'onLanguage:postgresql',
],
contributes: {
commands: [
{
command: 'sqlIntelliSense.refreshSchema',
title: '刷新数据库 Schema 缓存',
category: 'SQL IntelliSense',
},
{
command: 'sqlIntelliSense.showTableInfo',
title: '显示当前表的详细信息',
category: 'SQL IntelliSense',
},
],
configuration: {
title: 'SQL IntelliSense Settings',
properties: {
'sqlIntelliSense.connectionString': {
type: 'string',
default: '',
description: '数据库连接字符串',
order: 1,
},
'sqlIntelliSense.cacheTTL': {
type: 'number',
default: 300,
description: 'Schema 缓存有效期(秒)',
order: 2,
},
'sqlIntelliSense.enableAutoComplete': {
type: 'boolean',
default: true,
description: '启用 SQL 自动补全',
order: 3,
},
},
},
},
};
export async function activate(context: ExtensionContext): Promise<void> {
console.log('🐵 SQL IntelliSense extension activated!');
// 初始化 Schema 缓存管理器
const schemaCache = new DatabaseSchemaCache(context);
// 注册补全提供者
const completionProvider = new SQLCompletionProvider(schemaCache);
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider(
{ scheme: 'file', language: 'sql' },
completionProvider,
' ', '.', '`', // 触发字符
),
vscode.languages.registerCompletionItemProvider(
{ scheme: 'file', language: 'mysql' },
completionProvider,
' ', '.', '`',
),
vscode.languages.registerHoverProvider(
{ scheme: 'file', language: ['sql', 'mysql', 'postgresql'] },
new SQLHoverProvider(schemaCache),
),
// 注册命令
vscode.commands.registerCommand(
'sqlIntelliSense.refreshSchema',
() => schemaCache.refresh(),
),
vscode.commands.registerCommand(
'sqlIntelliSense.showTableInfo',
() => showTableInfo(schemaCache),
),
);
// 定期刷新缓存
const refreshInterval = setInterval(() => {
schemaCache.refreshIfNeeded();
}, 5 * 60 * 1000); // 每5分钟检查一次
context.subscriptions.push(new Disposable(() => clearInterval(refreshInterval)));
}
class SQLCompletionProvider implements vscode.CompletionItemProvider {
private cache: DatabaseSchemaCache;
constructor(cache: DatabaseSchemaCache) {
this.cache = cache;
}
async provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
context: vscode.CompletionContext,
): Promise<vscode.CompletionItem[]> {
const textBeforeCursor = document.getText(
new vscode.Range(new vscode.Position(position.line, 0), position)
);
const items: vscode.CompletionItem[] = [];
// 表名补全
if (/FROM\s+$/i.test(textBeforeCursor) || /JOIN\s+$/i.test(textBeforeCursor)) {
const tables = await this.cache.getTables();
for (const table of tables) {
items.push(new vscode.CompletionItem(table.name, vscode.CompletionItemKind.Class));
items[items.length - 1].documentation = table.comment || `Table: ${table.name}`;
items[items.length - 1].detail = `${table.columns.length} columns`;
}
}
// 列名补全
if (/SELECT\s+\w*$/i.test(textBeforeCursor) || /WHERE\s+\w*$/i.test(textBeforeCursor)) {
const tableName = this.extractTableName(textBeforeCursor);
if (tableName) {
const columns = await this.cache.getTableColumns(tableName);
for (const col of columns) {
const item = new vscode.CompletionItem(col.name, vscode.CompletionItemKind.Field);
item.detail = col.type;
item.documentation = col.comment || `Column: ${col.name} (${col.type})`;
// 根据 SQL 关键字插入不同格式
if (/SELECT/i.test(textBeforeCursor)) {
item.insertText = col.name;
} else {
item.insertText = `${col.name} = $0`;
}
items.push(item);
}
}
}
return items;
}
private extractTableName(sql: string): string | null {
// 从 SQL 中提取最近的表名
const fromMatch = sql.match(/(?:FROM|JOIN)\s+(\w+)/i);
return fromMatch ? fromMatch[1] : null;
}
}
三、主题编辑器
3.1 可视化主题编辑器
// ===== packages/theme-editor/src/editor.ts =====
/**
* MonkeyCode 可视化主题编辑器
*
* 提供所见即所得的主题定制体验
*/
import { Theme, ThemeSchema } from '@monkeycode/theme';
export class ThemeEditor {
private currentTheme: Theme;
private previewFrame: HTMLIFrameElement;
private colorPickers: Map<string, ColorPicker> = new Map();
constructor(container: HTMLElement, initialTheme: Theme) {
this.currentTheme = initialTheme;
this.render(container);
}
private render(container: HTMLElement): void {
container.innerHTML = `
<div class="theme-editor">
<div class="editor-layout">
<!-- 左侧:属性面板 -->
<div class="properties-panel">
${this.renderColorSection()}
${this.renderFontSection()}
${this.renderSpacingSection()}
${this.renderCSSSection()}
</div>
<!-- 中间:预览区域 -->
<div class="preview-panel">
<div class="preview-toolbar">
<select id="preview-mode">
<option value="editor">代码编辑器</option>
<option value="chat">对话面板</option>
<option value="review">审查报告</option>
<option value="settings">设置页面</option>
</select>
<button id="toggle-dark-light">切换明暗</button>
<button id="export-theme">导出主题</button>
<button id="share-theme">分享主题</button>
</div>
<iframe class="preview-frame" id="preview"></iframe>
</div>
<!-- 右侧:代码视图 -->
<div class="code-panel">
<div class="code-tabs">
<button class="tab active" data-tab="json">JSON</button>
<button class="tab" data-tab="css">CSS Variables</button>
<button class="tab" data-tab="typescript">TypeScript</button>
</div>
<pre><code id="theme-code"></code></pre>
</div>
</div>
</div>
`;
this.previewFrame = container.querySelector('#preview') as HTMLIFrameElement;
this.bindEvents();
this.updatePreview();
this.updateCodeView();
}
private renderColorSection(): string {
const { colors } = this.currentTheme;
return `
<section class="section">
<h3>🎨 颜色</h3>
<div class="color-group">
<h4>品牌色</h4>
<div class="color-row">
${this.colorInput('primary', colors.primary, '主色调')}
${this.colorInput('secondary', colors.secondary, '辅助色')}
${this.colorInput('accent', colors.accent, '强调色')}
</div>
</div>
<div class="color-group">
<h4>背景色</h4>
<div class="color-row">
${this.colorInput('background.primary', colors.background.primary, '主背景')}
${this.colorInput('background.secondary', colors.background.secondary, '次级背景')}
${this.colorInput('background.tertiary', colors.background.tertiary, '三级背景')}
${this.colorInput('background.elevated', colors.background.elevated, '浮起元素')}
</div>
</div>
<div class="color-group">
<h4>前景色(文字)</h4>
<div class="color-row">
${this.colorInput('foreground.primary', colors.foreground.primary, '主文字')}
${this.colorInput('foreground.secondary', colors.foreground.secondary, '次要文字')}
${this.colorInput('foreground.muted', colors.foreground.muted, '弱化文字')}
${this.colorInput('foreground.link', colors.foreground.link, '链接')}
</div>
</div>
<div class="color-group">
<h4>语法高亮</h4>
<div class="color-grid">
${Object.entries(colors.syntax).map(([key, value]) =>
this.colorInput(`syntax.${key}`, value, key)
).join('')}
</div>
</div>
<div class="color-group">
<h4>状态色</h4>
<div class="color-row">
${this.colorInput('status.success', colors.status.success, '成功 ✅')}
${this.colorInput('status.warning', colors.status.warning, '警告 ⚠️')}
${this.colorInput('status.error', colors.status.error, '错误 ❌')}
${this.colorInput('status.info', colors.status.info, '信息 ℹ️')}
</div>
</div>
</section>
`;
}
private colorInput(path: string, value: string, label: string): string {
return `
<div class="color-input-wrapper">
<label>${label}</label>
<div class="color-input">
<input type="color" value="${value}" data-path="${path}" />
<input type="text" value="${value}" data-path="${path}" class="hex-input" />
</div>
</div>
`;
}
/**
* 更新实时预览
*/
private updatePreview(): void {
const cssVariables = this.generateCSSVariables();
const previewHTML = this.generatePreviewHTML(cssVariables);
this.previewFrame.srcdoc = previewHTML;
}
/**
* 从主题生成 CSS 变量
*/
private generateCSSVariables(): string {
const { colors, fonts, spacing } = this.currentTheme;
let vars = ':root {\n';
// 颜色变量
const flattenColors = (obj: any, prefix: string = '') => {
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'object' && value !== null) {
flattenColors(value, `${prefix}-${key}`);
} else {
vars += ` --mc${prefix}-${key}: ${value};\n`;
}
}
};
flattenColors(colors, '--color');
// 字体变量
vars += `\n --mc-font-mono: ${fonts.fontFamily.mono};\n`;
vars += ` --mc-font-sans: ${fonts.fontFamily.sans};\n`;
vars += ` --mc-font-size-base: ${fonts.fontSize.base}px;\n`;
// 间距变量
vars += ` --mc-spacing-unit: ${spacing.unit}px;\n`;
vars += '}';
return vars;
}
/**
* 导出主题为可分发格式
*/
export(format: 'json' | 'css' | 'package'): string {
switch (format) {
case 'json':
return JSON.stringify(this.currentTheme, null, 2);
case 'css':
return this.generateFullCSS();
case 'package':
return this.generatePackageJSON();
}
}
}
四、社区主题市场
4.1 主题市场架构
// ===== packages/marketplace/src/theme-market.ts =====
/**
* MonkeyCode 主题市场
*
* 发现、安装、管理和分享社区主题
*/
export interface MarketTheme {
id: string;
name: string;
author: AuthorInfo;
version: string;
description: string;
// 元数据
tags: string[];
downloads: number;
rating: number;
ratingCount: number;
// 预览
screenshots: string[];
previewGif?: string;
// 安装信息
themePath: string;
manifest: Theme;
// 来源
source: 'official' | 'community' | 'verified';
// 兼容性
minEngineVersion: string;
lastUpdated: Date;
}
class ThemeMarketplace {
private registryUrl = 'https://market.monkeycode.ai';
private installedThemes = new Map<string, InstalledTheme>();
/**
* 搜索主题
*/
async searchThemes(query: SearchQuery): Promise<MarketTheme[]> {
const params = new URLSearchParams({
q: query.query || '',
sort: query.sort || 'popularity',
order: query.order || 'desc',
limit: String(query.limit || 20),
offset: String(query.offset || 0),
...(query.tags ? { tags: query.tags.join(',') } : {}),
...(query.darkOnly ? { dark: 'true' } : {}),
...(query.lightOnly ? { light: 'true' } : {}),
});
const response = await fetch(`${this.registryUrl}/api/themes?${params}`);
const data = await response.json();
return data.themes.map((t: any) => this.normalizeMarketTheme(t));
}
/**
* 安装主题
*/
async installTheme(themeId: string): Promise<InstalledTheme> {
// 1. 获取主题元数据
const meta = await fetch(`${this.registryUrl}/api/themes/${themeId}`);
const themeData = await meta.json();
// 2. 下载主题包
const downloadUrl = themeData.downloadUrl;
const response = await fetch(downloadUrl);
const buffer = await response.arrayBuffer();
// 3. 解压并验证
const themeDir = path.join(this.themesDir, themeId);
await fs.mkdir(themeDir, { recursive: true });
await extractZip(buffer, themeDir);
// 4. 验证 Schema
const manifestPath = path.join(themeDir, 'theme.json');
const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
const validated = ThemeSchema.parse(manifest);
// 5. 注册到本地
const installed: InstalledTheme = {
id: themeId,
manifest: validated,
installPath: themeDir,
installedAt: new Date(),
enabled: true,
};
this.installedThemes.set(themeId, installed);
await this.saveInstalledList();
// 6. 触发主题变更事件
this.emit('installed', installed);
return installed;
}
/**
* 发布主题到市场
*/
async publishTheme(themePath: string, options: PublishOptions): Promise<PublishResult> {
// 1. 验证主题
const manifestPath = path.join(themePath, 'theme.json');
const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
// Schema 验证
const result = ThemeSchema.safeParse(manifest);
if (!result.success) {
throw new Error(`主题验证失败: ${result.error.message}`);
}
// 2. 打包
const packageBuffer = await createZip(themePath, {
ignore: ['.git', 'node_modules', '.DS_Store', '*.ts'],
});
// 3. 上传
const formData = new FormData();
formData.append('package', new Blob([packageBuffer]), 'theme.zip');
formData.append('manifest', JSON.stringify(manifest));
formData.append('token', options.token);
const response = await fetch(`${this.registryUrl}/api/themes/publish`, {
method: 'POST',
body: formData,
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || '发布失败');
}
const result = await response.json();
return {
success: true,
themeId: result.themeId,
publishedUrl: result.url,
version: manifest.metadata.version,
};
}
}
4.2 热门社区主题
| 主题名 | 作者 | 下载量 | ⭐ 评分 | 类型 | 特色 |
|---|---|---|---|---|---|
| Dracula Pro | @zenorocha | 128K | 4.9 | Dark | 经典 Dracula 配色 |
| Nord | @arcticicestudio | 95K | 4.9 | Dark | 北极光灵感的冷色调 |
| Tokyo Night | @enkia | 87K | 4.8 | Dark | 夜晚东京霓虹风格 |
| Catppuccin | @catppuccin | 76K | 4.9 | Dark/Light | 温暖的 SOULful 配色 |
| One Dark Pro | @binaryify | 72K | 4.7 | Dark | Atom One Dark 移植 |
| Solarized Light | @ethanschoonover | 64K | 4.8 | Light | 护眼低对比度浅色 |
| Gruvbox | @morhetz | 58K | 4.8 | Dark | 复古终端配色 |
| Rose Pine | @rose-pine | 45K | 4.9 | Dark/Light | 优雅的玫瑰松色系 |
| Material | @equinusocio | 42K | 4.7 | Dark/Light | Material Design 配色 |
| GitHub Sharp | @princiwoj | 38K | 4.6 | Light | GitHub 浅色增强版 |
五、高级定制技巧
5.1 创建完整自定义主题
# ===== 快速创建主题模板 =====
$ monkeycode theme init my-awesome-theme
✅ Created theme directory: ./my-awesome-theme/
📄 Created theme.json (manifest)
🎨 Created colors.json (color tokens)
🖋️ Created fonts.json (font config)
📐 Created spacing.json (spacing system)
🖼️ Created screenshot.png placeholder
📝 Created README.md template
📦 Created package.json for publishing
# 编辑主题文件后,预览效果
$ monkeycode theme preview ./my-awesome-theme/
# 验证并打包
$ monkeycode theme validate ./my-awesome-theme/
$ monkeycode theme build ./my-awesome-theme/
# 发布到市场
$ monkeycode theme publish ./my-awesome-theme/ \
--token YOUR_API_TOKEN \
--category dark \
--tags "minimal professional blue"
5.2 高级 CSS 覆盖技巧
/* ===== 自定义主题的高级 CSS 覆盖 ===== */
/* 1. 自定义滚动条样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--mc-color-background-secondary);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--mc-color-border-default);
border-radius: 4px;
&:hover {
background: var(--mc-color-foreground-muted);
}
}
/* 2. AI 补全结果的自定义动画 */
@keyframes completion-fade-in {
from {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.mc-completion-item {
animation: completion-fade-in 0.15s ease-out;
&:hover {
background: var(--mc-color-background-elevated);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
}
/* 3. 代码审查标记的特殊样式 */
.mc-review-error {
background: linear-gradient(
90deg,
transparent 0%,
var(--mc-color-status-error) 0%,
var(--mc-color-status-error) 3%,
transparent 3%
);
background-size: 200% 100%;
animation: review-highlight 2s ease infinite;
}
@keyframes review-highlight {
0%, 100% { background-position: 100% 0; }
50% { background-position: -100% 0; }
}
/* 4. 对话气泡渐变背景 */
.mc-chat-bubble-assistant {
background: linear-gradient(
135deg,
var(--mc-color-background-tertiary) 0%,
var(--mc-color-background-secondary) 100%
);
border-left: 3px solid var(--mc-color-primary);
}
/* 5. 状态栏毛玻璃效果 */
.mc-status-bar {
backdrop-filter: blur(12px);
background: rgba(
var(--mc-color-background-primary-rgb),
0.85
);
border-top: 1px solid var(--mc-color-border-subtle);
}
六、性能优化
6.1 主题加载性能
| 优化策略 | 效果 | 实现方式 |
|---|---|---|
| CSS 变量动态切换 | 主题切换 < 16ms | 仅修改 CSS 变量值,无需重载 |
| 按需加载组件主题 | 初始加载减少 40% | 懒加载非活跃面板的主题资源 |
| 主题缓存 | 重复打开零延迟 | IndexedDB 缓存已解析的主题对象 |
| 增量更新 | 修改即时生效 | 只更新变化的 CSS 变量 |
| GPU 加速动画 | 动画不掉帧 | transform/opacity 触发 GPU 合成 |
6.2 扩展性能保障
// ===== packages/extensions/src/sandbox.ts =====
/**
* 扩展沙箱 — 性能和安全保障
*/
export class ExtensionSandbox {
/**
* 资源限制
*/
private limits = {
maxMemoryMB: 256, // 最大内存
maxCPUUsage: 30, // 最大 CPU 占用百分比
maxNetworkRequestsPerMin: 60, // 每分钟最大网络请求数
maxFileOperationsPerSec: 10, // 每秒最大文件操作数
startupTimeoutMs: 5000, // 启动超时
activationTimeoutMs: 3000, // 激活超时
};
/**
* 监控扩展性能
*/
monitor(extensionId: string): PerformanceMonitor {
return {
onMemoryWarning: (usage) => {
console.warn(`⚠️ Extension ${extensionId} memory usage high: ${usage}MB`);
if (usage > this.limits.maxMemoryMB * 0.9) {
this.throttle(extensionId, 'memory');
}
},
onCPUSpike: (usage) => {
if (usage > this.limits.maxCPUUsage) {
console.warn(`⚠️ Extension ${extensionId} CPU spike: ${usage}%`);
}
},
onSlowOperation: (operation, duration) => {
if (duration > 1000) {
console.warn(`⏱️ Slow operation in ${extensionId}: ${operation} took ${duration}ms`);
}
},
};
}
/**
* 对超限扩展进行节流
*/
private throttle(extensionId: string, reason: string): void {
// 降低该扩展的事件处理优先级
// 限制其 API 调用频率
// 必要时暂停扩展
}
}
结语
"最好的工具是那些能被你塑造的工具——而不是强迫你适应它的工具。"
MonkeyCode 的主题与扩展系统体现了我们对开发者个性化体验的深度尊重:
- 🎨 8 款精心设计的内置主题 — 开箱即用的视觉享受
- 🔧 完整的主题编辑器 — 所见即所得的定制体验
- 🛒 繁荣的主题市场 — 50+ 社区主题一键安装
- 🔌 强大的扩展 API — 无限的功能扩展可能
- ⚡ 性能优化的沙箱机制 — 扩展运行安全高效
无论你是追求极简美学的艺术家型开发者,还是注重效率的工具控,或是需要统一团队规范的团队管理者,MonkeykeyCode 的主题与扩展系统都能满足你的需求。
💬 参与方式
- 🎨 分享你的主题?→ 发布到主题市场
- 🔌 开发扩展?→ 扩展开发文档
- 💡 有功能建议?→ 提交 Feature Request
- 🐛 发现问题?→ 提交 Issue
MonkeyCode — 你的工具,你的风格,你的规则。 🐵🎨✨
浙公网安备 33010602011771号