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 +207 -0
- package/bin/mob.js +20 -0
- package/dist/client/assets/index-BE5nxcXU.css +32 -0
- package/dist/client/assets/index-DkdPImCW.js +11 -0
- package/dist/client/index.html +14 -0
- package/dist/server/server/__tests__/sanitize.test.js +154 -0
- package/dist/server/server/discovery.js +36 -0
- package/dist/server/server/express-app.js +173 -0
- package/dist/server/server/index.js +54 -0
- package/dist/server/server/instance-manager.js +434 -0
- package/dist/server/server/jira-client.js +33 -0
- package/dist/server/server/pty-manager.js +98 -0
- package/dist/server/server/scrollback-buffer.js +102 -0
- package/dist/server/server/session-store.js +127 -0
- package/dist/server/server/settings-manager.js +87 -0
- package/dist/server/server/status-reader.js +29 -0
- package/dist/server/server/terminal-state-detector.js +42 -0
- package/dist/server/server/types.js +1 -0
- package/dist/server/server/util/id.js +4 -0
- package/dist/server/server/util/logger.js +34 -0
- package/dist/server/server/util/platform.js +48 -0
- package/dist/server/server/util/sanitize.js +126 -0
- package/dist/server/server/ws-server.js +197 -0
- package/dist/server/shared/constants.js +8 -0
- package/dist/server/shared/protocol.js +1 -0
- package/dist/server/shared/settings.js +54 -0
- package/package.json +68 -0
- package/scripts/postinstall.cjs +68 -0
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}
|