claude-dojo 1.0.1 → 1.1.1

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 (45) hide show
  1. package/README.md +48 -5
  2. package/bin/claude-dojo.js +67 -63
  3. package/client/dist/assets/index-AdmPCPR4.css +1 -0
  4. package/client/dist/assets/index-DqFw-kTY.js +4411 -0
  5. package/client/dist/index.html +2 -2
  6. package/package.json +1 -1
  7. package/server/dist/claudeService.d.ts +14 -2
  8. package/server/dist/claudeService.js +227 -27
  9. package/server/dist/commands/handlers/clear.d.ts +2 -0
  10. package/server/dist/commands/handlers/clear.js +36 -0
  11. package/server/dist/commands/handlers/compact.d.ts +2 -0
  12. package/server/dist/commands/handlers/compact.js +170 -0
  13. package/server/dist/commands/handlers/export.d.ts +2 -0
  14. package/server/dist/commands/handlers/export.js +98 -0
  15. package/server/dist/commands/handlers/file.d.ts +2 -0
  16. package/server/dist/commands/handlers/file.js +159 -0
  17. package/server/dist/commands/handlers/help.d.ts +2 -0
  18. package/server/dist/commands/handlers/help.js +41 -0
  19. package/server/dist/commands/handlers/model.d.ts +4 -0
  20. package/server/dist/commands/handlers/model.js +59 -0
  21. package/server/dist/commands/handlers/resume.d.ts +2 -0
  22. package/server/dist/commands/handlers/resume.js +94 -0
  23. package/server/dist/commands/handlers/rewind.d.ts +2 -0
  24. package/server/dist/commands/handlers/rewind.js +162 -0
  25. package/server/dist/commands/handlers/run.d.ts +2 -0
  26. package/server/dist/commands/handlers/run.js +100 -0
  27. package/server/dist/commands/handlers/search.d.ts +2 -0
  28. package/server/dist/commands/handlers/search.js +112 -0
  29. package/server/dist/commands/handlers/settings.d.ts +11 -0
  30. package/server/dist/commands/handlers/settings.js +171 -0
  31. package/server/dist/commands/handlers/status.d.ts +2 -0
  32. package/server/dist/commands/handlers/status.js +68 -0
  33. package/server/dist/commands/handlers/system.d.ts +5 -0
  34. package/server/dist/commands/handlers/system.js +64 -0
  35. package/server/dist/commands/handlers/todos.d.ts +2 -0
  36. package/server/dist/commands/handlers/todos.js +159 -0
  37. package/server/dist/commands/index.d.ts +13 -0
  38. package/server/dist/commands/index.js +103 -0
  39. package/server/dist/index.js +6 -0
  40. package/server/dist/routes/api.js +40 -0
  41. package/server/dist/sessionManager.d.ts +19 -1
  42. package/server/dist/sessionManager.js +248 -0
  43. package/server/dist/types/index.d.ts +69 -1
  44. package/client/dist/assets/index-C08_Q4bL.css +0 -1
  45. package/client/dist/assets/index-C3m65njK.js +0 -4365
package/README.md CHANGED
@@ -21,11 +21,47 @@ export ANTHROPIC_API_KEY="your-api-key-here"
21
21
  npx claude-dojo
