mob-coordinator 0.2.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.
package/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # mob
2
+
3
+ A local web dashboard for coordinating multiple Claude Code CLI sessions. Launch, monitor, and switch between Claude instances working across different projects — all from a single browser tab.
4
+
5
+ ## Features
6
+
7
+ - **Launch and manage** multiple Claude Code sessions from a web UI
8
+ - **Live terminal** view with full I/O for each session (xterm.js)
9
+ - **Session persistence** — sessions survive server restarts and auto-resume
10
+ - **Auto-naming** — sessions are named based on what Claude is working on, refreshed every 5 prompts
11
+ - **Hook integration** — external Claude instances report status via hook scripts
12
+ - **Terminal state fallback** — detects running/waiting/idle from terminal output when hooks are silent
13
+ - **Browser notifications** — get notified when an instance needs input while you're in another tab
14
+ - **Git branch tracking** — see which branch each session is on, with tiered refresh rates
15
+ - **JIRA integration** — auto-detect ticket keys from branch names, show ticket status
16
+ - **Configurable settings** — keyboard shortcuts, launch defaults, terminal appearance, and more
17
+ - **Keyboard shortcuts** — cycle sessions, jump by number, clipboard support, all rebindable
18
+ - **Visual indicators** — pulsing "Needs Input" badge when Claude is waiting for you
19
+ - **Sidebar** — collapsible instance list, sorted by creation time with stopped instances last
20
+
21
+ ## Prerequisites
22
+
23
+ - **Node.js** 18+ (tested with 20.x)
24
+ - **Claude Code CLI** installed and authenticated (`claude` command available in your terminal)
25
+ - **git** (for branch detection)
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ git clone https://github.com/nickelbob/mob.git
31
+ cd mob
32
+ npm install
33
+ ```
34
+
35
+ If you see errors about missing native modules, run:
36
+
37
+ ```bash
38
+ npm run setup
39
+ ```
40
+
41
+ This detects your platform and installs the correct native binaries for node-pty and rollup. It also runs automatically before `npm run dev` and `npm run build`.
42
+
43
+ ### Install Claude Code Hooks
44
+
45
+ To enable status reporting (state, branch, auto-naming) from Claude instances launched by mob:
46
+
47
+ ```bash
48
+ npm run install-hooks
49
+ ```
50
+
51
+ This adds hook entries to `~/.claude/settings.json` that report instance status back to the dashboard.
52
+
53
+ ### Run
54
+
55
+ **Development** (hot-reload):
56
+
57
+ ```bash
58
+ npm run dev
59
+ ```
60
+
61
+ Opens the backend on `http://localhost:4040` and the Vite dev server on `http://localhost:4041`. Use port 4041 during development.
62
+
63
+ **Production**:
64
+
65
+ ```bash
66
+ npm run build
67
+ npm start
68
+ ```
69
+
70
+ Everything is served from `http://localhost:4040`.
71
+
72
+ ## Usage
73
+
74
+ ### Launching Instances
75
+
76
+ 1. Click **+ Launch Instance** (or press **Alt+N**)
77
+ 2. Type or paste a working directory path (autocomplete suggests as you type)
78
+ 3. Optionally set a name, model, and permission mode
79
+ 4. Click **Launch** (or press **Ctrl+Enter**)
80
+
81
+ The instance spawns a shell, loads your environment, and starts Claude Code in the specified directory.
82
+
83
+ ### Keyboard Shortcuts
84
+
85
+ All shortcuts are rebindable in **Settings > Shortcuts**.
86
+
87
+ | Default Shortcut | Action |
88
+ |---|---|
89
+ | **Alt+N** | Open launch dialog |
90
+ | **Alt+B** | Toggle sidebar |
91
+ | **Alt+Up/Down** | Cycle through sessions |
92
+ | **Alt+R** | Resume selected instance |
93
+ | **Ctrl/Cmd+1-9** | Jump to session by position |
94
+ | **Ctrl+C** | Copy selected text (or send interrupt if no selection) |
95
+ | **Ctrl+V** | Paste from clipboard into terminal |
96
+ | **Ctrl+Enter** | Launch instance (in launch dialog) |
97
+ | **Escape** | Close dialogs |
98
+
99
+ ### Session States
100
+
101
+ | State | Meaning |
102
+ |---|---|
103
+ | **Running** | Claude is working (using tools, generating response) |
104
+ | **Needs Input** | Claude is waiting for your input (permission prompt, question) |
105
+ | **Idle** | Session is idle at a prompt |
106
+ | **Launching** | Session is starting up |
107
+ | **Stopped** | Session ended (can be resumed) |
108
+
109
+ State detection uses hooks as the primary source. When hooks are silent for 15+ seconds, mob falls back to parsing terminal output for spinner characters, prompt patterns, and input requests. Dead PTY processes are detected and marked stopped automatically.
110
+
111
+ ### Settings
112
+
113
+ Open via the gear icon or **Settings** in the UI. Configurable sections:
114
+
115
+ - **Shortcuts** — rebind any keyboard shortcut
116
+ - **Launch Defaults** — default working directory, model, permission mode, auto-naming
117
+ - **Terminal** — font size, cursor style, scrollback lines
118
+ - **General** — sidebar default state, terminal cache size, browser notifications
119
+ - **JIRA** — base URL, email, and API token for ticket status integration
120
+
121
+ ### Browser Notifications
122
+
123
+ When enabled (default: on), mob sends a browser notification when an instance transitions to "waiting" while the tab is not focused. Grant notification permission when prompted on first load.
124
+
125
+ ### JIRA Integration
126
+
127
+ If configured in Settings > JIRA, mob will:
128
+ - Extract ticket keys (e.g., `PROJ-123`) from git branch names
129
+ - Show clickable ticket links in instance cards
130
+ - Fetch and display the current ticket status from JIRA
131
+
132
+ ### External Instances
133
+
134
+ Claude Code sessions started outside of mob (e.g., directly in a terminal) will appear in the dashboard as "external" instances if the hooks are installed. They show status, branch, and state but don't provide terminal I/O.
135
+
136
+ ## Architecture
137
+
138
+ Three layers:
139
+
140
+ - **Shared** (`src/shared/`) — WebSocket protocol types, constants, and settings schema
141
+ - **Server** (`src/server/`) — Node.js backend: Express + ws + node-pty + chokidar
142
+ - **Client** (`src/client/`) — Svelte 5 app with xterm.js
143
+
144
+ See `CLAUDE.md` for detailed architecture documentation.
145
+
146
+ ## Uninstalling Hooks
147
+
148
+ ```bash
149
+ npm run uninstall-hooks
150
+ ```
151
+
152
+ ## Troubleshooting
153
+
154
+ ### Missing native modules after `npm install`
155
+
156
+ npm has a [known bug](https://github.com/npm/cli/issues/4828) with optional platform-specific dependencies. Run `npm run setup` to auto-detect and install the correct ones. If that doesn't work:
157
+
158
+ ```bash
159
+ # Clean reinstall
160
+ rm -rf node_modules package-lock.json
161
+ npm install
162
+ npm run setup
163
+ ```
164
+
165
+ ### Port 4040 already in use
166
+
167
+ Another instance of mob (or another process) is using the port. Kill it or set a custom port:
168
+
169
+ ```bash
170
+ MOB_PORT=4050 npm run dev
171
+ ```
172
+
173
+ ### Hooks not reporting status
174
+
175
+ 1. Make sure hooks are installed: `npm run install-hooks`
176
+ 2. Check `~/.claude/settings.json` has entries for `PreToolUse`, `PostToolUse`, `Stop`, `UserPromptSubmit`, and `Notification`
177
+ 3. On Windows, ensure PowerShell can run the hook script (execution policy)
178
+
179
+ ### Instance stuck in wrong state
180
+
181
+ If hooks aren't firing (crash, subtask weirdness), the terminal state fallback should correct the state within ~15 seconds. If an instance shows as running but the PTY process is dead, it will be marked stopped automatically on the next stale check cycle.
182
+
183
+ ## Changelog
184
+
185
+ ### 0.2.0
186
+
187
+ - Terminal-based state fallback when hooks are silent (detects running/waiting/idle from scrollback)
188
+ - Dead PTY detection — instances with crashed processes auto-transition to stopped
189
+ - Browser notifications for waiting instances (configurable)
190
+ - Settings system with persistent configuration (shortcuts, launch defaults, terminal, general, JIRA)
191
+ - JIRA integration — ticket key extraction from branches, status display, clickable links
192
+ - Tiered git branch refresh (10s for active, 60s for idle instances)
193
+ - Fix auto-name state transfer on session resume
194
+ - Terminal auto-focus and scroll-to-bottom on session switch
195
+ - Collapsible sidebar
196
+ - Security hardening and input sanitization
197
+
198
+ ### 0.1.0
199
+
200
+ - Initial release
201
+ - Launch and manage multiple Claude Code sessions
202
+ - Live terminal with xterm.js
203
+ - Session persistence and resume
204
+ - Auto-naming from Claude's topic/subtask
205
+ - Hook-based status reporting
206
+ - Git branch tracking
207
+ - Keyboard shortcuts and clipboard support
package/bin/mob.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'child_process';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const root = join(__dirname, '..');
9
+ const serverEntry = join(root, 'dist', 'server', 'server', 'index.js');
10
+
11
+ // Run the server, forwarding stdio and signals
12
+ const child = spawn(process.execPath, [serverEntry], {
13
+ cwd: root,
14
+ stdio: 'inherit',
15
+ env: { ...process.env },
16
+ });
17
+
18
+ child.on('exit', (code) => process.exit(code ?? 0));
19
+ process.on('SIGINT', () => child.kill('SIGINT'));
20
+ process.on('SIGTERM', () => child.kill('SIGTERM'));
@@ -0,0 +1,32 @@
1
+ .badge.svelte-6xfvkt{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;padding:2px 8px;border-radius:10px;text-transform:uppercase;letter-spacing:.3px}.badge.svelte-6xfvkt:before{content:"";width:6px;height:6px;border-radius:50%}.running.svelte-6xfvkt{color:var(--green);background:#3fb9501a}.running.svelte-6xfvkt:before{background:var(--green)}.idle.svelte-6xfvkt{color:var(--accent);background:#58a6ff1a}.idle.svelte-6xfvkt:before{background:var(--accent)}.waiting.svelte-6xfvkt{color:var(--yellow);background:#d2992226}.waiting.svelte-6xfvkt:before{background:var(--yellow);animation:svelte-6xfvkt-pulse 1s ease-in-out infinite}.stopped.svelte-6xfvkt{color:var(--text-muted);background:#484f581a}.stopped.svelte-6xfvkt:before{background:var(--text-muted)}.launching.svelte-6xfvkt{color:var(--yellow);background:#d299221a}.launching.svelte-6xfvkt:before{background:var(--yellow);animation:svelte-6xfvkt-pulse 1s ease-in-out infinite}@keyframes svelte-6xfvkt-pulse{0%,to{opacity:1}50%{opacity:.3}}.progress-bar.svelte-1ogyq8y{width:100%;height:4px;background:var(--bg-primary);border-radius:2px;position:relative;margin-top:6px}.progress-fill.svelte-1ogyq8y{height:100%;background:var(--accent);border-radius:2px;transition:width .3s ease}.progress-label.svelte-1ogyq8y{position:absolute;right:0;top:-16px;font-size:10px;color:var(--text-muted)}.card.svelte-1u4di3v{padding:10px 12px;border:1px solid var(--border);border-radius:8px;cursor:pointer;transition:all .15s;background:var(--bg-secondary)}.card.svelte-1u4di3v:hover{border-color:var(--text-muted)}.card.selected.svelte-1u4di3v{border-color:var(--accent);background:#58a6ff0d}.card.needs-input.svelte-1u4di3v{border-left:3px solid var(--yellow);animation:svelte-1u4di3v-attention 2s ease-in-out infinite}@keyframes svelte-1u4di3v-attention{0%,to{border-left-color:var(--yellow)}50%{border-left-color:transparent}}.card-header.svelte-1u4di3v{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}.name.svelte-1u4di3v{font-weight:600;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-meta.svelte-1u4di3v{display:flex;flex-direction:column;gap:2px;margin-bottom:4px}.meta-item.svelte-1u4di3v{font-size:11px;color:var(--text-secondary);display:flex;align-items:center;gap:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-task.svelte-1u4di3v{display:flex;gap:6px;margin-bottom:4px;flex-wrap:wrap}.ticket.svelte-1u4di3v{font-size:11px;color:var(--purple);background:#bc8cff1a;padding:1px 6px;border-radius:4px}.ticket-link.svelte-1u4di3v{text-decoration:none;cursor:pointer}.ticket-link.svelte-1u4di3v:hover{text-decoration:underline}.ticket-status.svelte-1u4di3v{font-size:10px;color:var(--text-muted);background:#8b949e26;padding:1px 6px;border-radius:4px}.subtask.svelte-1u4di3v{font-size:11px;color:var(--text-secondary)}.current-tool.svelte-1u4di3v{font-size:11px;color:var(--yellow);margin-bottom:2px}.card-footer.svelte-1u4di3v{display:flex;align-items:center;justify-content:space-between;margin-top:6px}.badge-type.svelte-1u4di3v{font-size:10px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px}.kill-btn.svelte-1u4di3v{width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:4px;font-size:12px;color:var(--text-muted);transition:all .15s}.kill-btn.svelte-1u4di3v:hover{background:#f8514933;color:var(--red)}.card-actions.svelte-1u4di3v{display:flex;align-items:center;gap:4px}.resume-btn.svelte-1u4di3v{font-size:11px;padding:2px 8px;border-radius:4px;color:var(--accent);border:1px solid var(--accent);background:transparent;font-weight:600;transition:all .15s}.resume-btn.svelte-1u4di3v:hover{background:#58a6ff26}.dismiss-btn.svelte-1u4di3v{width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:4px;font-size:12px;color:var(--text-muted);transition:all .15s}.dismiss-btn.svelte-1u4di3v:hover{background:#8b949e33;color:var(--text-secondary)}.instance-list.svelte-1qhu77l{display:flex;flex-direction:column;gap:8px;padding:12px;overflow-y:auto;flex:1}.empty.svelte-1qhu77l{text-align:center;padding:32px 16px;color:var(--text-muted)}.empty.svelte-1qhu77l p:where(.svelte-1qhu77l){margin-bottom:8px}.hint.svelte-1qhu77l{font-size:12px}.terminal-panel.svelte-2hhoe8{flex:1;display:flex;flex-direction:column;background:var(--bg-primary);overflow:hidden}.waiting-banner.svelte-2hhoe8{padding:6px 12px;background:#d2992226;border-bottom:1px solid rgba(210,153,34,.3);color:var(--yellow);font-size:12px;text-align:center;font-weight:600;display:flex;align-items:center;justify-content:center;gap:8px}.pulse-dot.svelte-2hhoe8{width:8px;height:8px;border-radius:50%;background:var(--yellow);animation:svelte-2hhoe8-pulse 1s ease-in-out infinite}@keyframes svelte-2hhoe8-pulse{0%,to{opacity:1}50%{opacity:.3}}.stopped-banner.svelte-2hhoe8{padding:6px 12px;background:#8b949e26;border-bottom:1px solid var(--border);color:var(--text-muted);font-size:12px;text-align:center;font-weight:600}.launching-banner.svelte-2hhoe8{padding:6px 12px;background:#d299221a;border-bottom:1px solid var(--border);color:var(--yellow);font-size:12px;text-align:center;font-weight:600;display:flex;align-items:center;justify-content:center;gap:8px}.spinner.svelte-2hhoe8{width:12px;height:12px;border:2px solid rgba(210,153,34,.3);border-top-color:var(--yellow);border-radius:50%;animation:svelte-2hhoe8-spin .8s linear infinite}@keyframes svelte-2hhoe8-spin{to{transform:rotate(360deg)}}.terminal-container.svelte-2hhoe8{flex:1;padding:4px;overflow:hidden}.terminal-container.svelte-2hhoe8 .xterm{height:100%}.no-selection.svelte-2hhoe8,.external-info.svelte-2hhoe8{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-muted);gap:8px}.hint.svelte-2hhoe8,.external-hint.svelte-2hhoe8{font-size:12px;color:var(--text-muted)}.external-info.svelte-2hhoe8 h3:where(.svelte-2hhoe8){color:var(--text-primary);margin-bottom:16px}.info-grid.svelte-2hhoe8{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;font-size:13px;max-width:500px}.label.svelte-2hhoe8{color:var(--text-secondary);font-weight:600}.external-hint.svelte-2hhoe8{margin-top:24px;text-align:center;max-width:300px}.dashboard.svelte-62qd02{flex:1;display:flex;overflow:hidden}.sidebar.svelte-62qd02{width:var(--sidebar-width);min-width:var(--sidebar-width);border-right:1px solid var(--border);background:var(--bg-secondary);display:flex;flex-direction:row;overflow:hidden;transition:width .2s ease,min-width .2s ease;position:relative}.sidebar.collapsed.svelte-62qd02{width:28px;min-width:28px}.sidebar-content.svelte-62qd02{flex:1;display:flex;flex-direction:column;overflow:hidden}.sidebar.collapsed.svelte-62qd02 .sidebar-content:where(.svelte-62qd02){display:none}.collapse-toggle.svelte-62qd02{width:28px;min-width:28px;border:none;background:transparent;color:var(--text-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;font-size:16px;transition:color .15s}.collapse-toggle.svelte-62qd02:hover{color:var(--text-primary);background:var(--bg-tertiary)}.collapse-icon.svelte-62qd02{line-height:1}.main-area.svelte-62qd02{flex:1;display:flex;flex-direction:column;overflow:hidden;position:relative}.instance-toast.svelte-62qd02{position:absolute;top:8px;left:50%;transform:translate(-50%);background:var(--bg-tertiary);border:1px solid var(--border);border-radius:6px;padding:8px 14px;z-index:100;pointer-events:none;animation:svelte-62qd02-toast-fade 2s ease forwards;max-width:400px;text-align:center}.toast-name.svelte-62qd02{font-weight:600;font-size:13px;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.toast-detail.svelte-62qd02{font-size:11px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}@keyframes svelte-62qd02-toast-fade{0%{opacity:0;transform:translate(-50%) translateY(-4px)}10%{opacity:1;transform:translate(-50%) translateY(0)}80%{opacity:1}to{opacity:0}}.overlay.svelte-10d9dup{position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;display:flex;align-items:center;justify-content:center;z-index:100}.dialog.svelte-10d9dup{background:var(--bg-secondary);border:1px solid var(--border);border-radius:12px;padding:24px;width:480px;max-width:90vw;max-height:90vh;overflow-y:auto}h2.svelte-10d9dup{font-size:16px;margin-bottom:20px}.field.svelte-10d9dup{margin-bottom:14px;display:flex;flex-direction:column;gap:4px}.field-row.svelte-10d9dup{display:flex;gap:12px}.field-row.svelte-10d9dup .field:where(.svelte-10d9dup){flex:1}label.svelte-10d9dup{font-size:12px;color:var(--text-secondary);font-weight:600}input[type=text].svelte-10d9dup,select.svelte-10d9dup{width:100%}.autocomplete-wrap.svelte-10d9dup{position:relative}.input-with-browse.svelte-10d9dup{display:flex;gap:4px}.input-with-browse.svelte-10d9dup input:where(.svelte-10d9dup){flex:1}.browse-btn.svelte-10d9dup{padding:6px 10px;border-radius:6px;border:1px solid var(--accent);background:var(--accent);color:#fff;cursor:pointer;font-size:14px;flex-shrink:0;display:flex;align-items:center;justify-content:center}.browse-btn.svelte-10d9dup:hover:not(:disabled){background:var(--accent-hover);border-color:var(--accent-hover)}.browse-btn.svelte-10d9dup:disabled{opacity:.5;cursor:wait}.suggestions.svelte-10d9dup{position:absolute;top:100%;left:0;right:0;background:var(--bg-primary);border:1px solid var(--border);border-top:none;border-radius:0 0 6px 6px;list-style:none;max-height:200px;overflow-y:auto;z-index:10}.suggestions.svelte-10d9dup li:where(.svelte-10d9dup){padding:6px 10px;font-size:13px;cursor:pointer;color:var(--text-secondary)}.suggestions.svelte-10d9dup li:where(.svelte-10d9dup):hover,.suggestions.svelte-10d9dup li.selected:where(.svelte-10d9dup){background:var(--bg-tertiary);color:var(--text-primary)}.name-header.svelte-10d9dup{display:flex;align-items:center;justify-content:space-between}.toggle-label.svelte-10d9dup{display:flex;align-items:center;gap:5px;font-weight:400;cursor:pointer;-webkit-user-select:none;user-select:none}.toggle-label.svelte-10d9dup input[type=checkbox]:where(.svelte-10d9dup){width:auto;accent-color:var(--accent)}.toggle-label.svelte-10d9dup span:where(.svelte-10d9dup){font-size:11px;color:var(--text-muted)}.auto-name-hint.svelte-10d9dup{font-size:12px;color:var(--text-muted);font-style:italic;padding:6px 0}.actions.svelte-10d9dup{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.cancel-btn.svelte-10d9dup{padding:8px 16px;border-radius:6px;font-size:13px;color:var(--text-secondary);border:1px solid var(--border)}.cancel-btn.svelte-10d9dup:hover{color:var(--text-primary);border-color:var(--text-muted)}.launch-btn.svelte-10d9dup{background:var(--accent);color:#fff;padding:8px 16px;border-radius:6px;font-size:13px;font-weight:600}.launch-btn.svelte-10d9dup:hover{background:var(--accent-hover)}.overlay.svelte-1j81uoj{position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;display:flex;align-items:center;justify-content:center;z-index:100}.dialog.svelte-1j81uoj{background:var(--bg-secondary);border:1px solid var(--border);border-radius:12px;padding:24px;width:560px;max-width:90vw;max-height:90vh;overflow-y:auto}h2.svelte-1j81uoj{font-size:16px;margin-bottom:16px}.tabs.svelte-1j81uoj{display:flex;gap:4px;border-bottom:1px solid var(--border);margin-bottom:16px}.tab.svelte-1j81uoj{padding:8px 14px;font-size:13px;background:transparent;color:var(--text-secondary);border:none;border-bottom:2px solid transparent;cursor:pointer;transition:color .15s}.tab.svelte-1j81uoj:hover{color:var(--text-primary)}.tab.active.svelte-1j81uoj{color:var(--accent);border-bottom-color:var(--accent)}.tab-content.svelte-1j81uoj{min-height:200px}.shortcuts-table.svelte-1j81uoj{display:flex;flex-direction:column;gap:6px}.shortcut-row.svelte-1j81uoj{display:flex;align-items:center;gap:8px;padding:4px 0}.shortcut-label.svelte-1j81uoj{flex:1;font-size:13px;color:var(--text-primary)}.shortcut-binding.svelte-1j81uoj{min-width:120px;text-align:center}.shortcut-binding.svelte-1j81uoj kbd:where(.svelte-1j81uoj){background:var(--bg-primary);border:1px solid var(--border);border-radius:4px;padding:2px 8px;font-family:inherit;font-size:12px;color:var(--text-secondary)}.capturing.svelte-1j81uoj{font-size:12px;color:var(--accent);font-style:italic}.change-btn.svelte-1j81uoj{padding:3px 10px;font-size:12px;border-radius:4px;border:1px solid var(--border);background:transparent;color:var(--text-secondary);cursor:pointer}.change-btn.svelte-1j81uoj:hover{color:var(--text-primary);border-color:var(--text-muted)}.field.svelte-1j81uoj{margin-bottom:14px;display:flex;flex-direction:column;gap:4px}.field-row.svelte-1j81uoj{display:flex;gap:12px}.field-row.svelte-1j81uoj .field:where(.svelte-1j81uoj){flex:1}label.svelte-1j81uoj{font-size:12px;color:var(--text-secondary);font-weight:600}input[type=text].svelte-1j81uoj,input[type=number].svelte-1j81uoj,input[type=password].svelte-1j81uoj,select.svelte-1j81uoj{width:100%}input[type=range].svelte-1j81uoj{width:100%;accent-color:var(--accent)}.toggle-label.svelte-1j81uoj{display:flex;align-items:center;gap:6px;font-weight:400;cursor:pointer;-webkit-user-select:none;user-select:none}.toggle-label.svelte-1j81uoj input[type=checkbox]:where(.svelte-1j81uoj){width:auto;accent-color:var(--accent)}.toggle-label.svelte-1j81uoj span:where(.svelte-1j81uoj){font-size:13px;color:var(--text-primary)}.actions.svelte-1j81uoj{display:flex;justify-content:space-between;margin-top:20px;padding-top:16px;border-top:1px solid var(--border)}.reset-btn.svelte-1j81uoj{padding:8px 16px;border-radius:6px;font-size:13px;color:var(--red);border:1px solid var(--border);background:transparent;cursor:pointer}.reset-btn.svelte-1j81uoj:hover{border-color:var(--red)}.close-btn.svelte-1j81uoj{padding:8px 16px;border-radius:6px;font-size:13px;background:var(--accent);color:#fff;font-weight:600;cursor:pointer}.close-btn.svelte-1j81uoj:hover{background:var(--accent-hover)}main.svelte-y74n0s{height:100vh;display:flex;flex-direction:column}header.svelte-y74n0s{height:var(--header-height);background:var(--bg-secondary);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;padding:0 16px;flex-shrink:0}.logo.svelte-y74n0s{display:flex;align-items:baseline;gap:8px}.logo-text.svelte-y74n0s{font-size:18px;font-weight:700;color:var(--accent);letter-spacing:-.5px}.logo-sub.svelte-y74n0s{font-size:12px;color:var(--text-muted)}.header-actions.svelte-y74n0s{display:flex;align-items:center;gap:12px}.connection-status.svelte-y74n0s{font-size:12px;color:var(--red);display:flex;align-items:center;gap:6px}.connection-status.svelte-y74n0s:before{content:"";width:8px;height:8px;border-radius:50%;background:var(--red)}.connection-status.connected.svelte-y74n0s{color:var(--green)}.connection-status.connected.svelte-y74n0s:before{background:var(--green)}.settings-btn.svelte-y74n0s{background:transparent;border:1px solid var(--border);color:var(--text-secondary);padding:5px 7px;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:color .15s,border-color .15s}.settings-btn.svelte-y74n0s:hover{color:var(--text-primary);border-color:var(--text-muted)}.launch-btn.svelte-y74n0s{background:var(--accent);color:#fff;padding:6px 14px;border-radius:6px;font-size:13px;font-weight:600;transition:background .15s}.launch-btn.svelte-y74n0s:hover{background:var(--accent-hover)}.launch-btn.svelte-y74n0s kbd:where(.svelte-y74n0s){font-family:inherit;font-size:11px;opacity:.7;margin-left:4px}.error-toast-container.svelte-y74n0s{position:fixed;bottom:16px;right:16px;z-index:200;display:flex;flex-direction:column;gap:8px;max-width:400px}.error-toast.svelte-y74n0s{background:#f8514926;border:1px solid rgba(248,81,73,.4);color:var(--red);padding:10px 14px;border-radius:8px;font-size:13px;cursor:pointer;animation:svelte-y74n0s-toast-slide-in .3s ease}.error-context.svelte-y74n0s{display:block;font-size:11px;opacity:.7;margin-top:4px}@keyframes svelte-y74n0s-toast-slide-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}:root{--bg-primary: #0d1117;--bg-secondary: #161b22;--bg-tertiary: #21262d;--border: #30363d;--text-primary: #e6edf3;--text-secondary: #8b949e;--text-muted: #484f58;--accent: #58a6ff;--accent-hover: #79c0ff;--green: #3fb950;--yellow: #d29922;--red: #f85149;--orange: #d18616;--purple: #bc8cff;--sidebar-width: 320px;--header-height: 48px}*{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans,Helvetica,Arial,sans-serif;background:var(--bg-primary);color:var(--text-primary);overflow:hidden;height:100vh}#app{height:100vh;display:flex;flex-direction:column}button{cursor:pointer;border:none;background:none;color:inherit;font:inherit}input,select{font:inherit;color:var(--text-primary);background:var(--bg-primary);border:1px solid var(--border);border-radius:6px;padding:6px 10px}input:focus,select:focus{outline:none;border-color:var(--accent)}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:var(--bg-secondary)}::-webkit-scrollbar-thumb{background:var(--bg-tertiary);border-radius:4px}/**
2
+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
+ * https://github.com/chjj/term.js
5
+ * @license MIT
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ *
25
+ * Originally forked from (with the author's permission):
26
+ * Fabrice Bellard's javascript vt100 for jslinux:
27
+ * http://bellard.org/jslinux/
28
+ * Copyright (c) 2011 Fabrice Bellard
29
+ * The original design remains. The terminal itself
30
+ * has been extended to include xterm CSI codes, among
31
+ * other features.
32
+ */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}