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.
Files changed (154) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +2 -2
  3. package/.next/fallback-build-manifest.json +2 -2
  4. package/.next/next-server.js.nft.json +1 -1
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/required-server-files.js +4 -4
  7. package/.next/required-server-files.json +4 -4
  8. package/.next/server/app/_global-error.html +2 -2
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page.js +1 -1
  16. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  21. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  23. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  26. package/.next/server/app/api/config/route.js.nft.json +1 -1
  27. package/.next/server/app/api/gateway/[...path]/route.js.nft.json +1 -1
  28. package/.next/server/app/api/gateway/models/route.js.nft.json +1 -1
  29. package/.next/server/app/api/gateway/route.js.nft.json +1 -1
  30. package/.next/server/app/api/ide/claude/apply/route.js.nft.json +1 -1
  31. package/.next/server/app/api/ide/claude/available-models/route.js.nft.json +1 -1
  32. package/.next/server/app/api/ide/claude/restore/route.js.nft.json +1 -1
  33. package/.next/server/app/api/ide/claude/save/route.js.nft.json +1 -1
  34. package/.next/server/app/api/ide/claude/status/route.js.nft.json +1 -1
  35. package/.next/server/app/api/ide/claude/test/route.js.nft.json +1 -1
  36. package/.next/server/app/api/ide/openclaw/apply/route.js.nft.json +1 -1
  37. package/.next/server/app/api/ide/openclaw/available-models/route.js.nft.json +1 -1
  38. package/.next/server/app/api/ide/openclaw/preview/route.js.nft.json +1 -1
  39. package/.next/server/app/api/ide/openclaw/save/route.js.nft.json +1 -1
  40. package/.next/server/app/api/ide/openclaw/status/route.js.nft.json +1 -1
  41. package/.next/server/app/api/ide/opencode/apply/route.js.nft.json +1 -1
  42. package/.next/server/app/api/ide/opencode/available-models/route.js.nft.json +1 -1
  43. package/.next/server/app/api/ide/opencode/preview/route.js.nft.json +1 -1
  44. package/.next/server/app/api/ide/opencode/save/route.js.nft.json +1 -1
  45. package/.next/server/app/api/ide/opencode/status/route.js.nft.json +1 -1
  46. package/.next/server/app/api/logs/route.js.nft.json +1 -1
  47. package/.next/server/app/api/models/route.js.nft.json +1 -1
  48. package/.next/server/app/api/providers/route.js.nft.json +1 -1
  49. package/.next/server/app/api/providers/test/route.js.nft.json +1 -1
  50. package/.next/server/app/api/service/force-stop/route.js.nft.json +1 -1
  51. package/.next/server/app/api/service/start/route.js.nft.json +1 -1
  52. package/.next/server/app/api/service/status/route.js.nft.json +1 -1
  53. package/.next/server/app/api/service/stop/route.js.nft.json +1 -1
  54. package/.next/server/app/ide/page.js +1 -1
  55. package/.next/server/app/ide/page.js.nft.json +1 -1
  56. package/.next/server/app/ide/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/ide.html +1 -1
  58. package/.next/server/app/ide.rsc +3 -3
  59. package/.next/server/app/ide.segments/_full.segment.rsc +3 -3
  60. package/.next/server/app/ide.segments/_head.segment.rsc +1 -1
  61. package/.next/server/app/ide.segments/_index.segment.rsc +2 -2
  62. package/.next/server/app/ide.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/ide.segments/ide/__PAGE__.segment.rsc +2 -2
  64. package/.next/server/app/ide.segments/ide.segment.rsc +1 -1
  65. package/.next/server/app/index.html +1 -1
  66. package/.next/server/app/index.rsc +2 -2
  67. package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  68. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  69. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  70. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  71. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/logs/page.js +1 -1
  73. package/.next/server/app/logs/page.js.nft.json +1 -1
  74. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/logs.html +1 -1
  76. package/.next/server/app/logs.rsc +2 -2
  77. package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
  78. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  79. package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
  80. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  82. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  83. package/.next/server/app/models/page.js +1 -1
  84. package/.next/server/app/models/page.js.nft.json +1 -1
  85. package/.next/server/app/models/page_client-reference-manifest.js +1 -1
  86. package/.next/server/app/models.html +1 -1
  87. package/.next/server/app/models.rsc +2 -2
  88. package/.next/server/app/models.segments/_full.segment.rsc +2 -2
  89. package/.next/server/app/models.segments/_head.segment.rsc +1 -1
  90. package/.next/server/app/models.segments/_index.segment.rsc +2 -2
  91. package/.next/server/app/models.segments/_tree.segment.rsc +2 -2
  92. package/.next/server/app/models.segments/models/__PAGE__.segment.rsc +1 -1
  93. package/.next/server/app/models.segments/models.segment.rsc +1 -1
  94. package/.next/server/app/page.js +1 -1
  95. package/.next/server/app/page.js.nft.json +1 -1
  96. package/.next/server/app/page_client-reference-manifest.js +1 -1
  97. package/.next/server/app/providers/page.js +1 -1
  98. package/.next/server/app/providers/page.js.nft.json +1 -1
  99. package/.next/server/app/providers/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/providers.html +1 -1
  101. package/.next/server/app/providers.rsc +2 -2
  102. package/.next/server/app/providers.segments/_full.segment.rsc +2 -2
  103. package/.next/server/app/providers.segments/_head.segment.rsc +1 -1
  104. package/.next/server/app/providers.segments/_index.segment.rsc +2 -2
  105. package/.next/server/app/providers.segments/_tree.segment.rsc +2 -2
  106. package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +1 -1
  107. package/.next/server/app/providers.segments/providers.segment.rsc +1 -1
  108. package/.next/server/chunks/[root-of-the-server]__3a204d25._.js +1 -1
  109. package/.next/server/chunks/[root-of-the-server]__3a204d25._.js.map +1 -1
  110. package/.next/server/chunks/[root-of-the-server]__4a8f9bc7._.js +1 -1
  111. package/.next/server/chunks/[root-of-the-server]__4a8f9bc7._.js.map +1 -1
  112. package/.next/server/chunks/[root-of-the-server]__5e8276bc._.js +1 -1
  113. package/.next/server/chunks/[root-of-the-server]__5e8276bc._.js.map +1 -1
  114. package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js +16 -16
  115. package/.next/server/chunks/[root-of-the-server]__c1b4b601._.js.map +1 -1
  116. package/.next/server/chunks/[root-of-the-server]__f0461b8d._.js +1 -1
  117. package/.next/server/chunks/[root-of-the-server]__f0461b8d._.js.map +1 -1
  118. package/.next/server/chunks/ssr/_69468f4c._.js +3 -0
  119. package/.next/server/chunks/ssr/_69468f4c._.js.map +1 -0
  120. package/.next/server/chunks/ssr/src_app_ide_page_tsx_8962793b._.js +1 -1
  121. package/.next/server/chunks/ssr/src_app_ide_page_tsx_8962793b._.js.map +1 -1
  122. package/.next/server/pages/404.html +1 -1
  123. package/.next/server/pages/500.html +2 -2
  124. package/.next/server/server-reference-manifest.js +1 -1
  125. package/.next/server/server-reference-manifest.json +1 -1
  126. package/.next/static/chunks/6992685fe009e8fd.css +1 -0
  127. package/.next/static/chunks/8ccc14e022ff6de3.js +1 -0
  128. package/README.md +31 -4
  129. package/dist/package.json +55 -0
  130. package/dist/src/app/api/config/route.js +17 -0
  131. package/dist/src/app/api/ide/claude/apply/route.js +84 -0
  132. package/dist/src/app/api/ide/claude/restore/route.js +30 -0
  133. package/dist/src/app/api/ide/claude/save/route.js +79 -0
  134. package/dist/src/app/components/Footer.js +11 -0
  135. package/dist/src/app/ide/page.js +62 -2
  136. package/dist/src/app/layout.js +5 -1
  137. package/dist/src/cli/index.js +0 -0
  138. package/dist/src/db/database.js +21 -2
  139. package/package.json +1 -1
  140. package/src/app/api/config/route.ts +17 -0
  141. package/src/app/api/ide/claude/apply/route.ts +94 -0
  142. package/src/app/api/ide/claude/restore/route.ts +36 -1
  143. package/src/app/api/ide/claude/save/route.ts +91 -1
  144. package/src/app/components/Footer.tsx +9 -0
  145. package/src/app/ide/page.tsx +71 -5
  146. package/src/app/layout.tsx +3 -1
  147. package/src/db/database.ts +27 -2
  148. package/.next/server/chunks/ssr/src_app_b2b1d928._.js +0 -3
  149. package/.next/server/chunks/ssr/src_app_b2b1d928._.js.map +0 -1
  150. package/.next/static/chunks/0f120c117962200b.css +0 -1
  151. package/.next/static/chunks/8ed839b2e4948968.js +0 -1
  152. /package/.next/static/{dYin74gcpdlg8TGoGv-_d → cf2SWIkI5HVbnDjLExI42}/_buildManifest.js +0 -0
  153. /package/.next/static/{dYin74gcpdlg8TGoGv-_d → cf2SWIkI5HVbnDjLExI42}/_clientMiddlewareManifest.json +0 -0
  154. /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
+ }
@@ -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('cursor')}
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
- Cursor
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 === 'cursor' && (
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">Cursor 支持即将推出</h3>
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
- 我们将很快添加对 Cursor 编辑器的支持,敬请期待。
2244
+ 我们将很快添加对 PicoClaw 的支持,敬请期待。
2179
2245
  </p>
2180
2246
  </div>
2181
2247
  )}
@@ -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
  );
@@ -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
- saveDatabase(dbInstance!);
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
- saveDatabase(dbInstance!);
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}