mob-coordinator 0.3.1 → 0.3.3

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.
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Mob — Claude Instance Dashboard</title>
7
7
  <link rel="icon" href="/favicon.png" />
8
- <script type="module" crossorigin src="/assets/index-D6WthCp0.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BE5nxcXU.css">
8
+ <script type="module" crossorigin src="/assets/index-WEyPZ0av.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DjMgegm_.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="app"></div>
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { extractJiraKey } from '../instance-manager.js';
3
+ describe('extractJiraKey', () => {
4
+ it('extracts key from branch name', () => {
5
+ expect(extractJiraKey('feature/PROJ-123-add-login')).toBe('PROJ-123');
6
+ });
7
+ it('extracts key from bare key', () => {
8
+ expect(extractJiraKey('PROJ-456')).toBe('PROJ-456');
9
+ });
10
+ it('returns null for no match', () => {
11
+ expect(extractJiraKey('main')).toBe(null);
12
+ });
13
+ it('returns null for undefined', () => {
14
+ expect(extractJiraKey(undefined)).toBe(null);
15
+ });
16
+ it('returns null for empty string', () => {
17
+ expect(extractJiraKey('')).toBe(null);
18
+ });
19
+ it('extracts first key if multiple', () => {
20
+ expect(extractJiraKey('PROJ-1-PROJ-2')).toBe('PROJ-1');
21
+ });
22
+ it('handles multi-char project prefix', () => {
23
+ expect(extractJiraKey('AB-1')).toBe('AB-1');
24
+ });
25
+ });
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ScrollbackBuffer } from '../scrollback-buffer.js';
3
+ function createBuffer() {
4
+ // Don't call start() — avoids timers and filesystem flushes
5
+ return new ScrollbackBuffer();
6
+ }
7
+ describe('ScrollbackBuffer', () => {
8
+ it('getTail returns empty for unknown instance', () => {
9
+ const buf = createBuffer();
10
+ expect(buf.getTail('unknown', 100)).toBe('');
11
+ });
12
+ it('append + getTail returns data', () => {
13
+ const buf = createBuffer();
14
+ buf.append('a', 'hello');
15
+ expect(buf.getTail('a', 5)).toBe('hello');
16
+ });
17
+ it('getTail truncates to requested chars', () => {
18
+ const buf = createBuffer();
19
+ buf.append('a', 'abcdef');
20
+ expect(buf.getTail('a', 3)).toBe('def');
21
+ });
22
+ it('multiple appends concatenate', () => {
23
+ const buf = createBuffer();
24
+ buf.append('a', 'abc');
25
+ buf.append('a', 'def');
26
+ expect(buf.getTail('a', 6)).toBe('abcdef');
27
+ });
28
+ it('getTail with more chars than available returns all', () => {
29
+ const buf = createBuffer();
30
+ buf.append('a', 'hi');
31
+ expect(buf.getTail('a', 100)).toBe('hi');
32
+ });
33
+ it('trims oldest chunks when over max bytes', () => {
34
+ const buf = createBuffer();
35
+ // SCROLLBACK_MAX_BYTES is 512KB. Append chunks that exceed it.
36
+ const chunkSize = 100_000; // 100KB per chunk
37
+ const chunk = 'x'.repeat(chunkSize);
38
+ for (let i = 0; i < 7; i++) {
39
+ buf.append('a', chunk);
40
+ }
41
+ // 700KB appended, should be trimmed to ~512KB
42
+ // getTail should return data but less than 700KB
43
+ const tail = buf.getTail('a', 700_000);
44
+ expect(tail.length).toBeLessThan(700_000);
45
+ expect(tail.length).toBeGreaterThan(0);
46
+ // Recent data should still be present
47
+ expect(tail).toContain('x');
48
+ });
49
+ it('separate instances are independent', () => {
50
+ const buf = createBuffer();
51
+ buf.append('a', 'aaa');
52
+ buf.append('b', 'bbb');
53
+ expect(buf.getTail('a', 10)).toBe('aaa');
54
+ expect(buf.getTail('b', 10)).toBe('bbb');
55
+ });
56
+ });
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { detectStateFromTerminal } from '../terminal-state-detector.js';
3
+ describe('detectStateFromTerminal', () => {
4
+ it('returns null for empty string', () => {
5
+ expect(detectStateFromTerminal('')).toBe(null);
6
+ });
7
+ it('returns null for null/undefined', () => {
8
+ expect(detectStateFromTerminal(null)).toBe(null);
9
+ });
10
+ it('detects waiting: y/n prompt', () => {
11
+ expect(detectStateFromTerminal('Allow this? (y/n)')).toBe('waiting');
12
+ });
13
+ it('detects waiting: approve', () => {
14
+ expect(detectStateFromTerminal('Please approve this action')).toBe('waiting');
15
+ });
16
+ it('detects waiting: allow', () => {
17
+ expect(detectStateFromTerminal('Allow tool use?')).toBe('waiting');
18
+ });
19
+ it('detects waiting: yes/no', () => {
20
+ expect(detectStateFromTerminal('Do you accept? yes or no')).toBe('waiting');
21
+ });
22
+ it('detects running: esc to interrupt', () => {
23
+ expect(detectStateFromTerminal('Working... esc to interrupt')).toBe('running');
24
+ });
25
+ it('detects running: spinner char', () => {
26
+ expect(detectStateFromTerminal('Processing ⠋ loading')).toBe('running');
27
+ });
28
+ it('detects idle: > prompt', () => {
29
+ expect(detectStateFromTerminal('some output\n> ')).toBe('idle');
30
+ });
31
+ it('detects idle: $ prompt', () => {
32
+ expect(detectStateFromTerminal('some output\n$ ')).toBe('idle');
33
+ });
34
+ it('returns null for unrecognized text', () => {
35
+ expect(detectStateFromTerminal('just some random text')).toBe(null);
36
+ });
37
+ it('waiting takes priority over running', () => {
38
+ expect(detectStateFromTerminal('approve? (y/n) ⠋')).toBe('waiting');
39
+ });
40
+ it('running takes priority over idle', () => {
41
+ expect(detectStateFromTerminal('⠋ working\n> ')).toBe('running');
42
+ });
43
+ });
@@ -7,7 +7,7 @@ import { detectStateFromTerminal } from './terminal-state-detector.js';
7
7
  import { createLogger } from './util/logger.js';
