sparkdesign 0.4.5 → 0.4.6

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 (136) hide show
  1. package/README.md +74 -22
  2. package/cli/dist/commands/add.js +22 -12
  3. package/cli/dist/commands/diff.js +8 -4
  4. package/cli/dist/commands/init.js +84 -9
  5. package/cli/dist/commands/list.js +8 -4
  6. package/cli/dist/index.js +9 -6
  7. package/cli/dist/utils/config.js +16 -8
  8. package/cli/dist/utils/package-manager.js +75 -0
  9. package/cli/dist/utils/registry.js +8 -4
  10. package/cli/dist/utils/tokens.js +33 -25
  11. package/cli/dist/utils/transform.js +9 -5
  12. package/cli/dist/utils/tsconfig.js +182 -0
  13. package/cli/registry/AGENTS.md +18 -0
  14. package/cli/registry/__tests__/chat/thinking-indicator.test.tsx +2 -2
  15. package/cli/registry/chat/chat-input/chat-input-folder-selector.tsx +1 -1
  16. package/cli/registry/chat/chat-input/folder-permission-dialog.tsx +2 -2
  17. package/cli/registry/chat/image-generating.tsx +1 -1
  18. package/cli/registry/chat/response/context.tsx +1 -1
  19. package/cli/registry/chat/thinking-indicator.tsx +1 -1
  20. package/cli/registry/tokens/index.css +8 -5
  21. package/cli/registry/tokens/theme-base.css +235 -0
  22. package/{dist/tokens/themes/dark-qoder.css → cli/registry/tokens/themes/dark-mint.css} +1 -1
  23. package/cli/registry/tokens/themes/dark-parchment.css +104 -103
  24. package/{dist/tokens/themes/light-qoder.css → cli/registry/tokens/themes/light-mint.css} +1 -1
  25. package/cli/registry/tokens/themes/light-parchment.css +103 -102
  26. package/dist/registry/basic/alert-dialog.d.ts +7 -5
  27. package/dist/registry/basic/avatar.d.ts +7 -5
  28. package/dist/registry/basic/collapse.d.ts +7 -5
  29. package/dist/registry/basic/collapsible-card.d.ts +7 -6
  30. package/dist/registry/basic/collapsible.d.ts +7 -5
  31. package/dist/registry/basic/dropdown-menu.d.ts +7 -5
  32. package/dist/registry/basic/icons-inline.d.ts +7 -5
  33. package/dist/registry/basic/kbd.d.ts +7 -5
  34. package/dist/registry/basic/pagination.d.ts +7 -5
  35. package/dist/registry/basic/progress.d.ts +7 -5
  36. package/dist/registry/basic/radio-group.d.ts +7 -5
  37. package/dist/registry/basic/resizable.d.ts +7 -5
  38. package/dist/registry/basic/select.d.ts +7 -5
  39. package/dist/registry/basic/slider.d.ts +7 -5
  40. package/dist/registry/basic/sonner.d.ts +7 -5
  41. package/dist/registry/basic/switch.d.ts +7 -5
  42. package/dist/registry/basic/tabs.d.ts +7 -5
  43. package/dist/registry/basic/tag.d.ts +7 -5
  44. package/dist/registry/basic/theme-from-document.d.ts +7 -5
  45. package/dist/registry/basic/tooltip.d.ts +7 -5
  46. package/dist/registry/basic/typography.d.ts +7 -5
  47. package/dist/registry/chat/ask-user-part.d.ts +9 -3
  48. package/dist/registry/chat/browser-action-part.d.ts +9 -3
  49. package/dist/registry/chat/chat-input/compound.d.ts +7 -5
  50. package/dist/registry/chat/chat-input/context.d.ts +7 -5
  51. package/dist/registry/chat/chat-input/index.d.ts +7 -5
  52. package/dist/registry/chat/chat-input/types.d.ts +9 -3
  53. package/dist/registry/chat/chat-input/useAutoResizeTextarea.d.ts +9 -3
  54. package/dist/registry/chat/code-block-part.d.ts +9 -3
  55. package/dist/registry/chat/file-attachment.d.ts +7 -5
  56. package/dist/registry/chat/file-review-part.d.ts +9 -4
  57. package/dist/registry/chat/generated-images-grid.d.ts +9 -3
  58. package/dist/registry/chat/generation-status-bar.d.ts +8 -4
  59. package/dist/registry/chat/hint-banner.d.ts +9 -3
  60. package/dist/registry/chat/mermaid-part.d.ts +9 -3
  61. package/dist/registry/chat/plan-part.d.ts +9 -3
  62. package/dist/registry/chat/reasoning-step/index.d.ts +8 -6
  63. package/dist/registry/chat/reasoning-step/types.d.ts +9 -3
  64. package/dist/registry/chat/related-prompts.d.ts +9 -3
  65. package/dist/registry/chat/response/index.d.ts +8 -6
  66. package/dist/registry/chat/response/types.d.ts +9 -3
  67. package/dist/registry/chat/task-part.d.ts +9 -3
  68. package/dist/registry/chat/terminal-code-block-part.d.ts +9 -3
  69. package/dist/registry/chat/user-question/UserQuestionCard.d.ts +9 -3
  70. package/dist/registry/chat/user-question/UserQuestionFooter.d.ts +9 -3
  71. package/dist/registry/chat/user-question/UserQuestionHeader.d.ts +9 -3
  72. package/dist/registry/chat/user-question/types.d.ts +8 -5
  73. package/dist/registry/lib/file-icon-maps.d.ts +7 -7
  74. package/dist/registry/lib/utils.d.ts +8 -6
  75. package/dist/spark-design.cjs.js +6 -6
  76. package/dist/spark-design.es.js +10 -10
  77. package/dist/sparkdesign.css +2 -0
  78. package/dist/src/components/basic/AlertDialog/index.d.ts +7 -5
  79. package/dist/src/components/basic/Avatar/index.d.ts +9 -3
  80. package/dist/src/components/basic/Button/index.d.ts +9 -3
  81. package/dist/src/components/basic/Collapse/index.d.ts +7 -4
  82. package/dist/src/components/basic/Collapsible/index.d.ts +9 -4
  83. package/dist/src/components/basic/CollapsibleCard/index.d.ts +9 -3
  84. package/dist/src/components/basic/CollapsibleSection/index.d.ts +7 -7
  85. package/dist/src/components/basic/DropdownMenu/index.d.ts +7 -5
  86. package/dist/src/components/basic/EllipsisText/index.d.ts +7 -15
  87. package/dist/src/components/basic/IconButton/index.d.ts +9 -3
  88. package/dist/src/components/basic/Kbd/index.d.ts +9 -3
  89. package/dist/src/components/basic/OptionList/index.d.ts +9 -3
  90. package/dist/src/components/basic/Pagination/index.d.ts +9 -3
  91. package/dist/src/components/basic/Progress/index.d.ts +9 -3
  92. package/dist/src/components/basic/RadioGroup/index.d.ts +9 -3
  93. package/dist/src/components/basic/Resizable/index.d.ts +9 -3
  94. package/dist/src/components/basic/Scrollbar/index.d.ts +9 -3
  95. package/dist/src/components/basic/Select/index.d.ts +7 -4
  96. package/dist/src/components/basic/ShimmeringText/index.d.ts +9 -3
  97. package/dist/src/components/basic/Skeleton/index.d.ts +9 -3
  98. package/dist/src/components/basic/Slider/index.d.ts +9 -3
  99. package/dist/src/components/basic/Spinner/index.d.ts +9 -3
  100. package/dist/src/components/basic/Switch/index.d.ts +8 -5
  101. package/dist/src/components/basic/Table/index.d.ts +9 -3
  102. package/dist/src/components/basic/Tabs/index.d.ts +9 -3
  103. package/dist/src/components/basic/Tag/index.d.ts +7 -4
  104. package/dist/src/components/basic/Toggle/index.d.ts +9 -3
  105. package/dist/src/components/basic/Tooltip/index.d.ts +7 -4
  106. package/dist/src/components/basic/Typography/index.d.ts +9 -3
  107. package/dist/src/components/chat/GeneratedImagesGrid/index.d.ts +9 -3
  108. package/dist/src/components/chat/GenerationStatusBar/index.d.ts +9 -3
  109. package/dist/src/components/chat/Markdown/demo-content.d.ts +1 -1
  110. package/dist/src/components/chat/Markdown/index.d.ts +9 -3
  111. package/dist/src/components/chat/Response/StreamingMarkdownBlock.d.ts +9 -3
  112. package/dist/src/components/chat/Response/index.d.ts +8 -6
  113. package/dist/src/components/chat/UserMessage/index.d.ts +9 -3
  114. package/dist/src/components/index.d.ts +7 -9
  115. package/dist/src/icons/context.d.ts +7 -6
  116. package/dist/src/icons/types.d.ts +7 -5
  117. package/dist/src/lib/ThemeStyleContext.d.ts +9 -9
  118. package/dist/src/lib/file-icon.d.ts +7 -6
  119. package/dist/src/lib/i18n.d.ts +7 -6
  120. package/dist/src/lib/index.d.ts +9 -3
  121. package/dist/src/lib/utils.d.ts +7 -5
  122. package/dist/theme-base.css +7 -8
  123. package/dist/theme.css +2 -2
  124. package/dist/themes/{dark-qoder.css → dark-mint.css} +1 -1
  125. package/dist/themes/{light-qoder.css → light-mint.css} +1 -1
  126. package/dist/tokens/AGENTS.md +47 -0
  127. package/dist/tokens/index.css +10 -19
  128. package/dist/tokens/theme-base.css +7 -8
  129. package/dist/tokens/theme.css +2 -2
  130. package/dist/tokens/themes/dark-mint.css +133 -0
  131. package/dist/tokens/themes/light-mint.css +132 -0
  132. package/package.json +11 -5
  133. package/cli/registry/tokens/themes/dark-qoder.css +0 -132
  134. package/cli/registry/tokens/themes/light-qoder.css +0 -131
  135. package/dist/qoder-design.css +0 -2
  136. package/dist/tokens/CLAUDE.md +0 -305
