@yivan-lab/pretty-please 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +390 -1
  2. package/bin/pls.tsx +1255 -123
  3. package/dist/bin/pls.js +1098 -103
  4. package/dist/package.json +4 -4
  5. package/dist/src/alias.d.ts +41 -0
  6. package/dist/src/alias.js +240 -0
  7. package/dist/src/chat-history.js +10 -1
  8. package/dist/src/components/Chat.js +54 -26
  9. package/dist/src/components/CodeColorizer.js +26 -20
  10. package/dist/src/components/CommandBox.js +19 -8
  11. package/dist/src/components/ConfirmationPrompt.js +2 -1
  12. package/dist/src/components/Duration.js +2 -1
  13. package/dist/src/components/InlineRenderer.js +2 -1
  14. package/dist/src/components/MarkdownDisplay.js +2 -1
  15. package/dist/src/components/MultiStepCommandGenerator.d.ts +3 -1
  16. package/dist/src/components/MultiStepCommandGenerator.js +20 -10
  17. package/dist/src/components/TableRenderer.js +2 -1
  18. package/dist/src/config.d.ts +33 -3
  19. package/dist/src/config.js +83 -34
  20. package/dist/src/mastra-agent.d.ts +1 -0
  21. package/dist/src/mastra-agent.js +3 -11
  22. package/dist/src/mastra-chat.d.ts +13 -6
  23. package/dist/src/mastra-chat.js +31 -31
  24. package/dist/src/multi-step.d.ts +23 -7
  25. package/dist/src/multi-step.js +45 -26
  26. package/dist/src/prompts.d.ts +30 -4
  27. package/dist/src/prompts.js +218 -70
  28. package/dist/src/remote-history.d.ts +63 -0
  29. package/dist/src/remote-history.js +315 -0
  30. package/dist/src/remote.d.ts +113 -0
  31. package/dist/src/remote.js +634 -0
  32. package/dist/src/shell-hook.d.ts +58 -0
  33. package/dist/src/shell-hook.js +295 -26
  34. package/dist/src/ui/theme.d.ts +60 -23
  35. package/dist/src/ui/theme.js +544 -22
  36. package/dist/src/upgrade.d.ts +41 -0
  37. package/dist/src/upgrade.js +348 -0
  38. package/dist/src/utils/console.d.ts +4 -0
  39. package/dist/src/utils/console.js +89 -17
  40. package/package.json +4 -4
  41. package/src/alias.ts +301 -0
  42. package/src/chat-history.ts +11 -1
  43. package/src/components/Chat.tsx +71 -26
  44. package/src/components/CodeColorizer.tsx +27 -19
  45. package/src/components/CommandBox.tsx +26 -8
  46. package/src/components/ConfirmationPrompt.tsx +2 -1
  47. package/src/components/Duration.tsx +2 -1
  48. package/src/components/InlineRenderer.tsx +2 -1
  49. package/src/components/MarkdownDisplay.tsx +2 -1
  50. package/src/components/MultiStepCommandGenerator.tsx +25 -11
  51. package/src/components/TableRenderer.tsx +2 -1
  52. package/src/config.ts +126 -35
  53. package/src/mastra-agent.ts +3 -12
  54. package/src/mastra-chat.ts +40 -34
  55. package/src/multi-step.ts +62 -30
  56. package/src/prompts.ts +236 -78
  57. package/src/remote-history.ts +390 -0
  58. package/src/remote.ts +800 -0
  59. package/src/shell-hook.ts +339 -26
  60. package/src/ui/theme.ts +632 -23
  61. package/src/upgrade.ts +397 -0
  62. package/src/utils/console.ts +99 -17