22
22
  ```
23
23
 
24
- This starts the server and opens the dashboard in your browser. From there you can:
25
- - Create and manage Claude sessions
26
- - Watch agents work in real-time on a 3D hex grid
27
- - View conversation history and tool executions
28
- - Track token usage
24
+ This starts the server and opens the dashboard in your browser.
25
+
26
+ ## Upgrading
27
+
28
+ To update to the latest version:
29
+
30
+ ```bash
31
+ npx claude-dojo@latest
32
+ ```
33
+
34
+ Or if you installed globally:
35
+
36
+ ```bash
37
+ npm update -g claude-dojo
38
+ ```
39
+
40
+ ## Features
41
+
42
+ - **3D Hex Grid** - Visualize multiple Claude sessions as interactive hexagonal tiles
43
+ - **Real-time Streaming** - Watch responses stream in as Claude generates them
44
+ - **Multi-session Support** - Run multiple conversations in parallel
45
+ - **Subagents** - Spawn specialized agents (Explore, Plan, Bash, general-purpose)
46
+ - **Token Tracking** - Visual context bar showing token usage
47
+ - **Session Persistence** - Sessions survive server restarts
48
+ - **Resizable Panels** - Drag to resize the canvas/chat split
49
+ - **Markdown Rendering** - Rich formatting in chat messages
50
+
51
+ ## Slash Commands
52
+
53
+ | Command | Description |
54
+ |---------|-------------|
55
+ | `/help` | Show available commands |
56
+ | `/status` | Display session status and token usage |
57
+ | `/clear` | Clear conversation history |
58
+ | `/compact` | Compress context by summarizing messages |
59
+ | `/todos` | Manage session todo list |
60
+ | `/resume` | List and resume previous sessions |
61
+ | `/rewind` | Undo recent messages |
62
+ | `/export` | Export conversation to markdown |
63
+ | `/model` | Change the Claude model |
64
+ | `/settings` | View/modify settings |
29
65
 
30
66
  ## Keyboard Shortcuts
31
67
 
@@ -33,6 +69,13 @@ This starts the server and opens the dashboard in your browser. From there you c
33
69
  |-----|--------|
34
70
  | `1-9` | Select session by index |
35
71
  | `Esc` | Deselect current session |
72
+ | `/` | Focus input and start command |
73
+ | `Enter` | Send message |
74
+ | `Shift+Enter` | New line in message |
75
+
76
+ ## Data Storage
77
+
78
+ Sessions are persisted to `.claude-dojo/sessions/` in the project directory. Panel width preferences are stored in browser localStorage.
36
79
 
37
80
  ## License
38
81
 
@@ -1,85 +1,89 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { spawn } from 'child_process';
4
3
  import { fileURLToPath } from 'url';
5
4
  import { dirname, join } from 'path';
5
+ import { spawn } from 'child_process';
6
6
  import { existsSync } from 'fs';
7
7
 
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = dirname(__filename);
10
- const rootDir = join(__dirname, '..');
11
10
 
12
- console.log(`
13
- ╔═══════════════════════════════════════╗
14
- ║ Claude Dojo ║
15
- ║ Agent Dashboard ║
16
- ╚═══════════════════════════════════════╝
17
- `);
11
+ const PORT = process.env.PORT || 3001;
12
+ const OPEN_BROWSER = process.env.CLAUDE_DOJO_NO_BROWSER !== '1';
18
13
 
19
- // Check if we're in development or production mode
20
- const isDev = process.argv.includes('--dev');
14
+ // Find the server entry point
15
+ const serverPath = join(__dirname, '..', 'server', 'dist', 'index.js');
21
16
 
22
- async function openBrowser(url) {
23
- try {
24
- const open = (await import('open')).default;
25
- await open(url);
26
- } catch (error) {
27
- console.log(`Open ${url} in your browser`);
28
- }
17
+ if (!existsSync(serverPath)) {
18
+ console.error('Error: Server not built. Run "npm run build" first.');
19
+ console.error(`Expected server at: ${serverPath}`);
20
+ process.exit(1);
29
21
  }
30
22
 
31
- async function startProduction() {
32
- const serverPath = join(rootDir, 'server', 'dist', 'index.js');
23
+ // Check for client build
24
+ const clientPath = join(__dirname, '..', 'client', 'dist', 'index.html');
25
+ if (!existsSync(clientPath)) {
26
+ console.error('Error: Client not built. Run "npm run build" first.');
27
+ console.error(`Expected client at: ${clientPath}`);
28
+ process.exit(1);
29
+ }
33
30
 
34
- if (!existsSync(serverPath)) {
35
- console.error('Error: Server not built. Run "npm run build" first.');
36
- process.exit(1);
37
- }
31
+ console.log(`
32
+ ╔═══════════════════════════════════════════════════════════╗
33
+ ║ ║
34
+ ║ ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗ ║
35
+ ║ ██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝ ║
36
+ ║ ██║ ██║ ███████║██║ ██║██║ ██║█████╗ ║
37
+ ║ ██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ║
38
+ ║ ╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗ ║
39
+ ║ ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ║
40
+ ║ ║
41
+ ║ ██████╗ ██████╗ ██╗ ██████╗ ║
42
+ ║ ██╔══██╗██╔═══██╗ ██║██╔═══██╗ ║
43
+ ║ ██║ ██║██║ ██║ ██║██║ ██║ ║
44
+ ║ ██║ ██║██║ ██║██ ██║██║ ██║ ║
45
+ ║ ██████╔╝╚██████╔╝╚█████╔╝╚██████╔╝ ║
46
+ ║ ╚═════╝ ╚═════╝ ╚════╝ ╚═════╝ ║
47
+ ║ ║
48
+ ║ Visual Agent Dashboard for Claude ║
49
+ ║ ║
50
+ ╚═══════════════════════════════════════════════════════════╝
51
+ `);
38
52
 
39
- console.log('Starting Claude Dojo...\n');
53
+ console.log(`Starting server on http://localhost:${PORT}...`);
40
54
 