@@ -1,11 +1,13 @@
1
1
  /**
2
- * [INPUT]: cwd, registryRoot
3
- * [OUTPUT]: 见各函数
4
- * [POS]: cli/src/utils/tokens.ts
2
+ * [WHO]: Public exports from this file (see implementation below).
3
+ * [FROM]: See the import block immediately after this header.
4
+ * [TO]: sparkdesign package consumers; output of CLI `add` when applicable.
5
+ * [HERE]: cli/src/utils/tokens.ts — Spark Design source; keep aligned with the main library.
5
6
  *
6
- * [PROTOCOL]: 文件逻辑变更时同步更新此 Header
7
- * - init:ensureDesignTokens 注入到用户入口 CSS(globals/index.css)
8
- * - add:不改入口;writeQoderTokensFile 生成独立 qoder-tokens.css,由用户自行 import
7
+ * [PROTOCOL]:
8
+ * 1. Keep this P3 header in sync when the public contract changes.
9
+ * 2. Update module AGENTS.md (P2) and root AGENTS.md (P1) when boundaries change.
10
+ * 3. Follow design tokens and explicit type exports.
9
11
  */
10
12
  import path from 'node:path';
11
13
  import fs from 'fs-extra';
@@ -45,7 +47,8 @@ async function readScaleContents(registryRoot) {
45
47
  }