package/src/ui/theme.ts CHANGED
@@ -1,29 +1,76 @@
1
- // 主题配置 - 优雅的配色方案
2
- export const theme = {
3
- // 主色调
4
- primary: '#00D9FF', // 青色 - 主要交互元素
5
- secondary: '#A78BFA', // 紫色 - 次要元素
6
- accent: '#F472B6', // 粉色 - 强调元素
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import os from 'os'
7
4
 
8
- // 状态色
9
- success: '#10B981', // 绿色 - 成功
10
- error: '#EF4444', // 红色 - 错误
11
- warning: '#F59E0B', // 橙色 - 警告
12
- info: '#3B82F6', // 蓝色 - 信息
5
+ // 主题目录路径
6
+ const THEMES_DIR = path.join(os.homedir(), '.please', 'themes')
13
7
 
14
- // 文本色
8
+ // 主题类型定义(改为 string 支持动态主题)
9
+ export type ThemeName = string
10
+
11
+ // 内置主题名称
12
+ export type BuiltinThemeName = 'dark' | 'light' | 'nord' | 'dracula' | 'retro' | 'contrast' | 'monokai'
13
+
14
+ // 主题颜色配置
15
+ export interface Theme {
16
+ primary: string
17
+ secondary: string
18
+ accent: string
19
+ success: string
20
+ error: string
21
+ warning: string
22
+ info: string
15
23
  text: {
16
- primary: '#E5E7EB', // 主文本
17
- secondary: '#9CA3AF', // 次要文本
18
- muted: '#6B7280', // 弱化文本
19
- dim: '#4B5563', // 暗淡文本
20
- },
24
+ primary: string
25
+ secondary: string
26
+ muted: string
27
+ dim: string
28
+ }
29
+ border: string
30
+ divider: string
31
+ code: {
32
+ background: string
33
+ text: string
34
+ keyword: string
35
+ string: string
36
+ function: string
37
+ comment: string
38
+ }
39
+ }
40
+
41
+ // 主题元数据
42
+ export interface ThemeMetadata {
43
+ name: ThemeName // 内部标识符
44
+ displayName: string // 显示名称(如 "深色")
45
+ description: string // 描述(如 "默认深色主题,适合深色终端背景")
46
+ category: 'dark' | 'light' // 类别(用于分组)
47
+ previewColor: string // 预览颜色(在列表中显示)
48
+ author?: string // 作者(内置主题为 'built-in',自定义主题显示用户名)
49
+ }
21
50
 
22
- // 边框和分隔
23
- border: '#374151', // 边框色
24
- divider: '#1F2937', // 分隔线
51
+ // 完整的主题定义(颜色 + 元数据)
52
+ export interface ThemeDefinition {
53
+ metadata: ThemeMetadata
54
+ colors: Theme
55
+ }
25
56
 