41
- // Set environment variable to suppress duplicate logs
42
- process.env.CLAUDE_DOJO_CLI = '1';
55
+ // Start the server
56
+ const server = spawn('node', [serverPath], {
57
+ stdio: 'inherit',
58
+ env: { ...process.env, PORT: String(PORT) },
59
+ });
43
60
 
44
- // Import and run the server (it starts automatically)
45
- await import(serverPath);
61
+ server.on('error', (err) => {
62
+ console.error('Failed to start server:', err.message);
63
+ process.exit(1);
64
+ });
46
65
 
47
- // Give server a moment to start, then open browser
66
+ // Open browser after a short delay to let server start
67
+ if (OPEN_BROWSER) {
48
68
  setTimeout(async () => {
49
- const port = process.env.PORT || 3001;
50
- if (process.env.NO_BROWSER !== '1') {
51
- await openBrowser(`http://localhost:${port}`);
69
+ try {
70
+ const open = (await import('open')).default;
71
+ await open(`http://localhost:${PORT}`);
72
+ console.log(`\nOpened browser at http://localhost:${PORT}`);
73
+ } catch (err) {
74
+ console.log(`\nOpen http://localhost:${PORT} in your browser`);
52
75
  }
53
- console.log('\nPress Ctrl+C to stop\n');
54
- }, 1000);
76
+ }, 1500);
55
77
  }
56
78
 