46
48
  return '';
47
49
  }
48
- /** 项目里是否已有 token(入口 CSS 内联了变量、或已 import qoder-tokens.css) */
50
+ const PRIMARY_TOKENS_FILE = 'sparkdesign-tokens.css';
51
+ /** 项目里是否已有 token(入口 CSS 内联了变量、或已 import tokens 文件) */
49
52
  export async function projectHasTokens(cwd) {
50
53
  const candidates = [
51
54
  path.join(cwd, 'src', 'app', 'globals.css'),
@@ -58,7 +61,7 @@ export async function projectHasTokens(cwd) {
58
61
  const content = await fs.readFile(p, 'utf-8');
59
62
  if (content.includes('--color-primary:') ||
60
63
  content.includes(TOKEN_MARKER) ||
61
- content.includes('qoder-tokens.css')) {
64
+ content.includes(PRIMARY_TOKENS_FILE)) {
62
65
  return true;
63
66
  }
64
67
  }
@@ -73,6 +76,7 @@ export async function ensureDesignTokens(cwd, registryRoot) {
73
76
  const themePath = path.join(registryRoot, 'tokens', 'theme.css');
74
77
  const scalePath = path.join(registryRoot, 'tokens', 'scale.css');
75
78
  const scrollbarPath = path.join(registryRoot, 'tokens', 'scrollbar-utility.css');
79
+ const themeBasePath = path.join(registryRoot, 'tokens', 'theme-base.css');
76
80
  const themesDir = path.join(registryRoot, 'tokens', 'themes');
77
81
  // 检查是否有可用的 tokens 文件
78
82
  const hasNewStructure = await fs.pathExists(indexPath);
@@ -95,18 +99,20 @@ export async function ensureDesignTokens(cwd, registryRoot) {
95
99
  if (hasNewStructure && (await fs.pathExists(themesDir))) {
96
100
  // 新结构:读取拆分的主题文件并内联
97
101
  const scaleContent = await readScaleContents(registryRoot);
98
- const lightContent = await readFileIfExists(path.join(themesDir, 'light-qoder.css'));
99
- const darkContent = await readFileIfExists(path.join(themesDir, 'dark-qoder.css'));
102
+ const lightContent = await readFileIfExists(path.join(themesDir, 'light-mint.css'));
103
+ const darkContent = await readFileIfExists(path.join(themesDir, 'dark-mint.css'));
100
104
  const lightParchmentContent = await readFileIfExists(path.join(themesDir, 'light-parchment.css'));
101
105
  const darkParchmentContent = await readFileIfExists(path.join(themesDir, 'dark-parchment.css'));
102
106
  const scrollbarContent = await readFileIfExists(scrollbarPath);
107
+ const themeBaseContent = await readFileIfExists(themeBasePath);
103
108
  tokensBlock = `/* ${TOKEN_MARKER} - 由 npx sparkdesign init 写入,驱动组件样式 */\n` +
104
109
  `${scaleContent}\n\n` +
105
110
  `${lightContent}\n\n` +
106
111
  `${darkContent}\n\n` +
107
112
  `${lightParchmentContent}\n\n` +
108
113
  `${darkParchmentContent}\n` +
109
- (scrollbarContent ? `\n${scrollbarContent}\n` : '');
114
+ (scrollbarContent ? `\n${scrollbarContent}\n` : '') +
115
+ (themeBaseContent ? `\n${themeBaseContent}\n` : '');
110
116
  }
111
117
  else {
112
118
  // 兼容旧结构
@@ -124,39 +130,41 @@ export async function ensureDesignTokens(cwd, registryRoot) {
124
130
  await fs.writeFile(cssEntry, newContent, 'utf-8');
125
131
  return cssEntry;
126
132
  }
127
- const DEDICATED_TOKENS_FILE = 'qoder-tokens.css';
128
133
  /** 仅 add 使用:在 src 下生成独立 token 文件,不修改用户入口。若已存在则不覆盖。返回路径及是否本次创建。 */
129
- export async function writeQoderTokensFile(cwd, registryRoot) {
134
+ export async function writeSparkTokensFile(cwd, registryRoot) {
130
135
  const indexPath = path.join(registryRoot, 'tokens', 'index.css');
131
136
  const themePath = path.join(registryRoot, 'tokens', 'theme.css');
132
137
  const scalePath = path.join(registryRoot, 'tokens', 'scale.css');
133
138
  const scrollbarPath = path.join(registryRoot, 'tokens', 'scrollbar-utility.css');
139
+ const themeBasePath = path.join(registryRoot, 'tokens', 'theme-base.css');
134
140
  const themesDir = path.join(registryRoot, 'tokens', 'themes');
135
- const targetPath = path.join(cwd, 'src', DEDICATED_TOKENS_FILE);
136
- if (await fs.pathExists(targetPath)) {
137
- return { path: targetPath, created: false };
141
+ const primaryPath = path.join(cwd, 'src', PRIMARY_TOKENS_FILE);
142
+ if (await fs.pathExists(primaryPath)) {
143
+ return { path: primaryPath, created: false };
138
144
  }
139
145
  const hasNewStructure = await fs.pathExists(indexPath);
140
146
  const hasLegacyStructure = (await fs.pathExists(themePath)) && (await fs.pathExists(scalePath));
141
147
  if (!hasNewStructure && !hasLegacyStructure) {
142
- return { path: targetPath, created: false };
148
+ return { path: primaryPath, created: false };
143
149
  }
144
150
  let content;
145
151
  if (hasNewStructure && (await fs.pathExists(themesDir))) {
146
152
  // 新结构:读取拆分的主题文件并内联
147
153
  const scaleContent = await readScaleContents(registryRoot);
148
- const lightContent = await readFileIfExists(path.join(themesDir, 'light-qoder.css'));
149
- const darkContent = await readFileIfExists(path.join(themesDir, 'dark-qoder.css'));
154
+ const lightContent = await readFileIfExists(path.join(themesDir, 'light-mint.css'));
155
+ const darkContent = await readFileIfExists(path.join(themesDir, 'dark-mint.css'));
150
156
  const lightParchmentContent = await readFileIfExists(path.join(themesDir, 'light-parchment.css'));
151
157
  const darkParchmentContent = await readFileIfExists(path.join(themesDir, 'dark-parchment.css'));
152
158
  const scrollbarContent = await readFileIfExists(scrollbarPath);
159
+ const themeBaseContent = await readFileIfExists(themeBasePath);
153
160
  content = `/* ${TOKEN_MARKER} - 在应用入口 import 此文件以驱动组件样式 */\n` +
154
161
  `${scaleContent}\n\n` +
155
162
  `${lightContent}\n\n` +
156
163
  `${darkContent}\n\n` +
157
164
  `${lightParchmentContent}\n\n` +
158
165
  `${darkParchmentContent}\n` +
159
- (scrollbarContent ? `\n${scrollbarContent}\n` : '');
166
+ (scrollbarContent ? `\n${scrollbarContent}\n` : '') +
167
+ (themeBaseContent ? `\n${themeBaseContent}\n` : '');
160
168
  }
161
169
  else {
162
170
  // 兼容旧结构
@@ -167,10 +175,10 @@ export async function writeQoderTokensFile(cwd, registryRoot) {
167
175
  `/* ${TOKEN_MARKER} - 在应用入口 import 此文件以驱动组件样式 */\n${scaleContent}\n\n${themeContent}\n` +
168
176
  (scrollbarContent ? `\n${scrollbarContent}\n` : '');
169
177
  }
170
- await fs.ensureDir(path.dirname(targetPath));
171
- await fs.writeFile(targetPath, content, 'utf-8');
172
- return { path: targetPath, created: true };
178
+ await fs.ensureDir(path.dirname(primaryPath));
179
+ await fs.writeFile(primaryPath, content, 'utf-8');
180
+ return { path: primaryPath, created: true };
173
181
  }
174
- export function getQoderTokensImportPath() {
175
- return `./${DEDICATED_TOKENS_FILE}`;
182
+ export function getSparkTokensImportPath() {
183
+ return `./${PRIMARY_TOKENS_FILE}`;
176
184
  }
@@ -1,11 +1,15 @@
1
1
  /**
2
- * [INPUT]: 模板源码 + 用户 aliases
3
- * [OUTPUT]: 替换别名后的源码字符串
4
- * [POS]: cli/src/utils/transform.ts
2
+ * [WHO]: Public exports from this file (see implementation below).
3
+ * [FROM]: See the import block immediately after this header.
4
+ * [TO]: sparkdesign package consumers; output of CLI `add` when applicable.
5
+ * [HERE]: cli/src/utils/transform.ts — Spark Design source; keep aligned with the main library.
5
6
  *
6
- * [PROTOCOL]: 文件逻辑变更时同步更新此 Header
7
+ * [PROTOCOL]:
8
+ * 1. Keep this P3 header in sync when the public contract changes.
9
+ * 2. Update module AGENTS.md (P2) and root AGENTS.md (P1) when boundaries change.
10
+ * 3. Follow design tokens and explicit type exports.
7
11
  */
8
- /** 移除文件开头的 L3 协议块注释,使按需引入到用户项目的代码不包含内部文档。 */
12
+ /** 移除文件开头的 P3(DIP)协议块注释,使按需引入到用户项目的代码不包含内部导航头。 */
9
13
  export function stripL3Header(source) {
10
14
  return source.replace(/^\s*\/\*\*[\s\S]*?\*\/\s*/, '').trimStart();
11
15
  }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * [WHO]: Public exports from this file (see implementation below).
3
+ * [FROM]: See the import block immediately after this header.
4
+ * [TO]: sparkdesign package consumers; output of CLI `add` when applicable.
5
+ * [HERE]: cli/src/utils/tsconfig.ts — Spark Design source; keep aligned with the main library.
6
+ *
7
+ * [PROTOCOL]:
8
+ * 1. Keep this P3 header in sync when the public contract changes.
9
+ * 2. Update module AGENTS.md (P2) and root AGENTS.md (P1) when boundaries change.
10
+ * 3. Follow design tokens and explicit type exports.
11
+ */
12
+ import path from 'node:path';
13
+ import fs from 'fs-extra';
14
+ import chalk from 'chalk';
15
+ export function stripJsonComments(raw) {
16
+ return raw.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '').replace(/,(\s*[}\]])/g, '$1');
17
+ }
18
+ /** 检查 / 修复 tsconfig 的 @/* 路径别名,返回是否已存在(含本次写入) */
19
+ async function ensureTsPaths(cwd) {
20
+ for (const name of ['tsconfig.json', 'tsconfig.app.json']) {
21
+ const configPath = path.join(cwd, name);
22
+ if (!(await fs.pathExists(configPath)))
23
+ continue;
24
+ const raw = await fs.readFile(configPath, 'utf-8');
25
+ let config = null;
26
+ try {
27
+ config = JSON.parse(raw);
28
+ }
29
+ catch {
30
+ try {
31
+ config = JSON.parse(stripJsonComments(raw));
32
+ }
33
+ catch {
34
+ continue;
35
+ }
36
+ }
37
+ const co = config?.compilerOptions;
38
+ if (co?.paths && co.paths['@/*'])
39
+ return true;
40
+ if (!co || !config)
41
+ continue;
42
+ // 无论原文件是否含注释,都写回有效 JSON(注释丢失可接受,alias 能用更重要)
43
+ const paths = (co.paths ?? {});
44
+ paths['@/*'] = ['./src/*'];
45
+ co.paths = paths;
46
+ if (!co.baseUrl)
47
+ co.baseUrl = '.';
48
+ await fs.writeJson(configPath, config, { spaces: 2 });
49
+ console.log(chalk.green('✓'), `Added @/* → ./src/* to ${name}`);
50
+ return true;
51
+ }
52
+ console.log(chalk.yellow('⚠'), '未找到 tsconfig.json。Spark 组件使用 @/ 导入别名,请确保配置:');
53
+ console.log(chalk.cyan(' "compilerOptions": { "paths": { "@/*": ["./src/*"] } }'));
54
+ return false;
55
+ }
56
+ async function detectBundler(cwd) {
57
+ const pkgPath = path.join(cwd, 'package.json');
58
+ if (!(await fs.pathExists(pkgPath)))
59
+ return 'unknown';
60
+ const pkg = await fs.readJson(pkgPath);
61
+ const allDeps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
62
+ if (allDeps['next'])
63
+ return 'next';
64
+ if (allDeps['vite'] || allDeps['@vitejs/plugin-react'] || allDeps['@vitejs/plugin-react-swc'])
65
+ return 'vite';
66
+ return 'unknown';
67
+ }
68
+ /** Vite 配置文件路径(按优先级) */
69
+ const VITE_CONFIGS = ['vite.config.ts', 'vite.config.js', 'vite.config.mts', 'vite.config.mjs'];
70
+ /** 检查 Vite resolve.alias 是否已包含 @ 别名 */
71
+ async function viteHasAtAlias(cwd) {
72
+ for (const name of VITE_CONFIGS) {
73
+ const configPath = path.join(cwd, name);
74
+ if (!(await fs.pathExists(configPath)))
75
+ continue;
76
+ const content = await fs.readFile(configPath, 'utf-8');
77
+ // 常见写法检测:
78
+ // 1) resolve: { alias: { '@': ... } }
79
+ // 2) resolve: { alias: [{ find: '@', ... }] }
80
+ // 3) '@': path.resolve(...) 或 "@": ...
81
+ const hasAlias = /['"]@['"]/.test(content) && /alias/.test(content);
82
+ return { found: hasAlias, configFile: name };
83
+ }
84
+ return { found: false, configFile: null };
85
+ }
86
+ /** 尝试自动向 Vite 配置追加 resolve.alias(仅限简单场景) */
87
+ async function tryPatchViteAlias(cwd, configFile) {
88
+ const configPath = path.join(cwd, configFile);
89
+ const content = await fs.readFile(configPath, 'utf-8');
90
+ // 检查是否已 import path
91
+ const hasPathImport = /import\s+path\s+from\s+['"](?:node:)?path['"]/.test(content) ||
92
+ /import\s*\{\s*resolve\s*\}\s*from\s*['"](?:node:)?path['"]/.test(content) ||
93
+ /const\s+path\s*=\s*require/.test(content) ||
94
+ /import\s+\*\s+as\s+path\s+from/.test(content);
95
+ const pathRef = hasPathImport ? 'path.resolve(__dirname, "./src")' : 'fileURLToPath(new URL("./src", import.meta.url))';
96
+ const pathImportLine = hasPathImport ? '' : "import { fileURLToPath, URL } from 'node:url'\n";
97
+ // 场景 1:已有 resolve: { alias: { ... } } 对象形式,追加 '@' 键
98
+ const aliasObjMatch = content.match(/(resolve\s*:\s*\{[^}]*alias\s*:\s*\{)([^}]*)\}/);
99
+ if (aliasObjMatch) {
100
+ const existingAliases = aliasObjMatch[2];
101
+ if (existingAliases.includes("'@'") || existingAliases.includes('"@"'))
102
+ return true; // 已有
103
+ const separator = existingAliases.trim().endsWith(',') || existingAliases.trim() === '' ? '' : ',';
104
+ let patched = content.replace(aliasObjMatch[0], `${aliasObjMatch[1]}${existingAliases}${separator}\n '@': ${pathRef},\n }`);
105
+ if (pathImportLine && !hasPathImport)
106
+ patched = pathImportLine + patched;
107
+ await fs.writeFile(configPath, patched, 'utf-8');
108
+ console.log(chalk.green('✓'), `Added resolve.alias['@'] → src/ to ${configFile}`);
109
+ return true;
110
+ }
111
+ // 场景 2:没有 resolve.alias 块 —— 在 defineConfig({...}) 或 export default {...} 内注入
112
+ const resolveBlock = ` resolve: {\n alias: {\n '@': ${pathRef},\n },\n },`;
113
+ // 2a: defineConfig({ ... })
114
+ const defineMatch = content.match(/(defineConfig\s*\(\s*\{)/);
115
+ if (defineMatch) {
116
+ let patched = content.replace(defineMatch[0], `${defineMatch[0]}\n${resolveBlock}`);
117
+ if (pathImportLine && !hasPathImport)
118
+ patched = pathImportLine + patched;
119
+ await fs.writeFile(configPath, patched, 'utf-8');
120
+ console.log(chalk.green('✓'), `Added resolve.alias['@'] → src/ to ${configFile}`);
121
+ return true;
122
+ }
123
+ // 2b: export default { ... }
124
+ const exportMatch = content.match(/(export\s+default\s*\{)/);
125
+ if (exportMatch) {
126
+ let patched = content.replace(exportMatch[0], `${exportMatch[0]}\n${resolveBlock}`);
127
+ if (pathImportLine && !hasPathImport)
128
+ patched = pathImportLine + patched;
129
+ await fs.writeFile(configPath, patched, 'utf-8');
130
+ console.log(chalk.green('✓'), `Added resolve.alias['@'] → src/ to ${configFile}`);
131
+ return true;
132
+ }
133
+ return false;
134
+ }
135
+ /** 输出 Vite alias 缺失的修复提示 */
136
+ function printViteAliasHint(configFile) {
137
+ const file = configFile ?? 'vite.config.ts';
138
+ console.log(chalk.yellow('⚠'), `${file} 缺少 @/ 运行时别名,组件导入将在启动时报错。请添加:`);
139
+ console.log(chalk.cyan(`
140
+ // ${file}
141
+ import path from 'node:path'
142
+
143
+ export default defineConfig({
144
+ resolve: {
145
+ alias: {
146
+ '@': path.resolve(__dirname, './src'),
147
+ },
148
+ },
149
+ // ...其余配置
150
+ })
151
+ `));
152
+ }
153
+ /**
154
+ * 一站式 alias 检查:TS 层 + bundler 运行时层。
155
+ * 在 init / add 中调用。
156
+ */
157
+ export async function ensureTsAlias(cwd) {
158
+ // 1. TS 层
159
+ await ensureTsPaths(cwd);
160
+ // 2. Bundler 运行时层
161
+ const bundler = await detectBundler(cwd);
162
+ if (bundler === 'next') {
163
+ // Next.js 自动读取 tsconfig.json paths,无需额外 alias 配置
164
+ return;
165
+ }
166
+ if (bundler === 'vite') {
167
+ const { found, configFile } = await viteHasAtAlias(cwd);
168
+ if (found)
169
+ return;
170
+ // 没有找到 Vite alias —— 尝试自动 patch
171
+ if (configFile) {
172
+ const patched = await tryPatchViteAlias(cwd, configFile);
173
+ if (patched)
174
+ return;
175
+ }
176
+ // 自动 patch 失败或没找到配置文件 —— 输出手动提示
177
+ printViteAliasHint(configFile);
178
+ return;
179
+ }
180
+ // 未知 bundler —— 给出通用提示
181
+ console.log(chalk.gray(' 提示: 如果使用 webpack/Rspack 等打包器,请确保 resolve.alias 中配置了 @ → src/'));
182
+ }
@@ -0,0 +1,18 @@
1
+ # registry/
2
+
3
+ > **P2** | Parent: [Root AGENTS.md](../AGENTS.md)
4
+
5
+ ## Member list
6
+
7
+ > Complete this section with **one line per file** (and major folders): `basic/`, `chat/`, `lib/`, `tokens/`, `meta.json`, `__tests__/`, etc. The filesystem is the source of truth.
8
+
9
+ ## Submodules
10
+
11
+ - `basic/`, `chat/` — component source files consumed by the library and CLI
12
+ - `lib/` — shared helpers bundled with registry copies
13
+ - `tokens/` — CSS token packs synced to `cli/registry`
14
+ - `__tests__/` — component tests
15
+
16
+ ---
17
+
18
+ **Covenant:** update this file whenever you add/remove registry files or change global publishing behavior.
@@ -21,9 +21,9 @@ describe('ThinkingIndicator', () => {
21
21
  // 文案测试
22
22
  // ============================================================
23
23
  describe('text 文案', () => {
24
- it('应显示默认文案 "Qoder is Thinking..."', () => {
24
+ it('应显示默认文案 "Spark is Thinking..."', () => {
25
25
  render(<ThinkingIndicator />)
26
- expect(screen.getByText('Qoder is Thinking...')).toBeInTheDocument()
26
+ expect(screen.getByText('Spark is Thinking...')).toBeInTheDocument()
27
27
  })
28
28
 
29
29
  it('应显示自定义文案', () => {
@@ -13,7 +13,7 @@ import { FolderPermissionDialog } from './folder-permission-dialog'
13
13
  import { FolderLine, FolderFillLine, TimeLine } from '../../basic/icons-inline'
14
14
  import type { FolderPermissionConfig, InputPropsWithWebkitDirectory } from './types'
15
15
 
16
- const MOCK_HISTORY_FOLDERS = ['~/Projects/qoder-work-ui', '~/Documents/workspace', '~/Desktop/demo', '~/code/my-app']
16
+ const MOCK_HISTORY_FOLDERS = ['~/Projects/spark-design-app', '~/Documents/workspace', '~/Desktop/demo', '~/code/my-app']
17
17
 
18
18
  const iconClass = 'mr-2 w-4 h-4 shrink-0'
19
19
 
@@ -28,10 +28,10 @@ export function FolderPermissionDialog({
28
28
  <AlertDialogContent className="p-6">
29
29
  <AlertDialogHeader>
30
30
  <AlertDialogTitle className="text-xl leading-xl font-semibold text-text">
31
- Allow Qoder Work to access and modify files in &quot;{projectName}&quot;?
31
+ Allow Spark Design to access and modify files in &quot;{projectName}&quot;?
32
32
  </AlertDialogTitle>
33
33
  <AlertDialogDescription className="mt-2 text-sm leading-sm text-text-tertiary">
34
- This permission applies to all files and subfolders in this directory. Qoder Work may read, update, and
34
+ This permission applies to all files and subfolders in this directory. Spark Design may read, update, and
35
35
  permanently delete files here, and may share relevant file contents with connected tools when needed.
36
36
  Only allow access to folders you trust.
37
37
  </AlertDialogDescription>
@@ -24,7 +24,7 @@ export function ImageGenerating({
24
24
  width = 'var(--spacing-80)',
25
25
  height = 'var(--spacing-60)',
26
26
  speed = 1.0,
27
- caption = 'Qoder is drawing...',
27
+ caption = 'Spark is drawing...',
28
28
  captionClassName,
29
29
  className,
30
30
  style: styleProp,
@@ -111,7 +111,7 @@ export function useResponseContext() {
111
111
 
112
112
  // ===== Default ThinkingIndicator =====
113
113
 
114
- const DEFAULT_THINK = <ThinkingIndicator text="Qoder is thinking..." />
114
+ const DEFAULT_THINK = <ThinkingIndicator text="Spark is thinking..." />
115
115
 
116
116
  // ===== Provider =====
117
117
 
@@ -37,7 +37,7 @@ function DefaultLoader() {
37
37
  }
38
38
 
39
39
  export function ThinkingIndicator({
40
- text = 'Qoder is Thinking...',
40
+ text = 'Spark is Thinking...',
41
41
  textClassName,
42
42
  shimmer = true,
43
43
  shimmerDuration = 2,
@@ -2,11 +2,11 @@
2
2
  * Token 系统统一入口 (CLI 分发版)
3
3
  *
4
4
  * 引入方式:
5
- * 1. 全量引入(默认): @import "qoder-tokens.css";
5
+ * 1. 全量引入(默认): @import "sparkdesign-tokens.css";
6
6
  * 2. 按需引入:
7
7
  * @import "./scale/index.css"; // 布局风格
8
- * @import "./themes/light-qoder.css"; // 亮色主题
9
- * @import "./themes/dark-qoder.css"; // 暗色主题
8
+ * @import "./themes/light-mint.css"; // 亮色主题
9
+ * @import "./themes/dark-mint.css"; // 暗色主题
10
10
  *
11
11
  * 自定义主题:
12
12
  * @import "./scale/index.css";
@@ -22,10 +22,13 @@
22
22
  @import "./scale.css";
23
23
 
24
24
  /* 内置主题 (data-theme) */
25
- @import "./themes/light-qoder.css";
26
- @import "./themes/dark-qoder.css";
25
+ @import "./themes/light-mint.css";
26
+ @import "./themes/dark-mint.css";
27
27
  @import "./themes/light-parchment.css";
28
28
  @import "./themes/dark-parchment.css";
29
29
 
30
30
  /* 滚动条工具样式 */
31
31
  @import "./scrollbar-utility.css";
32
+
33
+ /* Tailwind v4 @theme 映射(--token-color-* → --color-*,驱动 bg-primary 等工具类) */
34
+ @import "./theme-base.css";