xinyu-pro 0.22.4 → 0.22.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.
- package/app/api/chat/route.ts +6 -8
- package/app/api/generate-svg/route.ts +5 -7
- package/app/api/generate-theme/route.ts +5 -7
- package/app/api/plugins/scan/route.ts +6 -21
- package/app/api/suggest-fields/route.ts +5 -7
- package/app/editor/page.tsx +6 -3
- package/app/extensions/edit/[id]/page.tsx +2412 -2342
- package/app/extensions/page.tsx +1576 -1572
- package/app/page.tsx +2 -2
- package/app/settings/page.tsx +1 -1
- package/bin/cli.js +226 -211
- package/components/ui/ThemeSwitcher.tsx +39 -37
- package/lib/db.ts +6 -9
- package/lib/get-ai-config.ts +33 -0
- package/lib/plugin-files.ts +186 -186
- package/package.json +2 -3
- package/version.json +7 -6
- package/.env.example +0 -17
- package/plugins/xinyu.auto-start-and-choices.xye +0 -0
- package/plugins/xinyu.bag-system.xye +0 -0
- package/plugins/xinyu.cache-optimizer.xye +0 -0
- package/plugins/xinyu.character-studio.xye +0 -0
- package/plugins/xinyu.dice-arbiter.xye +0 -0
- package/plugins/xinyu.game-auto-start-choices.xye +0 -0
- package/plugins/xinyu.markdown-render.xye +0 -0
- package/plugins/xinyu.slot-ui-beautify.xye +0 -0
- package/plugins/xinyu.world-info.xye +0 -0
package/app/page.tsx
CHANGED
|
@@ -86,7 +86,7 @@ export default function HomePage() {
|
|
|
86
86
|
}, []);
|
|
87
87
|
const handleTemplateClick = (templateId: string) => { router.push(`/editor?mode=edit&templateId=${templateId}`); };
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
/* 快速开始游戏(从主界面直接开始,携带模板配置的插件) */
|
|
90
90
|
const handleQuickStart = useCallback(async (tpl: WorldCardTemplate, forceStart = false) => {
|
|
91
91
|
const pluginIds = tpl.plugins || [];
|
|
92
92
|
const installedIds = new Set(availablePlugins.map(p => p.id));
|
|
@@ -142,7 +142,7 @@ export default function HomePage() {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
navigate(`/game/${gameId}`);
|
|
146
146
|
} catch (err) {
|
|
147
147
|
console.error('快速开始游戏失败:', err);
|
|
148
148
|
}
|
package/app/settings/page.tsx
CHANGED
|
@@ -703,7 +703,7 @@ export default function SettingsPage() {
|
|
|
703
703
|
</SettingCard>
|
|
704
704
|
|
|
705
705
|
<SettingCard title="连接配置">
|
|
706
|
-
<SettingRow label="API Key" description="
|
|
706
|
+
<SettingRow label="API Key" description="保存后存储在服务端数据库中,游戏会话自动读取">
|
|
707
707
|
<div className="relative">
|
|
708
708
|
<input
|
|
709
709
|
type={showKey ? 'text' : 'password'}
|
package/bin/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
3
|
+
const {existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync} = require('fs');
|
|
4
|
+
const {join, resolve} = require('path');
|
|
5
|
+
const {createInterface} = require('readline');
|
|
6
|
+
const {execSync, spawn} = require('child_process');
|
|
7
|
+
const {homedir} = require('os');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
10
10
|
const isWin = process.platform === 'win32';
|
|
@@ -13,272 +13,287 @@ const XINYU_HOME = join(homedir(), '.xinyu-pro');
|
|
|
13
13
|
const RUN_DIR = XINYU_HOME;
|
|
14
14
|
|
|
15
15
|
const args = process.argv.slice(2);
|
|
16
|
-
const VERSION_FLAG = args.includes('--version');
|
|
16
|
+
const VERSION_FLAG = args.includes('--version') || args.includes('-v');
|
|
17
17
|
const HELP_FLAG = args.includes('--help') || args.includes('-h');
|
|
18
18
|
const DEV_FLAG = args.includes('--dev');
|
|
19
19
|
const RESET_FLAG = args.includes('--reset');
|
|
20
|
+
const UNINSTALL_FLAG = args.includes('--uninstall') || args.includes('-unins');
|
|
20
21
|
const PORT_INDEX = args.indexOf('--port');
|
|
21
22
|
const CUSTOM_PORT = PORT_INDEX !== -1 ? args[PORT_INDEX + 1] : '3000';
|
|
22
23
|
|
|
23
24
|
const SKIP_DIRS = new Set(['node_modules', '.next', 'data', '.git']);
|
|
24
25
|
|
|
25
26
|
function echo(msg, type = 'info') {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
const colors = {
|
|
28
|
+
info: '\x1b[36m',
|
|
29
|
+
success: '\x1b[32m',
|
|
30
|
+
warn: '\x1b[33m',
|
|
31
|
+
error: '\x1b[31m',
|
|
32
|
+
dim: '\x1b[2m',
|
|
33
|
+
reset: '\x1b[0m',
|
|
34
|
+
bold: '\x1b[1m',
|
|
35
|
+
};
|
|
36
|
+
const color = colors[type] || colors.info;
|
|
37
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
function ask(question) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
const rl = createInterface({input: process.stdin, output: process.stdout});
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
rl.question(question, (answer) => {
|
|
44
|
+
rl.close();
|
|
45
|
+
resolve(answer.trim());
|
|
46
|
+
});
|
|
45
47
|
});
|
|
46
|
-
});
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
function showVersion() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
const versionPath = path.join(APP_DIR, 'version.json');
|
|
52
|
+
const versionData = JSON.parse(readFileSync(versionPath, 'utf-8'));
|
|
53
|
+
console.log(`${versionData.name} ${versionData.version}`);
|
|
54
|
+
process.exit(0);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
function showHelp() {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
process.exit(0);
|
|
58
|
+
echo(' ╔══════════════════════════════════════╗', 'bold');
|
|
59
|
+
echo(' ║ 星语 Pro (xinyu-pro) ║', 'bold');
|
|
60
|
+
echo(' ║ AI 驱动的互动叙事平台 ║', 'bold');
|
|
61
|
+
echo(' ╚══════════════════════════════════════╝', 'bold');
|
|
62
|
+
echo('');
|
|
63
|
+
echo(' 用法:', 'bold');
|
|
64
|
+
echo(' $ xinyu 启动服务器');
|
|
65
|
+
echo(' $ xinyu --dev 开发模式(热更新)');
|
|
66
|
+
echo(' $ xinyu --port 4000 指定端口');
|
|
67
|
+
echo(' $ xinyu --reset 重新安装并启动');
|
|
68
|
+
echo(' $ xinyu --help 显示帮助');
|
|
69
|
+
echo('');
|
|
70
|
+
echo(' 环境要求:', 'bold');
|
|
71
|
+
echo(' • Node.js >= 18');
|
|
72
|
+
echo('');
|
|
73
|
+
process.exit(0);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function checkNodeVersion() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
const [major] = process.versions.node.split('.').map(Number);
|
|
78
|
+
if (major < 18) {
|
|
79
|
+
echo(` ✗ Node.js 版本过低: ${process.versions.node},需要 >= 18`, 'error');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function copyRecursive(src, dest) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
mkdirSync(dest, {recursive: true});
|
|
86
|
+
const entries = readdirSync(src, {withFileTypes: true});
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
89
|
+
if (entry.name.startsWith('.env') && entry.name !== '.env.example') continue;
|
|
90
|
+
const srcPath = join(src, entry.name);
|
|
91
|
+
const destPath = join(dest, entry.name);
|
|
92
|
+
if (entry.isDirectory()) {
|
|
93
|
+
copyRecursive(srcPath, destPath);
|
|
94
|
+
} else {
|
|
95
|
+
if (!existsSync(destPath)) {
|
|
96
|
+
try {
|
|
97
|
+
mkdirSync(join(dest, '..'), {recursive: true});
|
|
98
|
+
writeFileSync(destPath, readFileSync(srcPath));
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
101
103
|
}
|
|
102
|
-
}
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
function ensureAppSetup() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
const isFresh = !existsSync(join(RUN_DIR, 'package.json'));
|
|
108
|
+
|
|
109
|
+
if (isFresh || RESET_FLAG) {
|
|
110
|
+
if (RESET_FLAG && existsSync(RUN_DIR)) {
|
|
111
|
+
echo(' ■ 正在重置应用...', 'info');
|
|
112
|
+
try {
|
|
113
|
+
const rmDir = join(RUN_DIR, '..', '.xinyu-pro-backup');
|
|
114
|
+
if (existsSync(rmDir)) {
|
|
115
|
+
execSync(isWin ? `rmdir /s /q "${rmDir}"` : `rm -rf "${rmDir}"`, {shell: isWin});
|
|
116
|
+
}
|
|
117
|
+
execSync(isWin ? `move "${RUN_DIR}" "${rmDir}"` : `mv "${RUN_DIR}" "${rmDir}"`, {shell: isWin});
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
115
120
|
}
|
|
116
|
-
execSync(isWin ? `move "${RUN_DIR}" "${rmDir}"` : `mv "${RUN_DIR}" "${rmDir}"`, { shell: isWin });
|
|
117
|
-
} catch { }
|
|
118
|
-
}
|
|
119
121
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
echo(' ■ 正在初始化应用目录...', 'info');
|
|
123
|
+
echo(' ' + RUN_DIR, 'dim');
|
|
124
|
+
mkdirSync(RUN_DIR, {recursive: true});
|
|
125
|
+
copyRecursive(APP_DIR, RUN_DIR);
|
|
126
|
+
|
|
127
|
+
const envTarget = join(RUN_DIR, '.env.local');
|
|
128
|
+
if (!existsSync(envTarget)) {
|
|
129
|
+
const envExample = join(RUN_DIR, '.env.example');
|
|
130
|
+
if (existsSync(envExample)) {
|
|
131
|
+
writeFileSync(envTarget, readFileSync(envExample, 'utf-8'), 'utf-8');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
131
135
|
}
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
136
|
|
|
135
|
-
|
|
137
|
+
return false;
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
function installDependencies() {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
if (!existsSync(join(RUN_DIR, 'node_modules'))) {
|
|
142
|
+
echo(' ■ 正在安装依赖...', 'info');
|
|
143
|
+
execSync('npm install', {cwd: RUN_DIR, stdio: 'inherit', shell: isWin});
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
function runBuild() {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
echo(' ■ 正在构建应用(首次需要 1-2 分钟)...', 'info');
|
|
151
|
+
try {
|
|
152
|
+
execSync('npx next build', {cwd: RUN_DIR, stdio: 'inherit', shell: isWin});
|
|
153
|
+
echo(' ✓ 构建完成', 'success');
|
|
154
|
+
} catch {
|
|
155
|
+
echo(' ✗ 构建失败,请检查错误信息', 'error');
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
function getNextBin() {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
const binDir = join(RUN_DIR, 'node_modules', '.bin');
|
|
162
|
+
if (isWin) {
|
|
163
|
+
const cmdPath = join(binDir, 'next.cmd');
|
|
164
|
+
if (existsSync(cmdPath)) return cmdPath;
|
|
165
|
+
}
|
|
166
|
+
const unixPath = join(binDir, 'next');
|
|
167
|
+
if (existsSync(unixPath)) return unixPath;
|
|
168
|
+
return 'next';
|
|
167
169
|
}
|
|
168
170
|
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
171
|
+
function startServer(port, devMode) {
|
|
172
|
+
const nextBin = getNextBin();
|
|
173
|
+
const action = devMode ? 'dev' : 'start';
|
|
174
|
+
|
|
175
|
+
echo(` ╔══════════════════════════════════════╗`, 'success');
|
|
176
|
+
echo(` ║ 星语 Pro 服务器已启动 ║`, 'success');
|
|
177
|
+
echo(` ║ 访问地址: http://localhost:${port} ║`, 'success');
|
|
178
|
+
echo(` ║ 工作目录: ${RUN_DIR}`, 'dim');
|
|
179
|
+
echo(` ║ 按 Ctrl+C 停止 ║`, 'success');
|
|
180
|
+
echo(` ╚══════════════════════════════════════╝`, 'success');
|
|
181
|
+
echo('');
|
|
172
182
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
183
|
+
const child = spawn(nextBin, [action, '-p', port], {
|
|
184
|
+
cwd: RUN_DIR,
|
|
185
|
+
stdio: 'inherit',
|
|
186
|
+
shell: isWin,
|
|
187
|
+
env: {...process.env, PORT: port},
|
|
188
|
+
});
|
|
180
189
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
190
|
+
child.on('error', (err) => {
|
|
191
|
+
echo(` ✗ 启动失败: ${err.message}`, 'error');
|
|
192
|
+
process.exit(1);
|
|
193
|
+
});
|
|
185
194
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
195
|
+
child.on('exit', (code) => {
|
|
196
|
+
if (code !== 0 && code !== null) {
|
|
197
|
+
echo(` ✗ 进程异常退出 (code: ${code})`, 'error');
|
|
198
|
+
}
|
|
199
|
+
process.exit(code || 0);
|
|
200
|
+
});
|
|
189
201
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return run();
|
|
202
|
+
process.on('SIGINT', () => {
|
|
203
|
+
echo('');
|
|
204
|
+
echo(' ■ 正在关闭...', 'info');
|
|
205
|
+
child.kill('SIGINT');
|
|
206
|
+
process.exit(0);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
process.on('SIGTERM', () => {
|
|
210
|
+
child.kill('SIGTERM');
|
|
211
|
+
process.exit(0);
|
|
212
|
+
});
|
|
202
213
|
}
|
|
203
214
|
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
-
const action = devMode ? 'dev' : 'start';
|
|
207
|
-
|
|
208
|
-
echo('');
|
|
209
|
-
echo(` ╔══════════════════════════════════════╗`, 'success');
|
|
210
|
-
echo(` ║ 星语 Pro 服务器已启动 ║`, 'success');
|
|
211
|
-
echo(` ║ 访问地址: http://localhost:${port} ║`, 'success');
|
|
212
|
-
echo(` ║ 工作目录: ${RUN_DIR}`, 'dim');
|
|
213
|
-
echo(` ║ 按 Ctrl+C 停止 ║`, 'success');
|
|
214
|
-
echo(` ╚══════════════════════════════════════╝`, 'success');
|
|
215
|
-
echo('');
|
|
216
|
-
|
|
217
|
-
const child = spawn(nextBin, [action, '-p', port], {
|
|
218
|
-
cwd: RUN_DIR,
|
|
219
|
-
stdio: 'inherit',
|
|
220
|
-
shell: isWin,
|
|
221
|
-
env: { ...process.env, PORT: port },
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
child.on('error', (err) => {
|
|
225
|
-
echo(` ✗ 启动失败: ${err.message}`, 'error');
|
|
226
|
-
process.exit(1);
|
|
227
|
-
});
|
|
215
|
+
async function handleUninstall() {
|
|
216
|
+
echo(' ■ 正在卸载星语 Pro...', 'warn');
|
|
228
217
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
218
|
+
// 确认卸载
|
|
219
|
+
const confirm = await ask(' 确认删除 ~/.xinyu-pro 目录吗?(y/N): ');
|
|
220
|
+
if (confirm.toLowerCase() !== 'y') {
|
|
221
|
+
echo(' 已取消卸载', 'info');
|
|
222
|
+
process.exit(0);
|
|
232
223
|
}
|
|
233
|
-
process.exit(code || 0);
|
|
234
|
-
});
|
|
235
224
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
225
|
+
echo(' ■ 正在删除应用目录...', 'info');
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
if (existsSync(RUN_DIR)) {
|
|
229
|
+
// 删除主目录
|
|
230
|
+
if (isWin) {
|
|
231
|
+
execSync(`rmdir /s /q "${RUN_DIR}"`, {shell: isWin});
|
|
232
|
+
} else {
|
|
233
|
+
execSync(`rm -rf "${RUN_DIR}"`, {shell: isWin});
|
|
234
|
+
}
|
|
235
|
+
echo(` ✓ 已删除 ${RUN_DIR}`, 'success');
|
|
236
|
+
} else {
|
|
237
|
+
echo(' 未找到安装目录', 'warn');
|
|
238
|
+
}
|
|
242
239
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
240
|
+
// 删除备份目录(如果存在)
|
|
241
|
+
const backupDir = join(RUN_DIR, '..', '.xinyu-pro-backup');
|
|
242
|
+
if (existsSync(backupDir)) {
|
|
243
|
+
if (isWin) {
|
|
244
|
+
execSync(`rmdir /s /q "${backupDir}"`, {shell: isWin});
|
|
245
|
+
} else {
|
|
246
|
+
execSync(`rm -rf "${backupDir}"`, {shell: isWin});
|
|
247
|
+
}
|
|
248
|
+
echo(' ✓ 已删除备份目录', 'success');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
echo(' ✓ 卸载完成!', 'success');
|
|
252
|
+
echo(' 感谢使用星语 Pro,期待再次相见 👋', 'info');
|
|
253
|
+
process.exit(0);
|
|
254
|
+
} catch (err) {
|
|
255
|
+
echo(` ✗ 卸载失败: ${err.message}`, 'error');
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
247
258
|
}
|
|
248
259
|
|
|
249
260
|
async function main() {
|
|
250
|
-
echo('');
|
|
251
|
-
echo(' ╔══════════════════════════════════════╗', 'bold');
|
|
252
|
-
echo(' ║ 星语 Pro (xinyu-pro) ║', 'bold');
|
|
253
|
-
echo(' ╚══════════════════════════════════════╝', 'bold');
|
|
254
|
-
echo('');
|
|
255
|
-
|
|
256
|
-
if (VERSION_FLAG) {
|
|
257
|
-
showVersion();
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (HELP_FLAG) {
|
|
262
|
-
showHelp();
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
checkNodeVersion();
|
|
267
|
-
ensureAppSetup();
|
|
268
|
-
installDependencies();
|
|
269
|
-
await setupEnvInteractive();
|
|
270
|
-
|
|
271
|
-
if (!DEV_FLAG && !existsSync(join(RUN_DIR, '.next', 'BUILD_ID'))) {
|
|
272
|
-
echo(' ⚠ 未检测到构建产物,自动构建...', 'warn');
|
|
273
|
-
echo(' (首次构建需要下载依赖并编译,请耐心等待)', 'dim');
|
|
274
261
|
echo('');
|
|
275
|
-
|
|
276
|
-
|
|
262
|
+
echo(' ╔══════════════════════════════════════╗', 'bold');
|
|
263
|
+
echo(' ║ 星语 Pro (xinyu-pro) ║', 'bold');
|
|
264
|
+
echo(' ╚══════════════════════════════════════╝', 'bold');
|
|
265
|
+
echo('');
|
|
266
|
+
|
|
267
|
+
if (VERSION_FLAG) {
|
|
268
|
+
showVersion();
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
277
271
|
|
|
278
|
-
|
|
272
|
+
if (HELP_FLAG) {
|
|
273
|
+
showHelp();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (UNINSTALL_FLAG) {
|
|
278
|
+
await handleUninstall();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
checkNodeVersion();
|
|
283
|
+
ensureAppSetup();
|
|
284
|
+
installDependencies();
|
|
285
|
+
|
|
286
|
+
if (!DEV_FLAG && !existsSync(join(RUN_DIR, '.next', 'BUILD_ID'))) {
|
|
287
|
+
echo(' ⚠ 未检测到构建产物,自动构建...', 'warn');
|
|
288
|
+
echo(' (首次构建需要下载依赖并编译,请耐心等待)', 'dim');
|
|
289
|
+
echo('');
|
|
290
|
+
runBuild();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
startServer(CUSTOM_PORT, DEV_FLAG);
|
|
279
294
|
}
|
|
280
295
|
|
|
281
296
|
main().catch((e) => {
|
|
282
|
-
|
|
283
|
-
|
|
297
|
+
echo(` ✗ 启动失败: ${e.message}`, 'error');
|
|
298
|
+
process.exit(1);
|
|
284
299
|
});
|
|
@@ -69,43 +69,45 @@ export function ThemeSwitcher({ buttonStyle = {} }: { buttonStyle?: React.CSSPro
|
|
|
69
69
|
更多
|
|
70
70
|
</button>
|
|
71
71
|
</div>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<div
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
72
|
+
<div className="flex flex-col gap-2 max-h-[300px] overflow-auto">
|
|
73
|
+
{([...config.presets, ...config.importedThemes]).map((theme) => (
|
|
74
|
+
<button
|
|
75
|
+
key={theme.id}
|
|
76
|
+
onClick={() => {
|
|
77
|
+
setActiveThemeId(theme.id);
|
|
78
|
+
}}
|
|
79
|
+
className="w-full flex items-center gap-3 px-3 py-2 rounded-md text-left transition-colors"
|
|
80
|
+
style={{
|
|
81
|
+
backgroundColor:
|
|
82
|
+
activeTheme.id === theme.id ? 'var(--color-bg-tertiary)' : 'transparent',
|
|
83
|
+
color: 'var(--color-text-primary)',
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
{/* 颜色预览 */}
|
|
87
|
+
<div className="flex gap-1">
|
|
88
|
+
<span
|
|
89
|
+
className="w-4 h-4 rounded-full border"
|
|
90
|
+
style={{
|
|
91
|
+
backgroundColor: theme.variables['--color-bg-primary'],
|
|
92
|
+
borderColor: theme.variables['--color-border'],
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
95
|
+
<span
|
|
96
|
+
className="w-4 h-4 rounded-full"
|
|
97
|
+
style={{ backgroundColor: theme.variables['--color-accent'] }}
|
|
98
|
+
/>
|
|
99
|
+
</div>
|
|
100
|
+
<div>
|
|
101
|
+
<div className="text-sm font-medium">{theme.name}</div>
|
|
102
|
+
</div>
|
|
103
|
+
{activeTheme.id === theme.id && (
|
|
104
|
+
<svg className="ml-auto" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
105
|
+
<path d="M20 6L9 17l-5-5" />
|
|
106
|
+
</svg>
|
|
107
|
+
)}
|
|
108
|
+
</button>
|
|
109
|
+
))}
|
|
110
|
+
</div>
|
|
109
111
|
|
|
110
112
|
{/* {config.customTheme && (
|
|
111
113
|
<>
|