57
- async function startDevelopment() {
58
- console.log('Starting in development mode...\n');
59
-
60
- const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
61
- const child = spawn(npm, ['run', 'dev'], {
62
- cwd: rootDir,
63
- stdio: 'inherit',
64
- shell: true,
65
- });
66
-
67
- child.on('error', (error) => {
68
- console.error('Failed to start development server:', error);
69
- process.exit(1);
70
- });
71
-
72
- child.on('close', (code) => {
73
- process.exit(code || 0);
74
- });
75
-
76
- process.on('SIGINT', () => child.kill('SIGINT'));
77
- process.on('SIGTERM', () => child.kill('SIGTERM'));
78
- }
79
-
80
- // Main
81
- if (isDev) {
82
- startDevelopment();
83
- } else {
84
- startProduction();
85
- }
79
+ // Handle shutdown
80
+ process.on('SIGINT', () => {
81
+ console.log('\nShutting down...');
82
+ server.kill('SIGINT');
83
+ process.exit(0);
84
+ });
85
+
86
+ process.on('SIGTERM', () => {
87
+ server.kill('SIGTERM');
88
+ process.exit(0);
89
+ });
@@ -0,0 +1 @@
1
+ *,:before,:after{--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: rgb(59 130 246 / .5);--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: }::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: rgb(59 130 246 / .5);--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-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings: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:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-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{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,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{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.-right-1{right:-4px}.-right-1\.5{right:-6px}.-top-1{top:-4px}.-top-1\.5{top:-6px}.bottom-4{bottom:16px}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:16px}.right-0{right:0}.top-1\/2{top:50%}.top-4{top:16px}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.-mx-1\.5{margin-left:-6px;margin-right:-6px}.my-2{margin-top:8px;margin-bottom:8px}.my-4{margin-top:16px;margin-bottom:16px}.mb-1{margin-bottom:4px}.mb-2{margin-bottom:8px}.mb-3{margin-bottom:12px}.mb-4{margin-bottom:16px}.mt-0\.5{margin-top:2px}.mt-1{margin-top:4px}.mt-2{margin-top:8px}.mt-3{margin-top:12px}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-1{height:4px}.h-1\.5{height:6px}.h-10{height:40px}.h-16{height:4rem}.h-2{height:8px}.h-2\.5{height:10px}.h-3{height:12px}.h-3\.5{height:.875rem}.h-4{height:16px}.h-5{height:20px}.h-6{height:24px}.h-8{height:32px}.h-full{height:100%}.h-screen{height:100vh}.max-h-40{max-height:10rem}.max-h-64{max-height:16rem}.max-h-\[200px\]{max-height:200px}.min-h-\[24px\]{min-height:24px}.w-1{width:4px}.w-1\.5{width:6px}.w-10{width:40px}.w-16{width:4rem}.w-2{width:8px}.w-2\.5{width:10px}.w-3{width:12px}.w-3\.5{width:.875rem}.w-4{width:16px}.w-5{width:20px}.w-6{width:24px}.w-8{width:32px}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:0px}.max-w-\[100px\]{max-width:100px}.max-w-\[120px\]{max-width:120px}.max-w-\[150px\]{max-width:150px}.max-w-\[85\%\]{max-width:85%}.max-w-\[95\%\]{max-width:95%}.max-w-fit{max-width:-moz-fit-content;max-width:fit-content}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.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)) skew(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)) skew(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)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-col-resize{cursor:col-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:4px}.gap-1\.5{gap:6px}.gap-2{gap:8px}.gap-3{gap:12px}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(6px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(6px * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(8px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(8px * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(16px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(16px * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:8px}.rounded-2xl{border-radius:20px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:12px}.rounded-md{border-radius:10px}.rounded-xl{border-radius:16px}.rounded-bl-md{border-bottom-left-radius:10px}.rounded-br-md{border-bottom-right-radius:10px}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-apple-accent{--tw-border-opacity: 1;border-color:rgb(10 132 255 / var(--tw-border-opacity, 1))}.border-apple-separator{--tw-border-opacity: 1;border-color:rgb(56 56 58 / var(--tw-border-opacity, 1))}.border-apple-text-tertiary{--tw-border-opacity: 1;border-color:rgb(99 99 102 / var(--tw-border-opacity, 1))}.border-apple-yellow{--tw-border-opacity: 1;border-color:rgb(255 214 10 / var(--tw-border-opacity, 1))}.border-red-500\/30{border-color:#ef44444d}.border-red-500\/50{border-color:#ef444480}.border-l-apple-green{--tw-border-opacity: 1;border-left-color:rgb(48 209 88 / var(--tw-border-opacity, 1))}.border-l-apple-red{--tw-border-opacity: 1;border-left-color:rgb(255 69 58 / var(--tw-border-opacity, 1))}.border-l-apple-yellow{--tw-border-opacity: 1;border-left-color:rgb(255 214 10 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.border-t-transparent{border-top-color:transparent}.bg-apple-accent{--tw-bg-opacity: 1;background-color:rgb(10 132 255 / var(--tw-bg-opacity, 1))}.bg-apple-accent\/10{background-color:#0a84ff1a}.bg-apple-accent\/20{background-color:#0a84ff33}.bg-apple-accent\/30{background-color:#0a84ff4d}.bg-apple-accent\/90{background-color:#0a84ffe6}.bg-apple-bg-elevated{--tw-bg-opacity: 1;background-color:rgb(72 72 74 / var(--tw-bg-opacity, 1))}.bg-apple-bg-primary{--tw-bg-opacity: 1;background-color:rgb(28 28 30 / var(--tw-bg-opacity, 1))}.bg-apple-bg-secondary{--tw-bg-opacity: 1;background-color:rgb(44 44 46 / var(--tw-bg-opacity, 1))}.bg-apple-bg-secondary\/50{background-color:#2c2c2e80}.bg-apple-bg-tertiary{--tw-bg-opacity: 1;background-color:rgb(58 58 60 / var(--tw-bg-opacity, 1))}.bg-apple-green{--tw-bg-opacity: 1;background-color:rgb(48 209 88 / var(--tw-bg-opacity, 1))}.bg-apple-green\/10{background-color:#30d1581a}.bg-apple-green\/20{background-color:#30d15833}.bg-apple-red{--tw-bg-opacity: 1;background-color:rgb(255 69 58 / var(--tw-bg-opacity, 1))}.bg-apple-red\/20{background-color:#ff453a33}.bg-apple-separator{--tw-bg-opacity: 1;background-color:rgb(56 56 58 / var(--tw-bg-opacity, 1))}.bg-apple-text-secondary{--tw-bg-opacity: 1;background-color:rgb(152 152 157 / var(--tw-bg-opacity, 1))}.bg-apple-text-secondary\/20{background-color:#98989d33}.bg-apple-text-tertiary{--tw-bg-opacity: 1;background-color:rgb(99 99 102 / var(--tw-bg-opacity, 1))}.bg-apple-yellow{--tw-bg-opacity: 1;background-color:rgb(255 214 10 / var(--tw-bg-opacity, 1))}.bg-apple-yellow\/20{background-color:#ffd60a33}.bg-black\/50{background-color:#00000080}.bg-black\/60{background-color:#0009}.bg-black\/70{background-color:#000000b3}.bg-indigo-500\/30{background-color:#6366f14d}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/20{background-color:#ef444433}.bg-red-900\/50{background-color:#7f1d1d80}.bg-red-950\/90{background-color:#450a0ae6}.bg-transparent{background-color:transparent}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:4px}.p-1\.5{padding:6px}.p-2{padding:8px}.p-3{padding:12px}.p-4{padding:16px}.p-6{padding:24px}.px-1{padding-left:4px;padding-right:4px}.px-1\.5{padding-left:6px;padding-right:6px}.px-2{padding-left:8px;padding-right:8px}.px-3{padding-left:12px;padding-right:12px}.px-4{padding-left:16px;padding-right:16px}.px-6{padding-left:24px;padding-right:24px}.py-0\.5{padding-top:2px;padding-bottom:2px}.py-1{padding-top:4px;padding-bottom:4px}.py-1\.5{padding-top:6px;padding-bottom:6px}.py-12{padding-top:48px;padding-bottom:48px}.py-2{padding-top:8px;padding-bottom:8px}.py-3{padding-top:12px;padding-bottom:12px}.py-4{padding-top:16px;padding-bottom:16px}.pb-2{padding-bottom:8px}.pb-3{padding-bottom:12px}.pb-4{padding-bottom:16px}.pt-2{padding-top:8px}.pt-3{padding-top:12px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-base{font-size:15px;line-height:20px;letter-spacing:-.24px}.text-lg{font-size:17px;line-height:22px;letter-spacing:-.41px}.text-sm{font-size:13px;line-height:18px;letter-spacing:-.08px}.text-xl{font-size:20px;line-height:24px;letter-spacing:.38px}.text-xs{font-size:11px;line-height:13px;letter-spacing:.07px}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.tracking-wider{letter-spacing:.05em}.text-apple-accent{--tw-text-opacity: 1;color:rgb(10 132 255 / var(--tw-text-opacity, 1))}.text-apple-green{--tw-text-opacity: 1;color:rgb(48 209 88 / var(--tw-text-opacity, 1))}.text-apple-purple{--tw-text-opacity: 1;color:rgb(191 90 242 / var(--tw-text-opacity, 1))}.text-apple-red{--tw-text-opacity: 1;color:rgb(255 69 58 / var(--tw-text-opacity, 1))}.text-apple-text-primary{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-apple-text-secondary{--tw-text-opacity: 1;color:rgb(152 152 157 / var(--tw-text-opacity, 1))}.text-apple-text-tertiary{--tw-text-opacity: 1;color:rgb(99 99 102 / var(--tw-text-opacity, 1))}.text-apple-yellow{--tw-text-opacity: 1;color:rgb(255 214 10 / var(--tw-text-opacity, 1))}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.text-red-300\/70{color:#fca5a5b3}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\/50{color:#ffffff80}.text-white\/70{color:#ffffffb3}.text-white\/80{color:#fffc}.text-white\/90{color:#ffffffe6}.line-through{text-decoration-line:line-through}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-60{opacity:.6}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px 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-apple-lg{--tw-shadow: 0 10px 40px rgba(0, 0, 0, .4);--tw-shadow-colored: 0 10px 40px 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-apple-xl{--tw-shadow: 0 20px 60px rgba(0, 0, 0, .5);--tw-shadow-colored: 0 20px 60px 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-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--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-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--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)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring{--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(3px + 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)}.ring-2{--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)}.ring-apple-accent{--tw-ring-opacity: 1;--tw-ring-color: rgb(10 132 255 / var(--tw-ring-opacity, 1))}.blur{--tw-blur: blur(8px);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)}.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-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{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.glass{background:#2c2c2ecc;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}.transition-apple{transition:all .25s cubic-bezier(.25,.1,.25,1)}:root{--color-bg-primary: #1c1c1e;--color-bg-secondary: #2c2c2e;--color-bg-tertiary: #3a3a3c;--color-bg-elevated: #48484a;--color-accent: #0a84ff;--color-green: #30d158;--color-yellow: #ffd60a;--color-red: #ff453a;--color-text-primary: #ffffff;--color-text-secondary: #98989d;--color-separator: #38383a;--blur-lg: blur(20px);--blur-xl: blur(40px);--shadow-lg: 0 10px 40px rgba(0, 0, 0, .4);--radius-lg: 12px}*{box-sizing:border-box}html,body,#root{height:100%;margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,SF Pro Display,SF Pro Text,system-ui,sans-serif;background-color:var(--color-bg-primary);color:var(--color-text-primary);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overflow:hidden}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#fff3;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#ffffff4d}::-moz-selection{background:#0a84ff66}::selection{background:#0a84ff66}*:focus-visible{outline:2px solid var(--color-accent);outline-offset:2px}input,textarea{font-family:inherit}input::-moz-placeholder,textarea::-moz-placeholder{color:var(--color-text-secondary);opacity:.7}input::placeholder,textarea::placeholder{color:var(--color-text-secondary);opacity:.7}button{font-family:inherit;cursor:pointer}button:disabled{cursor:not-allowed;opacity:.5}code,pre,.font-mono{font-family:SF Mono,Monaco,Menlo,Courier New,monospace}@keyframes statusPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(1.1)}}.status-pulse{animation:statusPulse 2s ease-in-out infinite}@keyframes thinkingDot{0%,20%{opacity:.3}50%{opacity:1}80%,to{opacity:.3}}.thinking-dot-1{animation:thinkingDot 1.4s ease-in-out infinite}.thinking-dot-2{animation:thinkingDot 1.4s ease-in-out infinite .2s}.thinking-dot-3{animation:thinkingDot 1.4s ease-in-out infinite .4s}.placeholder\:text-apple-text-tertiary::-moz-placeholder{--tw-text-opacity: 1;color:rgb(99 99 102 / var(--tw-text-opacity, 1))}.placeholder\:text-apple-text-tertiary::placeholder{--tw-text-opacity: 1;color:rgb(99 99 102 / var(--tw-text-opacity, 1))}.focus-within\:border-apple-accent:focus-within{--tw-border-opacity: 1;border-color:rgb(10 132 255 / var(--tw-border-opacity, 1))}.hover\:bg-apple-accent-hover:hover{--tw-bg-opacity: 1;background-color:rgb(64 156 255 / var(--tw-bg-opacity, 1))}.hover\:bg-apple-bg-elevated:hover{--tw-bg-opacity: 1;background-color:rgb(72 72 74 / var(--tw-bg-opacity, 1))}.hover\:bg-apple-bg-tertiary:hover{--tw-bg-opacity: 1;background-color:rgb(58 58 60 / var(--tw-bg-opacity, 1))}.hover\:bg-apple-red\/20:hover{background-color:#ff453a33}.hover\:bg-apple-red\/80:hover{background-color:#ff453acc}.hover\:bg-red-500\/30:hover{background-color:#ef44444d}.hover\:bg-red-600:hover{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.hover\:text-apple-accent-hover:hover{--tw-text-opacity: 1;color:rgb(64 156 255 / var(--tw-text-opacity, 1))}.hover\:text-apple-red:hover{--tw-text-opacity: 1;color:rgb(255 69 58 / var(--tw-text-opacity, 1))}.hover\:text-apple-red\/80:hover{color:#ff453acc}.hover\:text-apple-text-secondary:hover{--tw-text-opacity: 1;color:rgb(152 152 157 / var(--tw-text-opacity, 1))}.focus\:border-none:focus{border-style:none}.focus\:border-apple-accent:focus{--tw-border-opacity: 1;border-color:rgb(10 132 255 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.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)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:bg-apple-accent\/50{background-color:#0a84ff80}.group:hover .group-hover\:bg-indigo-500\/20{background-color:#6366f133}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width: 640px){.sm\:block{display:block}}