ai-agent-router 0.2.1 → 0.2.2
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/fallback-build-manifest.json +2 -2
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.js +4 -4
- package/.next/required-server-files.json +4 -4
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/gateway/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/gateway/models/route.js.nft.json +1 -1
- package/.next/server/app/api/gateway/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/apply/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/available-models/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/save/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/status/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/claude/test/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/openclaw/apply/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/openclaw/available-models/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/openclaw/preview/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/openclaw/save/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/openclaw/status/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/opencode/apply/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/opencode/available-models/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/opencode/preview/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/opencode/save/route.js.nft.json +1 -1
- package/.next/server/app/api/ide/opencode/status/route.js.nft.json +1 -1
- package/.next/server/app/api/logs/route.js.nft.json +1 -1
- package/.next/server/app/api/models/route.js.nft.json +1 -1
- package/.next/server/app/api/providers/route.js.nft.json +1 -1
- package/.next/server/app/api/providers/test/route.js.nft.json +1 -1
- package/.next/server/app/api/service/force-stop/route.js.nft.json +1 -1
- package/.next/server/app/api/service/start/route.js.nft.json +1 -1
- package/.next/server/app/api/service/status/route.js.nft.json +1 -1
- package/.next/server/app/api/service/stop/route.js.nft.json +1 -1
- package/.next/server/app/ide/page.js +1 -1
- package/.next/server/app/ide/page.js.nft.json +1 -1
- package/.next/server/app/ide/page_client-reference-manifest.js +1 -1
- package/.next/server/app/ide.html +1 -1
- package/.next/server/app/ide.rsc +3 -3
- package/.next/server/app/ide.segments/_full.segment.rsc +3 -3
- package/.next/server/app/ide.segments/_head.segment.rsc +1 -1
- package/.next/server/app/ide.segments/_index.segment.rsc +2 -2
- package/.next/server/app/ide.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/ide.segments/ide/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/ide.segments/ide.segment.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs/page.js +1 -1
- package/.next/server/app/logs/page.js.nft.json +1 -1
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +1 -1
- package/.next/server/app/logs.rsc +2 -2
- package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- package/.next/server/app/models/page.js +1 -1
- package/.next/server/app/models/page.js.nft.json +1 -1
- package/.next/server/app/models/page_client-reference-manifest.js +1 -1
- package/.next/server/app/models.html +1 -1
- package/.next/server/app/models.rsc +2 -2
- package/.next/server/app/models.segments/_full.segment.rsc +2 -2
- package/.next/server/app/models.segments/_head.segment.rsc +1 -1
- package/.next/server/app/models.segments/_index.segment.rsc +2 -2
- package/.next/server/app/models.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/models.segments/models/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/models.segments/models.segment.rsc +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/providers/page.js +1 -1
- package/.next/server/app/providers/page.js.nft.json +1 -1
- package/.next/server/app/providers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/providers.html +1 -1
- package/.next/server/app/providers.rsc +2 -2
- package/.next/server/app/providers.segments/_full.segment.rsc +2 -2
- package/.next/server/app/providers.segments/_head.segment.rsc +1 -1
- package/.next/server/app/providers.segments/_index.segment.rsc +2 -2
- package/.next/server/app/providers.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/providers.segments/providers.segment.rsc +1 -1
- package/.next/server/chunks/[root-of-the-server]__3a204d25._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__3a204d25._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__4a8f9bc7._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__4a8f9bc7._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__5e8276bc._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__5e8276bc._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js +16 -16
- package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__f0461b8d._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__f0461b8d._.js.map +1 -1
- package/.next/server/chunks/ssr/_69468f4c._.js +3 -0
- package/.next/server/chunks/ssr/_69468f4c._.js.map +1 -0
- package/.next/server/chunks/ssr/src_app_ide_page_tsx_8962793b._.js +1 -1
- package/.next/server/chunks/ssr/src_app_ide_page_tsx_8962793b._.js.map +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/6992685fe009e8fd.css +1 -0
- package/.next/static/chunks/8ccc14e022ff6de3.js +1 -0
- package/README.md +31 -4
- package/dist/package.json +55 -0
- package/dist/src/app/api/config/route.js +17 -0
- package/dist/src/app/api/ide/claude/apply/route.js +84 -0
- package/dist/src/app/api/ide/claude/restore/route.js +30 -0
- package/dist/src/app/api/ide/claude/save/route.js +79 -0
- package/dist/src/app/components/Footer.js +11 -0
- package/dist/src/app/ide/page.js +62 -2
- package/dist/src/app/layout.js +5 -1
- package/dist/src/cli/index.js +0 -0
- package/dist/src/db/database.js +21 -2
- package/package.json +1 -1
- package/src/app/api/config/route.ts +17 -0
- package/src/app/api/ide/claude/apply/route.ts +94 -0
- package/src/app/api/ide/claude/restore/route.ts +36 -1
- package/src/app/api/ide/claude/save/route.ts +91 -1
- package/src/app/components/Footer.tsx +9 -0
- package/src/app/ide/page.tsx +71 -5
- package/src/app/layout.tsx +3 -1
- package/src/db/database.ts +27 -2
- package/.next/server/chunks/ssr/src_app_b2b1d928._.js +0 -3
- package/.next/server/chunks/ssr/src_app_b2b1d928._.js.map +0 -1
- package/.next/static/chunks/0f120c117962200b.css +0 -1
- package/.next/static/chunks/8ed839b2e4948968.js +0 -1
- /package/.next/static/{dYin74gcpdlg8TGoGv-_d → cf2SWIkI5HVbnDjLExI42}/_buildManifest.js +0 -0
- /package/.next/static/{dYin74gcpdlg8TGoGv-_d → cf2SWIkI5HVbnDjLExI42}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{dYin74gcpdlg8TGoGv-_d → cf2SWIkI5HVbnDjLExI42}/_ssgManifest.js +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { existsSync, readFileSync, copyFileSync, unlinkSync } from 'fs';
|
|
2
|
+
import { existsSync, readFileSync, copyFileSync, unlinkSync, mkdirSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
|
|
@@ -8,6 +8,9 @@ const CLAUDE_DIR = join(homedir(), '.claude');
|
|
|
8
8
|
const SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
|
|
9
9
|
const BACKUP_FILE = join(CLAUDE_DIR, 'settings.json.aar.bak');
|
|
10
10
|
|
|
11
|
+
// AAR 备份目录
|
|
12
|
+
const AAR_BACKUP_DIR = join(homedir(), '.aar', 'backups');
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* 还原 Claude IDE 配置
|
|
13
16
|
*/
|
|
@@ -34,6 +37,12 @@ export async function POST(request: NextRequest) {
|
|
|
34
37
|
);
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
// 在还原前,先备份当前配置到 ~/.aar/backups 目录
|
|
41
|
+
const currentBackup = backupToAar(SETTINGS_FILE, 'settings-before-restore');
|
|
42
|
+
if (!currentBackup.success) {
|
|
43
|
+
console.warn('[IDE Restore] Current config backup warning:', currentBackup.error);
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
// 将备份内容复制到原配置文件
|
|
38
47
|
copyFileSync(BACKUP_FILE, SETTINGS_FILE);
|
|
39
48
|
|
|
@@ -93,3 +102,29 @@ export async function DELETE(request: NextRequest) {
|
|
|
93
102
|
);
|
|
94
103
|
}
|
|
95
104
|
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 备份文件到 ~/.aar/backups 目录
|
|
108
|
+
*/
|
|
109
|
+
function backupToAar(filePath: string, backupName: string): { success: boolean; backupPath?: string; error?: string } {
|
|
110
|
+
try {
|
|
111
|
+
if (!existsSync(filePath)) {
|
|
112
|
+
return { success: true }; // 文件不存在,无需备份
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!existsSync(AAR_BACKUP_DIR)) {
|
|
116
|
+
mkdirSync(AAR_BACKUP_DIR, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
120
|
+
const backupPath = join(AAR_BACKUP_DIR, `${backupName}-${timestamp}.json`);
|
|
121
|
+
copyFileSync(filePath, backupPath);
|
|
122
|
+
|
|
123
|
+
return { success: true, backupPath };
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: `Backup to AAR failed: ${error.message}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
import { getDatabase } from '@/db/database';
|
|
@@ -9,10 +9,16 @@ import { getConfig } from '@/db/queries';
|
|
|
9
9
|
const CLAUDE_DIR = join(homedir(), '.claude');
|
|
10
10
|
const SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
|
|
11
11
|
|
|
12
|
+
// Claude 登录配置文件(用于 hasCompletedOnboarding)
|
|
13
|
+
const CLAUDE_JSON_FILE = join(homedir(), '.claude.json');
|
|
14
|
+
|
|
12
15
|
// AAR 临时配置目录和文件
|
|
13
16
|
const AAR_DIR = join(homedir(), '.aar');
|
|
14
17
|
const TEMP_SETTINGS_FILE = join(AAR_DIR, 'settings.tmp.json');
|
|
15
18
|
|
|
19
|
+
// AAR 备份目录
|
|
20
|
+
const AAR_BACKUP_DIR = join(AAR_DIR, 'backups');
|
|
21
|
+
|
|
16
22
|
type SaveRequest = {
|
|
17
23
|
haiku?: string;
|
|
18
24
|
sonnet?: string;
|
|
@@ -64,6 +70,18 @@ export async function POST(request: NextRequest) {
|
|
|
64
70
|
// 已应用当前网关配置,更新 settings.json
|
|
65
71
|
savePath = SETTINGS_FILE;
|
|
66
72
|
saveType = 'applied';
|
|
73
|
+
|
|
74
|
+
// 备份到 ~/.aar/backups 目录
|
|
75
|
+
const settingsBackup = backupToAar(SETTINGS_FILE, 'settings');
|
|
76
|
+
const claudeJsonBackup = backupToAar(CLAUDE_JSON_FILE, 'claude');
|
|
77
|
+
|
|
78
|
+
if (!settingsBackup.success) {
|
|
79
|
+
console.warn('[IDE Save] Settings backup warning:', settingsBackup.error);
|
|
80
|
+
}
|
|
81
|
+
if (!claudeJsonBackup.success) {
|
|
82
|
+
console.warn('[IDE Save] Claude.json backup warning:', claudeJsonBackup.error);
|
|
83
|
+
}
|
|
84
|
+
|
|
67
85
|
mergedConfig = generateClaudeConfig(gatewayAddress, gatewayApiKey, {
|
|
68
86
|
haiku: haiku,
|
|
69
87
|
sonnet: sonnet,
|
|
@@ -71,6 +89,12 @@ export async function POST(request: NextRequest) {
|
|
|
71
89
|
default: defaultModel,
|
|
72
90
|
reasoning: reasoningModel
|
|
73
91
|
});
|
|
92
|
+
|
|
93
|
+
// 确保 ~/.claude.json 包含 hasCompletedOnboarding: true
|
|
94
|
+
const onboardingResult = ensureClaudeOnboarding();
|
|
95
|
+
if (!onboardingResult.success) {
|
|
96
|
+
console.warn('[IDE Save] Onboarding update warning:', onboardingResult.error);
|
|
97
|
+
}
|
|
74
98
|
} else {
|
|
75
99
|
// 未应用网络关配置,保存到临时文件 ~/.aar/settings.tmp.json
|
|
76
100
|
savePath = TEMP_SETTINGS_FILE;
|
|
@@ -179,3 +203,69 @@ function generateClaudeConfig(
|
|
|
179
203
|
},
|
|
180
204
|
};
|
|
181
205
|
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 备份文件到 ~/.aar/backups 目录
|
|
209
|
+
*/
|
|
210
|
+
function backupToAar(filePath: string, backupName: string): { success: boolean; backupPath?: string; error?: string } {
|
|
211
|
+
try {
|
|
212
|
+
if (!existsSync(filePath)) {
|
|
213
|
+
return { success: true }; // 文件不存在,无需备份
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!existsSync(AAR_BACKUP_DIR)) {
|
|
217
|
+
mkdirSync(AAR_BACKUP_DIR, { recursive: true });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
221
|
+
const backupPath = join(AAR_BACKUP_DIR, `${backupName}-${timestamp}.json`);
|
|
222
|
+
copyFileSync(filePath, backupPath);
|
|
223
|
+
|
|
224
|
+
return { success: true, backupPath };
|
|
225
|
+
} catch (error: any) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: `Backup to AAR failed: ${error.message}`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 确保 ~/.claude.json 文件中包含 hasCompletedOnboarding: true
|
|
235
|
+
* 这允许用户跳过 Claude 登录使用自定义模型
|
|
236
|
+
*/
|
|
237
|
+
function ensureClaudeOnboarding(): { success: boolean; created: boolean; error?: string } {
|
|
238
|
+
try {
|
|
239
|
+
let config: any = {};
|
|
240
|
+
let created = false;
|
|
241
|
+
|
|
242
|
+
if (existsSync(CLAUDE_JSON_FILE)) {
|
|
243
|
+
try {
|
|
244
|
+
const content = readFileSync(CLAUDE_JSON_FILE, 'utf-8');
|
|
245
|
+
config = JSON.parse(content);
|
|
246
|
+
} catch {
|
|
247
|
+
// 文件存在但解析失败,使用空对象
|
|
248
|
+
config = {};
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
created = true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 检查是否已经有 hasCompletedOnboarding
|
|
255
|
+
if (config.hasCompletedOnboarding === true) {
|
|
256
|
+
return { success: true, created: false };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 添加 hasCompletedOnboarding
|
|
260
|
+
config.hasCompletedOnboarding = true;
|
|
261
|
+
writeFileSync(CLAUDE_JSON_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
262
|
+
|
|
263
|
+
return { success: true, created };
|
|
264
|
+
} catch (error: any) {
|
|
265
|
+
return {
|
|
266
|
+
success: false,
|
|
267
|
+
created: false,
|
|
268
|
+
error: `Failed to update claude.json: ${error.message}`,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import packageJson from '../../../package.json';
|
|
2
|
+
|
|
3
|
+
export default function Footer() {
|
|
4
|
+
return (
|
|
5
|
+
<footer className="fixed bottom-0 left-0 right-0 py-2 text-center text-xs text-gray-400 bg-gray-50 border-t border-gray-200">
|
|
6
|
+
<span>AAR v{packageJson.version}</span>
|
|
7
|
+
</footer>
|
|
8
|
+
);
|
|
9
|
+
}
|
package/src/app/ide/page.tsx
CHANGED
|
@@ -349,6 +349,72 @@ export default function IDEConfigPage() {
|
|
|
349
349
|
return () => document.removeEventListener('mousedown', onDocClick);
|
|
350
350
|
}, [openClawDropdown]);
|
|
351
351
|
|
|
352
|
+
// 同步 Claude 下拉框默认值:确保状态值在可用模型列表中
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
const allModels = Object.values(models).flat();
|
|
355
|
+
if (allModels.length === 0) return;
|
|
356
|
+
|
|
357
|
+
const modelIds = allModels.map(m => m.model_id);
|
|
358
|
+
const firstModelId = allModels[0].model_id;
|
|
359
|
+
|
|
360
|
+
// 如果当前状态值不在可用模型列表中,更新为第一个可用模型
|
|
361
|
+
if (!modelIds.includes(haikuModel)) {
|
|
362
|
+
setHaikuModel(firstModelId);
|
|
363
|
+
}
|
|
364
|
+
if (!modelIds.includes(sonnetModel)) {
|
|
365
|
+
setSonnetModel(firstModelId);
|
|
366
|
+
}
|
|
367
|
+
if (!modelIds.includes(opusModel)) {
|
|
368
|
+
setOpusModel(firstModelId);
|
|
369
|
+
}
|
|
370
|
+
if (!modelIds.includes(defaultModel)) {
|
|
371
|
+
setDefaultModel(firstModelId);
|
|
372
|
+
}
|
|
373
|
+
if (!modelIds.includes(reasoningModel)) {
|
|
374
|
+
setReasoningModel(firstModelId);
|
|
375
|
+
}
|
|
376
|
+
}, [models]);
|
|
377
|
+
|
|
378
|
+
// 同步 OpenCode 下拉框默认值
|
|
379
|
+
useEffect(() => {
|
|
380
|
+
const allModels = Object.values(opencodeModels).flat();
|
|
381
|
+
if (allModels.length === 0) return;
|
|
382
|
+
|
|
383
|
+
const modelIds = allModels.map(m => m.model_id);
|
|
384
|
+
const firstModelId = allModels[0].model_id;
|
|
385
|
+
|
|
386
|
+
if (!modelIds.includes(opencodeDefaultModel)) {
|
|
387
|
+
setOpencodeDefaultModel(firstModelId);
|
|
388
|
+
}
|
|
389
|
+
}, [opencodeModels]);
|
|
390
|
+
|
|
391
|
+
// 同步 OpenClaw 下拉框默认值
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
const allModels = Object.values(openclawModels).flat();
|
|
394
|
+
if (allModels.length === 0) return;
|
|
395
|
+
|
|
396
|
+
const modelIds = allModels.map(m => m.model_id);
|
|
397
|
+
const firstModelId = allModels[0].model_id;
|
|
398
|
+
|
|
399
|
+
if (!modelIds.includes(openclawDefaultModel)) {
|
|
400
|
+
setOpenclawDefaultModel(firstModelId);
|
|
401
|
+
}
|
|
402
|
+
// 过滤无效的 fallback 模型
|
|
403
|
+
const validFallbacks = openclawDefaultFallbacks.filter(id => modelIds.includes(id));
|
|
404
|
+
if (validFallbacks.length !== openclawDefaultFallbacks.length) {
|
|
405
|
+
setOpenclawDefaultFallbacks(validFallbacks);
|
|
406
|
+
}
|
|
407
|
+
// 检查 image model
|
|
408
|
+
if (openclawImageModel && !modelIds.includes(openclawImageModel)) {
|
|
409
|
+
setOpenclawImageModel('');
|
|
410
|
+
}
|
|
411
|
+
// 过滤无效的 image fallback 模型
|
|
412
|
+
const validImageFallbacks = openclawImageFallbacks.filter(id => modelIds.includes(id));
|
|
413
|
+
if (validImageFallbacks.length !== openclawImageFallbacks.length) {
|
|
414
|
+
setOpenclawImageFallbacks(validImageFallbacks);
|
|
415
|
+
}
|
|
416
|
+
}, [openclawModels]);
|
|
417
|
+
|
|
352
418
|
// Claude Handlers
|
|
353
419
|
const handleApply = async () => {
|
|
354
420
|
setApplying(true);
|
|
@@ -771,12 +837,12 @@ export default function IDEConfigPage() {
|
|
|
771
837
|
)}
|
|
772
838
|
</button>
|
|
773
839
|
<button
|
|
774
|
-
onClick={() => setActiveTab('
|
|
840
|
+
onClick={() => setActiveTab('picoclaw')}
|
|
775
841
|
disabled
|
|
776
842
|
className="relative px-4 py-3.5 text-sm font-medium text-slate-300 disabled:cursor-not-allowed"
|
|
777
843
|
>
|
|
778
844
|
<div className="flex items-center gap-2">
|
|
779
|
-
|
|
845
|
+
PicoClaw
|
|
780
846
|
<span className="px-2 py-0.5 text-[10px] font-medium text-slate-400 bg-slate-100/60 rounded-full border border-slate-200/50">
|
|
781
847
|
即将推出
|
|
782
848
|
</span>
|
|
@@ -2166,16 +2232,16 @@ export default function IDEConfigPage() {
|
|
|
2166
2232
|
</div>
|
|
2167
2233
|
)}
|
|
2168
2234
|
|
|
2169
|
-
{activeTab === '
|
|
2235
|
+
{activeTab === 'picoclaw' && (
|
|
2170
2236
|
<div className="flex flex-col items-center justify-center py-12 text-center">
|
|
2171
2237
|
<div className="w-16 h-16 bg-slate-100 rounded-full flex items-center justify-center mb-4">
|
|
2172
2238
|
<svg className="w-8 h-8 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
2173
2239
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
|
2174
2240
|
</svg>
|
|
2175
2241
|
</div>
|
|
2176
|
-
<h3 className="text-sm font-medium text-slate-900 mb-1">
|
|
2242
|
+
<h3 className="text-sm font-medium text-slate-900 mb-1">PicoClaw 支持即将推出</h3>
|
|
2177
2243
|
<p className="text-xs text-slate-500 max-w-xs">
|
|
2178
|
-
我们将很快添加对
|
|
2244
|
+
我们将很快添加对 PicoClaw 的支持,敬请期待。
|
|
2179
2245
|
</p>
|
|
2180
2246
|
</div>
|
|
2181
2247
|
)}
|
package/src/app/layout.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Metadata } from 'next';
|
|
2
2
|
import './globals.css';
|
|
3
3
|
import { ToastProvider } from './components/ToastProvider';
|
|
4
|
+
import Footer from './components/Footer';
|
|
4
5
|
|
|
5
6
|
export const metadata: Metadata = {
|
|
6
7
|
title: 'AAR - AI Agent Router',
|
|
@@ -14,8 +15,9 @@ export default function RootLayout({
|
|
|
14
15
|
}) {
|
|
15
16
|
return (
|
|
16
17
|
<html lang="zh-CN" suppressHydrationWarning>
|
|
17
|
-
<body>
|
|
18
|
+
<body className="pb-10">
|
|
18
19
|
<ToastProvider>{children}</ToastProvider>
|
|
20
|
+
<Footer />
|
|
19
21
|
</body>
|
|
20
22
|
</html>
|
|
21
23
|
);
|
package/src/db/database.ts
CHANGED
|
@@ -3,9 +3,17 @@ import { CREATE_TABLES_SQL } from './schema';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import os from 'os';
|
|
6
|
+
import crypto from 'crypto';
|
|
6
7
|
|
|
7
8
|
const DB_PATH = process.env.DB_PATH || path.join(os.homedir(), '.aar', 'gateway.db');
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* 生成随机的 API Key(用于网关密码默认值)
|
|
12
|
+
*/
|
|
13
|
+
function generateRandomApiKey(): string {
|
|
14
|
+
return `aar-${crypto.randomBytes(16).toString('hex')}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
// Use hardcoded path for the built/distributed WASM file
|
|
10
18
|
// Next.js copies sql-wasm.wasm to its chunks directory
|
|
11
19
|
function findSqlJsWasmPath(): string {
|
|
@@ -101,14 +109,19 @@ export async function getDatabase(): Promise<Database> {
|
|
|
101
109
|
const engine = await initSqlJsEngine();
|
|
102
110
|
|
|
103
111
|
const loaded = loadDatabase();
|
|
112
|
+
let isNewDatabase = false;
|
|
113
|
+
|
|
104
114
|
if (loaded) {
|
|
105
115
|
dbInstance = loaded;
|
|
106
116
|
if (fs.existsSync(DB_PATH)) lastLoadMtimeMs = fs.statSync(DB_PATH).mtimeMs;
|
|
107
117
|
} else {
|
|
108
118
|
dbInstance = new engine.Database();
|
|
109
119
|
lastLoadMtimeMs = 0;
|
|
120
|
+
isNewDatabase = true; // 标记为新数据库
|
|
110
121
|
}
|
|
111
122
|
|
|
123
|
+
let migrationPerformed = false;
|
|
124
|
+
|
|
112
125
|
try {
|
|
113
126
|
dbInstance!.run(CREATE_TABLES_SQL);
|
|
114
127
|
|
|
@@ -145,7 +158,7 @@ export async function getDatabase(): Promise<Database> {
|
|
|
145
158
|
dbInstance!.run(`DROP TABLE request_logs;`);
|
|
146
159
|
dbInstance!.run(`ALTER TABLE request_logs_new RENAME TO request_logs;`);
|
|
147
160
|
|
|
148
|
-
|
|
161
|
+
migrationPerformed = true;
|
|
149
162
|
console.log('[Database] Migration completed successfully');
|
|
150
163
|
}
|
|
151
164
|
}
|
|
@@ -158,7 +171,19 @@ export async function getDatabase(): Promise<Database> {
|
|
|
158
171
|
}
|
|
159
172
|
}
|
|
160
173
|
|
|
161
|
-
|
|
174
|
+
// 只在新数据库创建或执行了 migration 时才保存,避免覆盖现有数据
|
|
175
|
+
if (isNewDatabase || migrationPerformed) {
|
|
176
|
+
// 为新数据库初始化默认的 api_key
|
|
177
|
+
if (isNewDatabase) {
|
|
178
|
+
const defaultApiKey = generateRandomApiKey();
|
|
179
|
+
dbInstance!.run(
|
|
180
|
+
`INSERT OR IGNORE INTO config (key, value, updated_at) VALUES ('api_key', ?, datetime('now'))`,
|
|
181
|
+
[defaultApiKey]
|
|
182
|
+
);
|
|
183
|
+
console.log('[Database] Generated default API key for new database');
|
|
184
|
+
}
|
|
185
|
+
saveDatabase(dbInstance!);
|
|
186
|
+
}
|
|
162
187
|
} catch (schemaError: any) {
|
|
163
188
|
if (!schemaError.message.includes('already exists') &&
|
|
164
189
|
!schemaError.message.includes('duplicate')) {
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[76470,a=>{"use strict";a.s(["ToastProvider",()=>c,"useToast",()=>d]);var b=a.i(11857);let c=(0,b.registerClientReference)(function(){throw Error("Attempted to call ToastProvider() from the server but ToastProvider is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/src/app/components/ToastProvider.tsx <module evaluation>","ToastProvider"),d=(0,b.registerClientReference)(function(){throw Error("Attempted to call useToast() from the server but useToast is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/src/app/components/ToastProvider.tsx <module evaluation>","useToast")},11328,a=>{"use strict";a.s(["ToastProvider",()=>c,"useToast",()=>d]);var b=a.i(11857);let c=(0,b.registerClientReference)(function(){throw Error("Attempted to call ToastProvider() from the server but ToastProvider is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/src/app/components/ToastProvider.tsx","ToastProvider"),d=(0,b.registerClientReference)(function(){throw Error("Attempted to call useToast() from the server but useToast is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"[project]/src/app/components/ToastProvider.tsx","useToast")},79490,a=>{"use strict";a.i(76470);var b=a.i(11328);a.n(b)},27572,a=>{"use strict";var b=a.i(7997),c=a.i(79490);function d({children:a}){return(0,b.jsx)("html",{lang:"zh-CN",suppressHydrationWarning:!0,children:(0,b.jsx)("body",{children:(0,b.jsx)(c.ToastProvider,{children:a})})})}a.s(["default",()=>d,"metadata",0,{title:"AAR - AI Agent Router",description:"Unified API gateway for managing multiple AI model providers"}])}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=src_app_b2b1d928._.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/app/components/ToastProvider.tsx/__nextjs-internal-proxy.mjs","../../../../src/app/layout.tsx"],"sourcesContent":["// This file is generated by next-core EcmascriptClientReferenceModule.\nimport { registerClientReference } from \"react-server-dom-turbopack/server\";\nexport const ToastProvider = registerClientReference(\n function() { throw new Error(\"Attempted to call ToastProvider() from the server but ToastProvider is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.\"); },\n \"[project]/src/app/components/ToastProvider.tsx\",\n \"ToastProvider\",\n);\nexport const useToast = registerClientReference(\n function() { throw new Error(\"Attempted to call useToast() from the server but useToast is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.\"); },\n \"[project]/src/app/components/ToastProvider.tsx\",\n \"useToast\",\n);\n","import type { Metadata } from 'next';\nimport './globals.css';\nimport { ToastProvider } from './components/ToastProvider';\n\nexport const metadata: Metadata = {\n title: 'AAR - AI Agent Router',\n description: 'Unified API gateway for managing multiple AI model providers',\n};\n\nexport default function RootLayout({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n <html lang=\"zh-CN\" suppressHydrationWarning>\n <body>\n <ToastProvider>{children}</ToastProvider>\n </body>\n </html>\n );\n}\n"],"names":[],"mappings":"qFACA,IAAA,EAAA,EAAA,CAAA,CAAA,OACO,IAAM,EAAgB,CAAA,EAAA,EAAA,uBAAA,AAAuB,EAChD,WAAa,MAAM,AAAI,MAAM,wOAA0O,EACvQ,qEACA,iBAES,EAAW,CAAA,EAAA,EAAA,uBAAA,AAAuB,EAC3C,WAAa,MAAM,AAAI,MAAM,8NAAgO,EAC7P,qEACA,kFATJ,IAAA,EAAA,EAAA,CAAA,CAAA,OACO,IAAM,EAAgB,CAAA,EAAA,EAAA,uBAAA,AAAuB,EAChD,WAAa,MAAM,AAAI,MAAM,wOAA0O,EACvQ,iDACA,iBAES,EAAW,CAAA,EAAA,EAAA,uBAAA,AAAuB,EAC3C,WAAa,MAAM,AAAI,MAAM,8NAAgO,EAC7P,iDACA,+GCRJ,EAAA,EAAA,CAAA,CAAA,OAOe,SAAS,EAAW,UACjC,CAAQ,CAGT,EACC,MACE,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAK,KAAK,QAAQ,wBAAwB,CAAA,CAAA,WACzC,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,UACC,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,aAAa,CAAA,UAAE,OAIxB,mCAjBkC,CAChC,MAAO,wBACP,YAAa,8DACf","ignoreList":[0]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-1\/2{left:50%}.left-2\.5{left:.625rem}.left-3{left:.75rem}.left-4{left:1rem}.left-9{left:2.25rem}.right-2{right:.5rem}.right-2\.5{right:.625rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-20{top:5rem}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-auto{margin-left:auto;margin-right:auto}.-ml-1{margin-left:-.25rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-2\.5{margin-bottom:.625rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.ml-1{margin-left:.25rem}.ml-1\.5{margin-left:.375rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-2\.5{margin-top:.625rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-8{margin-top:2rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-10{height:2.5rem}.h-16{height:4rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[2px\]{height:2px}.max-h-44{max-height:11rem}.max-h-56{max-height:14rem}.max-h-96{max-height:24rem}.max-h-\[240px\]{max-height:240px}.max-h-\[60vh\]{max-height:60vh}.min-h-\[12rem\]{min-height:12rem}.min-h-\[38px\]{min-height:38px}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-16{width:4rem}.w-2\.5{width:.625rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-\[68px\]{width:68px}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-3xl{max-width:48rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-xs{max-width:20rem}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:1s linear infinite spin}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.list-none{list-style-type:none}.appearance-none{appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*calc(1 - var(--tw-space-x-reverse)))}.space-x-12>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(3rem*var(--tw-space-x-reverse));margin-left:calc(3rem*calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*calc(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*calc(1 - var(--tw-space-x-reverse)))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-3\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.875rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.875rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(241 245 249/var(--tw-divide-opacity,1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0}.border-dashed{border-style:dashed}.border-amber-200\/50{border-color:#fde68a80}.border-amber-200\/60{border-color:#fde68a99}.border-amber-200\/80{border-color:#fde68acc}.border-emerald-100\/30{border-color:#d1fae54d}.border-emerald-100\/50{border-color:#d1fae580}.border-emerald-200\/50{border-color:#a7f3d080}.border-emerald-200\/60{border-color:#a7f3d099}.border-emerald-600\/30{border-color:#0596694d}.border-indigo-200{--tw-border-opacity:1;border-color:rgb(199 210 254/var(--tw-border-opacity,1))}.border-rose-200{--tw-border-opacity:1;border-color:rgb(254 205 211/var(--tw-border-opacity,1))}.border-rose-200\/50{border-color:#fecdd380}.border-rose-200\/60{border-color:#fecdd399}.border-rose-600\/30{border-color:#e11d484d}.border-sky-200\/50{border-color:#bae6fd80}.border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity,1))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}.border-slate-200\/40{border-color:#e2e8f066}.border-slate-200\/50{border-color:#e2e8f080}.border-slate-200\/80{border-color:#e2e8f0cc}.border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}.border-slate-700\/30{border-color:#3341554d}.border-slate-800{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1))}.border-slate-900{--tw-border-opacity:1;border-color:rgb(15 23 42/var(--tw-border-opacity,1))}.border-transparent{border-color:#0000}.border-white\/40{border-color:#fff6}.border-t-slate-900{--tw-border-opacity:1;border-top-color:rgb(15 23 42/var(--tw-border-opacity,1))}.bg-amber-100{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity,1))}.bg-amber-100\/80{background-color:#fef3c7cc}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-amber-50\/80{background-color:#fffbebcc}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.bg-emerald-100\/50{background-color:#d1fae580}.bg-emerald-100\/80{background-color:#d1fae5cc}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.bg-emerald-50\/20{background-color:#ecfdf533}.bg-emerald-50\/30{background-color:#ecfdf54d}.bg-emerald-50\/60{background-color:#ecfdf599}.bg-emerald-50\/80{background-color:#ecfdf5cc}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.bg-indigo-100\/80{background-color:#e0e7ffcc}.bg-rose-100{--tw-bg-opacity:1;background-color:rgb(255 228 230/var(--tw-bg-opacity,1))}.bg-rose-100\/50{background-color:#ffe4e680}.bg-rose-100\/80{background-color:#ffe4e6cc}.bg-rose-50\/60{background-color:#fff1f299}.bg-rose-500{--tw-bg-opacity:1;background-color:rgb(244 63 94/var(--tw-bg-opacity,1))}.bg-rose-600{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity,1))}.bg-sky-50{--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.bg-slate-100\/60{background-color:#f1f5f999}.bg-slate-100\/80{background-color:#f1f5f9cc}.bg-slate-100\/90{background-color:#f1f5f9e6}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.bg-slate-50\/60{background-color:#f8fafc99}.bg-slate-50\/80{background-color:#f8fafccc}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.bg-slate-900{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}.bg-slate-900\/40{background-color:#0f172a66}.bg-slate-900\/95{background-color:#0f172af2}.bg-slate-950{--tw-bg-opacity:1;background-color:rgb(2 6 23/var(--tw-bg-opacity,1))}.bg-transparent{background-color:#0000}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/40{background-color:#fff6}.bg-white\/50{background-color:#ffffff80}.bg-white\/60{background-color:#fff9}.bg-white\/70{background-color:#ffffffb3}.bg-white\/80{background-color:#fffc}.bg-white\/90{background-color:#ffffffe6}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-emerald-50\/30{--tw-gradient-from:#ecfdf54d var(--tw-gradient-from-position);--tw-gradient-to:#ecfdf500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-white\/50{--tw-gradient-from:#ffffff80 var(--tw-gradient-from-position);--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-white{--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fff var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-slate-50\/50{--tw-gradient-to:#f8fafc80 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-3\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-20{padding-bottom:5rem}.pb-4{padding-bottom:1rem}.pl-9{padding-left:2.25rem}.pr-20{padding-right:5rem}.pr-3{padding-right:.75rem}.pr-7{padding-right:1.75rem}.pr-8{padding-right:2rem}.pr-9{padding-right:2.25rem}.pt-1{padding-top:.25rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-4{line-height:1rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-emerald-700{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.text-emerald-700\/80{color:#047857cc}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-indigo-700{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-600{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity,1))}.text-rose-700{--tw-text-opacity:1;color:rgb(190 18 60/var(--tw-text-opacity,1))}.text-rose-700\/80{color:#be123ccc}.text-sky-700{--tw-text-opacity:1;color:rgb(3 105 161/var(--tw-text-opacity,1))}.text-slate-200{--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity,1))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-emerald-500\/20{--tw-shadow-color:#10b98133;--tw-shadow:var(--tw-shadow-colored)}.shadow-rose-500\/20{--tw-shadow-color:#f43f5e33;--tw-shadow:var(--tw-shadow-colored)}.shadow-slate-600\/20{--tw-shadow-color:#47556933;--tw-shadow:var(--tw-shadow-colored)}.filter{filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px);-webkit-backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur)var(--tw-backdrop-brightness)var(--tw-backdrop-contrast)var(--tw-backdrop-grayscale)var(--tw-backdrop-hue-rotate)var(--tw-backdrop-invert)var(--tw-backdrop-opacity)var(--tw-backdrop-saturate)var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}:root{--background:#fafbf9;--foreground:#2d3436;--emerald-50:#ecfdf5;--emerald-100:#d1fae5;--emerald-500:#10b981;--emerald-700:#047857;--slate-50:#f8fafc;--slate-100:#f1f5f9;--slate-400:#94a3b8;--slate-500:#64748b;--slate-600:#475569;--slate-700:#334155;--slate-800:#1e293b}body{color:var(--foreground);background:var(--background);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;letter-spacing:-.01em;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}input,select,textarea{transition-property:all;transition-duration:.3s;transition-timing-function:cubic-bezier(.4,0,.2,1)}input:focus,select:focus,textarea:focus{outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:#34d39933;--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;outline:2px solid #0000}button{transition-property:all;transition-duration:.3s;transition-timing-function:cubic-bezier(.4,0,.2,1)}button:disabled{cursor:not-allowed;opacity:.5}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#f1f5f9}::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#94a3b8}.opencode-preview-textarea::-moz-selection{color:inherit!important;background:0 0!important}.opencode-preview-textarea::selection{color:inherit!important;background:0 0!important}.opencode-preview-textarea:focus{box-shadow:none!important;--tw-ring-shadow:none!important;--tw-ring-offset-shadow:none!important;outline:none!important}.placeholder\:text-slate-500::placeholder{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.last\:border-0:last-child{border-width:0}.hover\:border-amber-300:hover{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity,1))}.hover\:border-emerald-300:hover{--tw-border-opacity:1;border-color:rgb(110 231 183/var(--tw-border-opacity,1))}.hover\:border-slate-300:hover{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}.hover\:border-slate-300\/60:hover{border-color:#cbd5e199}.hover\:border-slate-300\/80:hover{border-color:#cbd5e1cc}.hover\:bg-amber-100:hover{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity,1))}.hover\:bg-amber-200:hover{--tw-bg-opacity:1;background-color:rgb(253 230 138/var(--tw-bg-opacity,1))}.hover\:bg-amber-50:hover{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.hover\:bg-amber-50\/50:hover{background-color:#fffbeb80}.hover\:bg-amber-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-emerald-100:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.hover\:bg-emerald-200:hover{--tw-bg-opacity:1;background-color:rgb(167 243 208/var(--tw-bg-opacity,1))}.hover\:bg-emerald-50:hover{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.hover\:bg-emerald-50\/20:hover{background-color:#ecfdf533}.hover\:bg-emerald-50\/50:hover{background-color:#ecfdf580}.hover\:bg-emerald-700:hover{--tw-bg-opacity:1;background-color:rgb(4 120 87/var(--tw-bg-opacity,1))}.hover\:bg-indigo-50:hover{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity,1))}.hover\:bg-rose-50:hover{--tw-bg-opacity:1;background-color:rgb(255 241 242/var(--tw-bg-opacity,1))}.hover\:bg-rose-600:hover{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity,1))}.hover\:bg-rose-700:hover{--tw-bg-opacity:1;background-color:rgb(190 18 60/var(--tw-bg-opacity,1))}.hover\:bg-slate-200\/80:hover{background-color:#e2e8f0cc}.hover\:bg-slate-50:hover{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.hover\:bg-slate-700:hover{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.hover\:bg-slate-800:hover{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:from-slate-50\/60:hover{--tw-gradient-from:#f8fafc99 var(--tw-gradient-from-position);--tw-gradient-to:#f8fafc00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:text-amber-700:hover{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.hover\:text-blue-700:hover{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.hover\:text-emerald-600:hover{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.hover\:text-emerald-700:hover{--tw-text-opacity:1;color:rgb(4 120 87/var(--tw-text-opacity,1))}.hover\:text-rose-500:hover{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.hover\:text-rose-600:hover{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity,1))}.hover\:text-slate-600:hover{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.hover\:text-slate-700:hover{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-sm:hover{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:border-amber-400:focus{--tw-border-opacity:1;border-color:rgb(251 191 36/var(--tw-border-opacity,1))}.focus\:border-emerald-400:focus{--tw-border-opacity:1;border-color:rgb(52 211 153/var(--tw-border-opacity,1))}.focus\:border-emerald-500:focus{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.focus\:border-indigo-400:focus{--tw-border-opacity:1;border-color:rgb(129 140 248/var(--tw-border-opacity,1))}.focus\:border-rose-400:focus{--tw-border-opacity:1;border-color:rgb(251 113 133/var(--tw-border-opacity,1))}.focus\:border-slate-400\/80:focus{border-color:#94a3b8cc}.focus\:outline-none:focus{outline-offset:2px;outline:2px solid #0000}.focus\:ring-0:focus{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-amber-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(253 230 138/var(--tw-ring-opacity,1))}.focus\:ring-amber-500\/50:focus{--tw-ring-color:#f59e0b80}.focus\:ring-blue-500\/50:focus{--tw-ring-color:#3b82f680}.focus\:ring-emerald-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(167 243 208/var(--tw-ring-opacity,1))}.focus\:ring-emerald-400\/20:focus{--tw-ring-color:#34d39933}.focus\:ring-emerald-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(16 185 129/var(--tw-ring-opacity,1))}.focus\:ring-emerald-500\/30:focus{--tw-ring-color:#10b9814d}.focus\:ring-emerald-500\/50:focus{--tw-ring-color:#10b98180}.focus\:ring-indigo-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(199 210 254/var(--tw-ring-opacity,1))}.focus\:ring-indigo-200\/50:focus{--tw-ring-color:#c7d2fe80}.focus\:ring-rose-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(254 205 211/var(--tw-ring-opacity,1))}.focus\:ring-rose-200\/50:focus{--tw-ring-color:#fecdd380}.focus\:ring-rose-500\/50:focus{--tw-ring-color:#f43f5e80}.focus\:ring-slate-200\/50:focus{--tw-ring-color:#e2e8f080}.focus\:ring-slate-400\/30:focus{--tw-ring-color:#94a3b84d}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px}.focus\:ring-offset-1:focus{--tw-ring-offset-width:1px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group[open] .group-open\:rotate-90{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y))rotate(var(--tw-rotate))skewX(var(--tw-skew-x))skewY(var(--tw-skew-y))scaleX(var(--tw-scale-x))scaleY(var(--tw-scale-y))}.group\/tip:hover .group-hover\/tip\:visible{visibility:visible}.group:hover .group-hover\:bg-slate-50\/40{background-color:#f8fafc66}.group:hover .group-hover\:text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.group\/tip:hover .group-hover\/tip\:opacity-100{opacity:1}@media (min-width:640px){.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:block{display:block}.sm\:flex{display:flex}.sm\:w-full{width:100%}.sm\:max-w-3xl{max-width:48rem}.sm\:max-w-md{max-width:28rem}.sm\:max-w-sm{max-width:24rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-0{padding-left:0;padding-right:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}.sm\:align-middle{vertical-align:middle}}@media (min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:px-10{padding-left:2.5rem;padding-right:2.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}}.\[\&\:\:-webkit-scrollbar-thumb\]\:rounded-full::-webkit-scrollbar-thumb{border-radius:9999px}.\[\&\:\:-webkit-scrollbar-thumb\]\:bg-slate-600::-webkit-scrollbar-thumb{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.\[\&\:\:-webkit-scrollbar-track\]\:bg-slate-900::-webkit-scrollbar-track{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}.\[\&\:\:-webkit-scrollbar\]\:w-2::-webkit-scrollbar{width:.5rem}
|