26
- // 代码相关
57
+ // 深色主题(原默认主题)
58
+ const darkTheme: Theme = {
59
+ primary: '#00D9FF',
60
+ secondary: '#A78BFA',
61
+ accent: '#F472B6',
62
+ success: '#10B981',
63
+ error: '#EF4444',
64
+ warning: '#F59E0B',
65
+ info: '#3B82F6',
66
+ text: {
67
+ primary: '#E5E7EB',
68
+ secondary: '#9CA3AF',
69
+ muted: '#6B7280',
70
+ dim: '#4B5563',
71
+ },
72
+ border: '#374151',
73
+ divider: '#1F2937',
27
74
  code: {
28
75
  background: '#1F2937',
29
76
  text: '#E5E7EB',
@@ -31,7 +78,569 @@ export const theme = {
31
78
  string: '#98C379',
32
79
  function: '#61AFEF',
33
80
  comment: '#5C6370',
81
+ },
82
+ }
83
+
84
+ // 浅色主题(白色/浅色终端背景)
85
+ // 所有颜色都要在白色背景上清晰可见
86
+ const lightTheme: Theme = {
87
+ primary: '#0369A1', // 深天蓝,在白底上醒目
88
+ secondary: '#6D28D9', // 深紫色
89
+ accent: '#BE185D', // 深粉色
90
+ success: '#047857', // 深绿色
91
+ error: '#B91C1C', // 深红色
92
+ warning: '#B45309', // 深橙色
93
+ info: '#1D4ED8', // 深蓝色
94
+ text: {
95
+ primary: '#111827', // 近黑色,主要文字
96
+ secondary: '#374151', // 深灰色
97
+ muted: '#4B5563', // 中灰色
98
+ dim: '#6B7280', // 浅灰色
99
+ },
100
+ border: '#6B7280', // 边框要明显
101
+ divider: '#9CA3AF',
102
+ code: {
103
+ background: '#F3F4F6',
104
+ text: '#111827',
105
+ keyword: '#6D28D9',
106
+ string: '#047857',
107
+ function: '#0369A1',
108
+ comment: '#4B5563',
109
+ },
110
+ }
111
+
112
+ // Nord 主题 - 冷色调护眼,适合长时间使用
113
+ const nordTheme: Theme = {
114
+ primary: '#88C0D0', // Nord8 - 冰蓝色
115
+ secondary: '#B48EAD', // Nord15 - 紫色
116
+ accent: '#EBCB8B', // Nord13 - 黄色
117
+ success: '#A3BE8C', // Nord14 - 绿色
118
+ error: '#BF616A', // Nord11 - 红色
119
+ warning: '#D08770', // Nord12 - 橙色
120
+ info: '#81A1C1', // Nord9 - 蓝色
121
+ text: {
122
+ primary: '#ECEFF4', // Nord6 - 雪白色
123
+ secondary: '#D8DEE9', // Nord4 - 浅灰色
124
+ muted: '#A3BE8C', // Nord14 - 柔和绿
125
+ dim: '#5E81AC', // Nord10 - 暗蓝色
126
+ },
127
+ border: '#4C566A', // Nord3
128
+ divider: '#3B4252', // Nord1
129
+ code: {
130
+ background: '#2E3440', // Nord0 - 深灰背景
131
+ text: '#ECEFF4', // Nord6
132
+ keyword: '#81A1C1', // Nord9 - 蓝色
133
+ string: '#A3BE8C', // Nord14 - 绿色
134
+ function: '#88C0D0', // Nord8 - 青色
135
+ comment: '#616E88', // Nord3 变体
136
+ },
137
+ }
138
+
139
+ // Dracula 主题 - 暗色系流行配色
140
+ const draculaTheme: Theme = {
141
+ primary: '#8BE9FD', // 青色
142
+ secondary: '#BD93F9', // 紫色
143
+ accent: '#FF79C6', // 粉色
144
+ success: '#50FA7B', // 绿色
145
+ error: '#FF5555', // 红色
146
+ warning: '#FFB86C', // 橙色
147
+ info: '#8BE9FD', // 青色
148
+ text: {
149
+ primary: '#F8F8F2', // 几乎白色
150
+ secondary: '#BFBFBF', // 灰色
151
+ muted: '#6272A4', // 暗灰蓝
152
+ dim: '#44475A', // 深灰
153
+ },
154
+ border: '#6272A4', // 评论色
155
+ divider: '#44475A', // 深灰
156
+ code: {
157
+ background: '#282A36', // 背景色
158
+ text: '#F8F8F2', // 前景色
159
+ keyword: '#FF79C6', // 粉色关键字
160
+ string: '#F1FA8C', // 黄色字符串
161
+ function: '#50FA7B', // 绿色函数
162
+ comment: '#6272A4', // 评论色
163
+ },
164
+ }
165
+
166
+ // Retro Green 主题 - 经典终端荧光绿
167
+ const retroTheme: Theme = {
168
+ primary: '#00FF00', // 荧光绿
169
+ secondary: '#00CC00', // 暗绿
170
+ accent: '#00FFAA', // 青绿色
171
+ success: '#00FF00', // 荧光绿
172
+ error: '#FF0000', // 纯红
173
+ warning: '#FFFF00', // 纯黄
174
+ info: '#00FFFF', // 青色
175
+ text: {
176
+ primary: '#00FF00', // 荧光绿
177
+ secondary: '#00DD00', // 稍暗绿
178
+ muted: '#00AA00', // 中绿
179
+ dim: '#008800', // 暗绿
180
+ },
181
+ border: '#00AA00', // 中绿边框
182
+ divider: '#006600', // 暗绿分割线
183
+ code: {
184
+ background: '#000000', // 纯黑背景
185
+ text: '#00FF00', // 荧光绿
186
+ keyword: '#00FFFF', // 青色关键字
187
+ string: '#00FF00', // 绿色字符串
188
+ function: '#00FFAA', // 青绿函数
189
+ comment: '#008800', // 暗绿注释
190
+ },
191
+ }
192
+
193
+ // High Contrast 主题 - 极高对比度,辅助功能
194
+ const contrastTheme: Theme = {
195
+ primary: '#FFFFFF', // 纯白
196
+ secondary: '#AAAAAA', // 灰色
197
+ accent: '#00FFFF', // 青色
198
+ success: '#00FF00', // 纯绿
199
+ error: '#FF0000', // 纯红
200
+ warning: '#FFFF00', // 纯黄
201
+ info: '#00FFFF', // 青色
202
+ text: {
203
+ primary: '#FFFFFF', // 纯白
204
+ secondary: '#CCCCCC', // 浅灰
205
+ muted: '#999999', // 中灰
206
+ dim: '#666666', // 暗灰
207
+ },
208
+ border: '#FFFFFF', // 白色边框
209
+ divider: '#888888', // 灰色分割线
210
+ code: {
211
+ background: '#000000', // 纯黑背景
212
+ text: '#FFFFFF', // 纯白文字
213
+ keyword: '#00FFFF', // 青色关键字
214
+ string: '#00FF00', // 绿色字符串
215
+ function: '#FFFF00', // 黄色函数
216
+ comment: '#888888', // 灰色注释
217
+ },
218
+ }
219
+
220
+ // Monokai 主题 - 经典编辑器配色
221
+ const monokaiTheme: Theme = {
222
+ primary: '#66D9EF', // 青色
223
+ secondary: '#AE81FF', // 紫色
224
+ accent: '#F92672', // 粉红
225
+ success: '#A6E22E', // 绿色
226
+ error: '#F92672', // 粉红
227
+ warning: '#E6DB74', // 黄色
228
+ info: '#66D9EF', // 青色
229
+ text: {
230
+ primary: '#F8F8F2', // 几乎白色
231
+ secondary: '#CFCFC2', // 浅灰
232
+ muted: '#75715E', // 暗灰
233
+ dim: '#49483E', // 深灰
234
+ },
235
+ border: '#75715E', // 注释色
236
+ divider: '#49483E', // 深灰
237
+ code: {
238
+ background: '#272822', // 深灰背景
239
+ text: '#F8F8F2', // 前景色
240
+ keyword: '#F92672', // 粉红关键字
241
+ string: '#E6DB74', // 黄色字符串
242
+ function: '#A6E22E', // 绿色函数
243
+ comment: '#75715E', // 注释色
244
+ },
245
+ }
246
+
247
+ // 所有主题定义(颜色 + 元数据)
248
+ export const themeDefinitions: Record<ThemeName, ThemeDefinition> = {
249
+ dark: {
250
+ metadata: {
251
+ name: 'dark',
252
+ displayName: '深色',
253
+ description: '默认深色主题,明亮的颜色适合深色终端背景',
254
+ category: 'dark',
255
+ previewColor: '#00D9FF',
256
+ author: 'built-in',
257
+ },
258
+ colors: darkTheme,
259
+ },
260
+ light: {
261
+ metadata: {
262
+ name: 'light',
263
+ displayName: '浅色',
264
+ description: '浅色主题,较深的颜色适合浅色终端背景',
265
+ category: 'light',
266
+ previewColor: '#0369A1',
267
+ author: 'built-in',
268
+ },
269
+ colors: lightTheme,
270
+ },
271
+ nord: {
272
+ metadata: {
273
+ name: 'nord',
274
+ displayName: '北欧冷色',
275
+ description: '冷色调护眼主题,适合长时间使用',
276
+ category: 'dark',
277
+ previewColor: '#88C0D0',
278
+ author: 'built-in',
279
+ },
280
+ colors: nordTheme,
281
+ },
282
+ dracula: {
283
+ metadata: {
284
+ name: 'dracula',
285
+ displayName: '德古拉暗色',
286
+ description: '高对比暗色主题,色彩丰富但不刺眼',
287
+ category: 'dark',
288
+ previewColor: '#BD93F9',
289
+ author: 'built-in',
290
+ },
291
+ colors: draculaTheme,
292
+ },
293
+ retro: {
294
+ metadata: {
295
+ name: 'retro',
296
+ displayName: '复古终端绿',
297
+ description: '经典终端荧光绿,致敬老派 hacker 风格',
298
+ category: 'dark',
299
+ previewColor: '#00FF00',
300
+ author: 'built-in',
301
+ },
302
+ colors: retroTheme,
303
+ },
304
+ contrast: {
305
+ metadata: {
306
+ name: 'contrast',
307
+ displayName: '高对比度',
308
+ description: '极高对比度,适合视力辅助和强光环境',
309
+ category: 'dark',
310
+ previewColor: '#FFFFFF',
311
+ author: 'built-in',
312
+ },
313
+ colors: contrastTheme,
314
+ },
315
+ monokai: {
316
+ metadata: {
317
+ name: 'monokai',
318
+ displayName: '经典编辑器',
319
+ description: 'Monokai 经典配色,开发者熟悉的选择',
320
+ category: 'dark',
321
+ previewColor: '#66D9EF',
322
+ author: 'built-in',
323
+ },
324
+ colors: monokaiTheme,
325
+ },
326
+ }
327
+
328
+ // 向后兼容:导出纯颜色主题对象
329
+ export const themes: Record<ThemeName, Theme> = Object.fromEntries(
330
+ Object.entries(themeDefinitions).map(([name, def]) => [name, def.colors])
331
+ ) as Record<ThemeName, Theme>
332
+
333
+ // 获取当前主题(按需加载策略)
334
+ export function getCurrentTheme(): Theme {
335
+ // 直接读取配置文件,避免循环依赖
336
+ try {
337
+ const configPath = path.join(os.homedir(), '.please', 'config.json')
338
+ if (fs.existsSync(configPath)) {
339
+ const content = fs.readFileSync(configPath, 'utf-8')
340
+ const config = JSON.parse(content)
341
+ const themeName = config.theme || 'dark'
342
+
343
+ // 1. 先检查是否为内置主题
344
+ if (themeName in themeDefinitions) {
345
+ return themeDefinitions[themeName].colors
346
+ }
347
+
348
+ // 2. 如果不是内置主题,加载自定义主题
349
+ const customTheme = loadCustomTheme(themeName)
350
+ if (customTheme) {
351
+ return customTheme.colors
352
+ }
353
+
354
+ // 3. 加载失败,回退到默认主题
355
+ console.warn(`⚠ 主题 "${themeName}" 不存在,已回退到 dark 主题`)
356
+ }
357
+ } catch {
358
+ // 忽略错误,返回默认主题
359
+ }
360
+ return themeDefinitions.dark.colors
361
+ }
362
+
363
+ // 获取单个主题的元数据(支持自定义主题)
364
+ export function getThemeMetadata(name: ThemeName): ThemeMetadata | undefined {
365
+ // 先检查内置主题
366
+ if (name in themeDefinitions) {
367
+ return themeDefinitions[name].metadata
368
+ }
369
+
370
+ // 加载自定义主题
371
+ const customTheme = loadCustomTheme(name)
372
+ return customTheme?.metadata || undefined
373
+ }
374
+
375
+ // 获取所有主题的元数据列表(扫描所有主题)
376
+ export function getAllThemeMetadata(): ThemeMetadata[] {
377
+ const customThemes = loadAllCustomThemes()
378
+ const allThemes = { ...themeDefinitions, ...customThemes }
379
+ return Object.values(allThemes).map((def) => def.metadata)
380
+ }
381
+
382
+ // 获取完整的主题定义(颜色 + 元数据)
383
+ export function getThemeDefinition(name: ThemeName): ThemeDefinition | undefined {
384
+ // 先检查内置主题
385
+ if (name in themeDefinitions) {
386
+ return themeDefinitions[name]
387
+ }
388
+
389
+ // 加载自定义主题
390
+ return loadCustomTheme(name) || undefined
391
+ }
392
+
393
+ // 检查主题是否存在(支持自定义主题)
394
+ export function isValidTheme(name: string): name is ThemeName {
395
+ // 先检查内置主题
396
+ if (name in themeDefinitions) {
397
+ return true
398
+ }
399
+
400
+ // 检查自定义主题文件是否存在
401
+ const themePath = path.join(THEMES_DIR, `${name}.json`)
402
+ return fs.existsSync(themePath)
403
+ }
404
+
405
+ // 检查是否为内置主题
406
+ export function isBuiltinTheme(name: string): boolean {
407
+ return name in themeDefinitions
408
+ }
409
+
410
+ // ========== 自定义主题加载逻辑 ==========
411
+
412
+ /**
413
+ * 加载单个自定义主题(按需加载)
414
+ */
415
+ function loadCustomTheme(name: string): ThemeDefinition | null {
416
+ try {
417
+ const themePath = path.join(THEMES_DIR, `${name}.json`)
418
+
419
+ if (!fs.existsSync(themePath)) {
420
+ return null
421
+ }
422
+
423
+ const content = fs.readFileSync(themePath, 'utf-8')
424
+ const theme = JSON.parse(content)
425
+
426
+ // 验证主题格式
427
+ if (!validateTheme(theme)) {
428
+ console.warn(`⚠ 主题 "${name}" 格式不正确,已跳过`)
429
+ return null
430
+ }
431
+
432
+ return theme
433
+ } catch (error: any) {
434
+ console.warn(`⚠ 加载主题 "${name}" 失败: ${error.message}`)
435
+ return null
436
+ }
437
+ }
438
+
439
+ /**
440
+ * 加载所有自定义主题(完整扫描)
441
+ */
442
+ function loadAllCustomThemes(): Record<string, ThemeDefinition> {
443
+ const themes: Record<string, ThemeDefinition> = {}
444
+
445
+ try {
446
+ // 确保主题目录存在
447
+ if (!fs.existsSync(THEMES_DIR)) {
448
+ return themes
449
+ }
450
+
451
+ // 扫描所有 .json 文件
452
+ const files = fs.readdirSync(THEMES_DIR).filter((f) => f.endsWith('.json'))
453
+
454
+ for (const file of files) {
455
+ try {
456
+ const themePath = path.join(THEMES_DIR, file)
457
+ const content = fs.readFileSync(themePath, 'utf-8')
458
+ const theme = JSON.parse(content)
459
+
460
+ // 验证主题格式
461
+ if (validateTheme(theme)) {
462
+ themes[theme.metadata.name] = theme
463
+ }
464
+ } catch (error) {
465
+ // 单个主题加载失败不影响其他主题
466
+ continue
467
+ }
468
+ }
469
+ } catch (error) {
470
+ // 目录读取失败,返回空对象
471
+ }
472
+
473
+ return themes
474
+ }
475
+
476
+ /**
477
+ * 验证主题格式是否正确
478
+ */
479
+ export function validateTheme(theme: any): theme is ThemeDefinition {
480
+ try {
481
+ // 检查基本结构
482
+ if (!theme || typeof theme !== 'object') {
483
+ return false
484
+ }
485
+
486
+ // 检查 metadata 必填字段
487
+ if (
488
+ !theme.metadata ||
489
+ typeof theme.metadata !== 'object' ||
490
+ !theme.metadata.name ||
491
+ !theme.metadata.displayName ||
492
+ !theme.metadata.category
493
+ ) {
494
+ return false
495
+ }
496
+
497
+ // 检查 category 值
498
+ if (theme.metadata.category !== 'dark' && theme.metadata.category !== 'light') {
499
+ return false
500
+ }
501
+
502
+ // 检查 colors 必填字段
503
+ if (
504
+ !theme.colors ||
505
+ typeof theme.colors !== 'object' ||
506
+ !theme.colors.primary ||
507
+ !theme.colors.success ||
508
+ !theme.colors.error ||
509
+ !theme.colors.text ||
510
+ !theme.colors.text.primary
511
+ ) {
512
+ return false
513
+ }
514
+
515
+ // 验证颜色格式(十六进制)
516
+ const hexRegex = /^#[0-9A-Fa-f]{6}$/
517
+ const requiredColors = [
518
+ theme.colors.primary,
519
+ theme.colors.secondary,
520
+ theme.colors.accent,
521
+ theme.colors.success,
522
+ theme.colors.error,
523
+ theme.colors.warning,
524
+ theme.colors.info,
525
+ theme.colors.text.primary,
526
+ theme.colors.border,
527
+ ]
528
+
529
+ for (const color of requiredColors) {
530
+ if (!color || !hexRegex.test(color)) {
531
+ return false
532
+ }
533
+ }
534
+
535
+ return true
536
+ } catch {
537
+ return false
538
+ }
539
+ }
540
+
541
+ /**
542
+ * 获取主题验证的详细错误信息
543
+ */
544
+ export function validateThemeWithDetails(theme: any): { valid: boolean; errors: string[] } {
545
+ const errors: string[] = []
546
+
547
+ // 检查基本结构
548
+ if (!theme || typeof theme !== 'object') {
549
+ errors.push('主题必须是一个 JSON 对象')
550
+ return { valid: false, errors }
551
+ }
552
+
553
+ // 检查 metadata
554
+ if (!theme.metadata || typeof theme.metadata !== 'object') {
555
+ errors.push('缺少 metadata 字段')
556
+ } else {
557
+ if (!theme.metadata.name) errors.push('缺少 metadata.name')
558
+ if (!theme.metadata.displayName) errors.push('缺少 metadata.displayName')
559
+ if (!theme.metadata.category) {
560
+ errors.push('缺少 metadata.category')
561
+ } else if (theme.metadata.category !== 'dark' && theme.metadata.category !== 'light') {
562
+ errors.push('metadata.category 必须是 "dark" 或 "light"')
563
+ }
564
+ }
565
+
566
+ // 检查 colors
567
+ if (!theme.colors || typeof theme.colors !== 'object') {
568
+ errors.push('缺少 colors 字段')
569
+ } else {
570
+ const hexRegex = /^#[0-9A-Fa-f]{6}$/
571
+
572
+ // 检查必填颜色字段
573
+ const colorFields = [
574
+ 'primary',
575
+ 'secondary',
576
+ 'accent',
577
+ 'success',
578
+ 'error',
579
+ 'warning',
580
+ 'info',
581
+ 'border',
582
+ 'divider',
583
+ ]
584
+
585
+ for (const field of colorFields) {
586
+ if (!theme.colors[field]) {
587
+ errors.push(`缺少 colors.${field}`)
588
+ } else if (!hexRegex.test(theme.colors[field])) {
589
+ errors.push(`colors.${field} 格式错误(应为 #RRGGBB 格式)`)
590
+ }
591
+ }
592
+
593
+ // 检查 text 字段
594
+ if (!theme.colors.text || typeof theme.colors.text !== 'object') {
595
+ errors.push('缺少 colors.text')
596
+ } else {
597
+ const textFields = ['primary', 'secondary', 'muted', 'dim']
598
+ for (const field of textFields) {
599
+ if (!theme.colors.text[field]) {
600
+ errors.push(`缺少 colors.text.${field}`)
601
+ } else if (!hexRegex.test(theme.colors.text[field])) {
602
+ errors.push(`colors.text.${field} 格式错误(应为 #RRGGBB 格式)`)
603
+ }
604
+ }
605
+ }
606
+
607
+ // 检查 code 字段
608
+ if (!theme.colors.code || typeof theme.colors.code !== 'object') {
609
+ errors.push('缺少 colors.code')
610
+ } else {
611
+ const codeFields = ['background', 'text', 'keyword', 'string', 'function', 'comment']
612
+ for (const field of codeFields) {
613
+ if (!theme.colors.code[field]) {
614
+ errors.push(`缺少 colors.code.${field}`)
615
+ } else if (!hexRegex.test(theme.colors.code[field])) {
616
+ errors.push(`colors.code.${field} 格式错误(应为 #RRGGBB 格式)`)
617
+ }
618
+ }
619
+ }
620
+ }
621
+
622
+ return { valid: errors.length === 0, errors }
623
+ }
624
+
625
+ /**
626
+ * 创建主题模板
627
+ */
628
+ export function createThemeTemplate(name: string, displayName: string, category: 'dark' | 'light'): ThemeDefinition {
629
+ // 根据类别选择基础配色
630
+ const baseTheme = category === 'dark' ? darkTheme : lightTheme
631
+
632
+ return {
633
+ metadata: {
634
+ name,
635
+ displayName,
636
+ description: '自定义主题',
637
+ category,
638
+ previewColor: baseTheme.primary,
639
+ author: os.userInfo().username || 'user',
640
+ },
641
+ colors: baseTheme,
34
642
  }
35
- } as const
643
+ }
36
644
 
37
- export type Theme = typeof theme
645
+ // 向后兼容:导出默认主题
646
+ export const theme = darkTheme