8
8
  const logger = createLogger('instance-mgr');
9
9
  const JIRA_KEY_RE = /([A-Z][A-Z0-9]+-\d+)/;
10
- function extractJiraKey(branch) {
10
+ export function extractJiraKey(branch) {
11
11
  if (!branch)
12
12
  return null;
13
13
  const m = branch.match(JIRA_KEY_RE);
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mergeWithDefaults, DEFAULT_SETTINGS } from '../settings.js';
3
+ describe('mergeWithDefaults', () => {
4
+ it('returns defaults for empty object', () => {
5
+ expect(mergeWithDefaults({})).toEqual(DEFAULT_SETTINGS);
6
+ });
7
+ it('overrides a single field', () => {
8
+ const result = mergeWithDefaults({ terminal: { fontSize: 18 } });
9
+ expect(result.terminal.fontSize).toBe(18);
10
+ expect(result.terminal.cursorStyle).toBe('block');
11
+ expect(result.terminal.scrollbackLines).toBe(5000);
12
+ });
13
+ it('ignores unrecognized top-level sections', () => {
14
+ const result = mergeWithDefaults({ unknown: { x: 1 } });
15
+ expect(result.unknown).toBeUndefined();
16
+ expect(result.terminal).toEqual(DEFAULT_SETTINGS.terminal);
17
+ });
18
+ it('overrides nested jira fields', () => {
19
+ const result = mergeWithDefaults({ jira: { baseUrl: 'https://jira.example.com' } });
20
+ expect(result.jira.baseUrl).toBe('https://jira.example.com');
21
+ expect(result.jira.email).toBe('');
22
+ expect(result.jira.apiToken).toBe('');
23
+ });
24
+ it('does not mutate DEFAULT_SETTINGS', () => {
25
+ const before = structuredClone(DEFAULT_SETTINGS);
26
+ const result = mergeWithDefaults({ terminal: { fontSize: 99 } });
27
+ result.terminal.fontSize = 999;
28
+ expect(DEFAULT_SETTINGS).toEqual(before);
29
+ });
30
+ it('handles non-object section values gracefully', () => {
31
+ const result = mergeWithDefaults({ terminal: 'invalid' });
32
+ expect(result.terminal).toEqual(DEFAULT_SETTINGS.terminal);
33
+ });
34
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mob-coordinator",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Local web dashboard for coordinating multiple Claude Code CLI sessions",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,32 +0,0 @@
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}