@idl3/claude-control 0.1.2 → 0.1.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.
- package/lib/match.js +136 -0
- package/lib/pins.js +61 -0
- package/lib/prompt.js +73 -0
- package/lib/sessions.js +248 -105
- package/lib/subagents.js +154 -0
- package/lib/tmux.js +39 -18
- package/lib/uploads.js +20 -0
- package/package.json +1 -1
- package/server.js +193 -7
- package/web/dist/assets/{core-BYJcZW10.js → core-BYoRNKN7.js} +1 -1
- package/web/dist/assets/index-BxcH-YdA.css +1 -0
- package/web/dist/assets/index-CmkTUTz_.js +77 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-Bb7gXgl-.css +0 -1
- package/web/dist/assets/index-wrjqfzbL.js +0 -77
package/lib/tmux.js
CHANGED
|
@@ -110,6 +110,16 @@ export function isValidTarget(target) {
|
|
|
110
110
|
// Internal helpers
|
|
111
111
|
// ---------------------------------------------------------------------------
|
|
112
112
|
|
|
113
|
+
// We delimit `-F` format fields with US (\x1f). tmux only emits that control
|
|
114
|
+
// byte verbatim under a UTF-8 locale; in the C/POSIX locale (e.g. a bare launchd
|
|
115
|
+
// or cron environment) it sanitizes \x1f to "_", which collapses every row to a
|
|
116
|
+
// single field and yields zero panes. Force a UTF-8 locale for tmux so parsing
|
|
117
|
+
// is correct regardless of how the server was launched.
|
|
118
|
+
const TMUX_ENV = {
|
|
119
|
+
...process.env,
|
|
120
|
+
LC_ALL: process.env.LC_ALL || process.env.LANG || 'en_US.UTF-8',
|
|
121
|
+
};
|
|
122
|
+
|
|
113
123
|
/**
|
|
114
124
|
* Run a tmux sub-command with an explicit args array.
|
|
115
125
|
* @param {string[]} args
|
|
@@ -121,10 +131,7 @@ async function runTmux(args, opts = {}) {
|
|
|
121
131
|
return execFile(bin, args, {
|
|
122
132
|
timeout: opts.timeout ?? 10_000,
|
|
123
133
|
maxBuffer: 4 * 1024 * 1024,
|
|
124
|
-
|
|
125
|
-
// sanitizes our \x1f field separator to '_', so list parsing yields nothing.
|
|
126
|
-
// A UTF-8 locale makes tmux emit \x1f literally. (Honor an existing locale.)
|
|
127
|
-
env: { ...process.env, LC_ALL: process.env.LC_ALL || 'en_US.UTF-8' },
|
|
134
|
+
env: TMUX_ENV,
|
|
128
135
|
});
|
|
129
136
|
}
|
|
130
137
|
|
|
@@ -139,7 +146,7 @@ function assertTarget(target) {
|
|
|
139
146
|
}
|
|
140
147
|
|
|
141
148
|
// ---------------------------------------------------------------------------
|
|
142
|
-
// List
|
|
149
|
+
// List panes
|
|
143
150
|
// ---------------------------------------------------------------------------
|
|
144
151
|
|
|
145
152
|
/**
|
|
@@ -147,8 +154,9 @@ function assertTarget(target) {
|
|
|
147
154
|
* @property {string} sessionName
|
|
148
155
|
* @property {number} windowIndex
|
|
149
156
|
* @property {string} windowName
|
|
150
|
-
* @property {string} target "sessionName:windowIndex"
|
|
151
|
-
* @property {boolean} active
|
|
157
|
+
* @property {string} target "sessionName:windowIndex.paneIndex"
|
|
158
|
+
* @property {boolean} active window is the active window in its session
|
|
159
|
+
* @property {boolean} paneActive pane is the active pane in its window
|
|
152
160
|
* @property {number} panePid
|
|
153
161
|
* @property {string} cwd
|
|
154
162
|
* @property {string} cmd
|
|
@@ -168,18 +176,23 @@ const FORMAT = [
|
|
|
168
176
|
'#{pane_current_command}',
|
|
169
177
|
'#{window_id}',
|
|
170
178
|
'#{pane_index}',
|
|
179
|
+
'#{pane_active}',
|
|
171
180
|
].join(SEP);
|
|
172
181
|
|
|
173
182
|
/**
|
|
174
|
-
* List
|
|
175
|
-
*
|
|
183
|
+
* List every tmux PANE across all sessions (one entry per pane, not per window).
|
|
184
|
+
* A `target` therefore identifies an exact pane —
|
|
185
|
+
* "sessionName:windowIndex.paneIndex" — so the cockpit distinguishes multiple
|
|
186
|
+
* Claude panes sharing one window, and `send-keys -t <target>` always lands in
|
|
187
|
+
* the intended pane rather than the window's currently-active pane. Resolves to
|
|
188
|
+
* [] when no tmux server is running.
|
|
176
189
|
*
|
|
177
190
|
* @returns {Promise<Window[]>}
|
|
178
191
|
*/
|
|
179
|
-
export async function
|
|
192
|
+
export async function listPanes() {
|
|
180
193
|
let stdout;
|
|
181
194
|
try {
|
|
182
|
-
({ stdout } = await runTmux(['list-
|
|
195
|
+
({ stdout } = await runTmux(['list-panes', '-a', '-F', FORMAT]));
|
|
183
196
|
} catch (err) {
|
|
184
197
|
// tmux exits 1 with "no server running" or "error connecting to server"
|
|
185
198
|
const msg = String(err?.message || '');
|
|
@@ -201,35 +214,43 @@ export async function listWindows() {
|
|
|
201
214
|
throw err;
|
|
202
215
|
}
|
|
203
216
|
|
|
204
|
-
const
|
|
217
|
+
const panes = [];
|
|
205
218
|
for (const line of stdout.split('\n')) {
|
|
206
219
|
const trimmed = line.trim();
|
|
207
220
|
if (!trimmed) continue;
|
|
208
221
|
|
|
209
222
|
const parts = trimmed.split(SEP);
|
|
210
|
-
if (parts.length <
|
|
223
|
+
if (parts.length < 9) continue;
|
|
211
224
|
|
|
212
|
-
const [sessionName, rawIndex, windowName, rawActive, rawPid, cwd, cmd, windowId, rawPane] = parts;
|
|
225
|
+
const [sessionName, rawIndex, windowName, rawActive, rawPid, cwd, cmd, windowId, rawPane, rawPaneActive] = parts;
|
|
213
226
|
const windowIndex = Number(rawIndex);
|
|
214
227
|
const panePid = Number(rawPid);
|
|
228
|
+
const paneIndex = Number(rawPane) || 0;
|
|
215
229
|
|
|
216
|
-
|
|
230
|
+
panes.push({
|
|
217
231
|
sessionName,
|
|
218
232
|
windowIndex,
|
|
219
233
|
windowName,
|
|
220
|
-
target: `${sessionName}:${windowIndex}`,
|
|
234
|
+
target: `${sessionName}:${windowIndex}.${paneIndex}`,
|
|
221
235
|
active: rawActive === '1',
|
|
236
|
+
paneActive: rawPaneActive === '1',
|
|
222
237
|
panePid,
|
|
223
238
|
cwd,
|
|
224
239
|
cmd,
|
|
225
240
|
windowId: windowId ?? `${sessionName}:${windowIndex}`,
|
|
226
|
-
paneIndex
|
|
241
|
+
paneIndex,
|
|
227
242
|
});
|
|
228
243
|
}
|
|
229
244
|
|
|
230
|
-
return
|
|
245
|
+
return panes;
|
|
231
246
|
}
|
|
232
247
|
|
|
248
|
+
/**
|
|
249
|
+
* @deprecated Back-compat alias — enumerates panes (not windows). Kept so any
|
|
250
|
+
* external caller keeps working after the window→pane migration.
|
|
251
|
+
*/
|
|
252
|
+
export const listWindows = listPanes;
|
|
253
|
+
|
|
233
254
|
// ---------------------------------------------------------------------------
|
|
234
255
|
// Session-name helpers
|
|
235
256
|
// ---------------------------------------------------------------------------
|
package/lib/uploads.js
CHANGED
|
@@ -40,3 +40,23 @@ export async function sweepUploads(dir, ttlMs, now = Date.now()) {
|
|
|
40
40
|
}
|
|
41
41
|
return { removed, kept };
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a client-supplied file path, confining it to `uploadsDir`. Returns the
|
|
46
|
+
* absolute path when it lives strictly inside uploadsDir, else null. Guards the
|
|
47
|
+
* /api/file route against serving arbitrary filesystem paths (path traversal).
|
|
48
|
+
*
|
|
49
|
+
* @param {string} raw client-supplied ?path= value
|
|
50
|
+
* @param {string} uploadsDir absolute uploads directory
|
|
51
|
+
* @returns {string|null}
|
|
52
|
+
*/
|
|
53
|
+
export function resolveUploadPath(raw, uploadsDir) {
|
|
54
|
+
if (!raw || typeof raw !== 'string') return null;
|
|
55
|
+
let full;
|
|
56
|
+
try {
|
|
57
|
+
full = path.resolve(raw);
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return full.startsWith(uploadsDir + path.sep) ? full : null;
|
|
62
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idl3/claude-control",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local web UI to watch and drive your Claude Code sessions running in tmux — live transcripts, reply, answer AskUserQuestion, attach files, from a browser or phone.",
|
|
6
6
|
"keywords": [
|
package/server.js
CHANGED
|
@@ -15,10 +15,13 @@ import { WebSocketServer } from 'ws';
|
|
|
15
15
|
import * as tmux from './lib/tmux.js';
|
|
16
16
|
import * as terminal from './lib/terminal.js';
|
|
17
17
|
import { TranscriptTailer } from './lib/transcript.js';
|
|
18
|
-
import {
|
|
18
|
+
import { SubAgentsWatcher } from './lib/subagents.js';
|
|
19
|
+
import { parsePanePrompt } from './lib/prompt.js';
|
|
20
|
+
import { SessionRegistry, listRecentTranscripts } from './lib/sessions.js';
|
|
21
|
+
import { loadPins, savePins, validateTranscriptPath, pinKey } from './lib/pins.js';
|
|
19
22
|
import { ResourceMonitor } from './lib/resources.js';
|
|
20
23
|
import { buildAnswerProgram } from './lib/answer.js';
|
|
21
|
-
import { sweepUploads } from './lib/uploads.js';
|
|
24
|
+
import { sweepUploads, resolveUploadPath } from './lib/uploads.js';
|
|
22
25
|
import { getVersionInfo, currentVersion } from './lib/version.js';
|
|
23
26
|
import * as push from './lib/push.js';
|
|
24
27
|
import { readConfig, writeConfig } from './lib/config.js';
|
|
@@ -69,6 +72,8 @@ const CONFIG = {
|
|
|
69
72
|
uploadsDir:
|
|
70
73
|
env('UPLOADS') || path.join(os.homedir(), '.claude-control', 'uploads'),
|
|
71
74
|
uploadTtlHours: Number(env('UPLOAD_TTL_HOURS')) || 24,
|
|
75
|
+
pinsFile:
|
|
76
|
+
env('PINS') || path.join(os.homedir(), '.claude-control', 'pins.json'),
|
|
72
77
|
};
|
|
73
78
|
|
|
74
79
|
const MIME = {
|
|
@@ -99,6 +104,10 @@ const IMAGE_MIME = {
|
|
|
99
104
|
const registry = new SessionRegistry({ projectsRoot: CONFIG.projectsRoot, tmux });
|
|
100
105
|
const resources = new ResourceMonitor({ rssLimitMB: CONFIG.rssLimitMB });
|
|
101
106
|
|
|
107
|
+
// Manual transcript pins (windowId.paneIndex -> transcript path). Loaded at boot,
|
|
108
|
+
// applied to the registry, and editable via /api/pins.
|
|
109
|
+
let pins = loadPins(CONFIG.pinsFile);
|
|
110
|
+
|
|
102
111
|
/** id -> { tailer, clients:Set<ws>, pending } */
|
|
103
112
|
const subscriptions = new Map();
|
|
104
113
|
|
|
@@ -175,6 +184,21 @@ const server = http.createServer((req, res) => {
|
|
|
175
184
|
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
176
185
|
return handleUpload(req, res, u);
|
|
177
186
|
}
|
|
187
|
+
if (u.pathname === '/api/file') {
|
|
188
|
+
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
189
|
+
return handleServeFile(res, u);
|
|
190
|
+
}
|
|
191
|
+
if (u.pathname === '/api/pins') {
|
|
192
|
+
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
193
|
+
if (req.method === 'POST') return handleSetPin(req, res);
|
|
194
|
+
return endJson(res, 200, { pins });
|
|
195
|
+
}
|
|
196
|
+
if (u.pathname === '/api/transcripts') {
|
|
197
|
+
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
198
|
+
return listRecentTranscripts({ projectsRoot: CONFIG.projectsRoot })
|
|
199
|
+
.then((list) => endJson(res, 200, { transcripts: list }))
|
|
200
|
+
.catch((err) => endJson(res, 500, { error: String(err?.message || err) }));
|
|
201
|
+
}
|
|
178
202
|
if (u.pathname === '/api/push/vapid') {
|
|
179
203
|
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
180
204
|
return endJson(res, 200, { publicKey: push.getPublicKey() });
|
|
@@ -539,6 +563,69 @@ async function proxyTerminalHttp(req, res, u) {
|
|
|
539
563
|
req.pipe(proxyReq);
|
|
540
564
|
}
|
|
541
565
|
|
|
566
|
+
// Serve a previously-uploaded file back to the UI by absolute path (used by the
|
|
567
|
+
// in-transcript image previews / lightbox). Coexists with /api/uploads/<basename>
|
|
568
|
+
// above; both confine strictly to uploadsDir.
|
|
569
|
+
const FILE_MIME = {
|
|
570
|
+
'.png': 'image/png',
|
|
571
|
+
'.jpg': 'image/jpeg',
|
|
572
|
+
'.jpeg': 'image/jpeg',
|
|
573
|
+
'.gif': 'image/gif',
|
|
574
|
+
'.webp': 'image/webp',
|
|
575
|
+
'.avif': 'image/avif',
|
|
576
|
+
'.bmp': 'image/bmp',
|
|
577
|
+
'.svg': 'image/svg+xml',
|
|
578
|
+
'.pdf': 'application/pdf',
|
|
579
|
+
};
|
|
580
|
+
function handleServeFile(res, u) {
|
|
581
|
+
const full = resolveUploadPath(u.searchParams.get('path') || '', CONFIG.uploadsDir);
|
|
582
|
+
// Confinement: only files strictly inside uploadsDir are served.
|
|
583
|
+
if (!full) {
|
|
584
|
+
return endJson(res, 403, { error: 'forbidden' });
|
|
585
|
+
}
|
|
586
|
+
fs.readFile(full, (err, data) => {
|
|
587
|
+
if (err) { res.writeHead(404); return res.end('not found'); }
|
|
588
|
+
const ext = path.extname(full).toLowerCase();
|
|
589
|
+
res.writeHead(200, {
|
|
590
|
+
'content-type': FILE_MIME[ext] || 'application/octet-stream',
|
|
591
|
+
'cache-control': 'private, max-age=3600',
|
|
592
|
+
});
|
|
593
|
+
res.end(data);
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Set or clear a manual transcript pin. Body: { id, transcriptPath }.
|
|
598
|
+
// transcriptPath null/empty clears the pin. The pin is keyed by the session's
|
|
599
|
+
// stable windowId.paneIndex so it survives tmux window renumbering.
|
|
600
|
+
async function handleSetPin(req, res) {
|
|
601
|
+
let body;
|
|
602
|
+
try {
|
|
603
|
+
body = await readJsonBody(req);
|
|
604
|
+
} catch (err) {
|
|
605
|
+
return endJson(res, 400, { error: String(err?.message || err) });
|
|
606
|
+
}
|
|
607
|
+
const id = typeof body?.id === 'string' ? body.id : '';
|
|
608
|
+
const session = sessionById(id);
|
|
609
|
+
if (!session) return endJson(res, 404, { error: 'unknown session' });
|
|
610
|
+
const key = pinKey(session.windowId, session.paneIndex);
|
|
611
|
+
|
|
612
|
+
const raw = body?.transcriptPath;
|
|
613
|
+
if (raw == null || raw === '') {
|
|
614
|
+
delete pins[key];
|
|
615
|
+
} else {
|
|
616
|
+
const full = validateTranscriptPath(raw, CONFIG.projectsRoot);
|
|
617
|
+
if (!full) return endJson(res, 400, { error: 'invalid transcript path' });
|
|
618
|
+
pins = { ...pins, [key]: full };
|
|
619
|
+
}
|
|
620
|
+
try {
|
|
621
|
+
savePins(CONFIG.pinsFile, pins);
|
|
622
|
+
} catch (err) {
|
|
623
|
+
return endJson(res, 500, { error: String(err?.message || err) });
|
|
624
|
+
}
|
|
625
|
+
registry.setPins(pins);
|
|
626
|
+
return endJson(res, 200, { ok: true, pins });
|
|
627
|
+
}
|
|
628
|
+
|
|
542
629
|
function serveStatic(pathname, res) {
|
|
543
630
|
const rel = pathname === '/' ? 'index.html' : pathname.replace(/^\/+/, '');
|
|
544
631
|
const full = path.join(PUBLIC_DIR, rel);
|
|
@@ -547,7 +634,15 @@ function serveStatic(pathname, res) {
|
|
|
547
634
|
res.writeHead(403); return res.end('forbidden');
|
|
548
635
|
}
|
|
549
636
|
fs.readFile(full, (err, data) => {
|
|
550
|
-
if (err) {
|
|
637
|
+
if (err) {
|
|
638
|
+
// SPA fallback: a missing path with no file extension is a client-side
|
|
639
|
+
// route (e.g. /0/1/1, a deep link to a session) — serve index.html so the
|
|
640
|
+
// app boots and reads the path. Real missing assets (with an extension)
|
|
641
|
+
// still 404.
|
|
642
|
+
if (!path.extname(rel)) return serveIndexHtml(res);
|
|
643
|
+
res.writeHead(404);
|
|
644
|
+
return res.end('not found');
|
|
645
|
+
}
|
|
551
646
|
const ext = path.extname(full).toLowerCase();
|
|
552
647
|
res.writeHead(200, {
|
|
553
648
|
'content-type': MIME[ext] || 'application/octet-stream',
|
|
@@ -559,6 +654,18 @@ function serveStatic(pathname, res) {
|
|
|
559
654
|
});
|
|
560
655
|
}
|
|
561
656
|
|
|
657
|
+
// Serve the SPA shell (index.html) for client-side routes.
|
|
658
|
+
function serveIndexHtml(res) {
|
|
659
|
+
fs.readFile(path.join(PUBLIC_DIR, 'index.html'), (err, data) => {
|
|
660
|
+
if (err) { res.writeHead(404); return res.end('not found'); }
|
|
661
|
+
res.writeHead(200, {
|
|
662
|
+
'content-type': MIME['.html'],
|
|
663
|
+
'cache-control': 'no-store, must-revalidate',
|
|
664
|
+
});
|
|
665
|
+
res.end(data);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
562
669
|
// --- WebSocket --------------------------------------------------------------
|
|
563
670
|
// 1 MB cap: control messages are tiny; this prevents a single huge frame from
|
|
564
671
|
// forcing a multi-hundred-MB string allocation in the cockpit process.
|
|
@@ -685,14 +792,32 @@ function ensureSubscription(id) {
|
|
|
685
792
|
if (!session.transcriptPath) {
|
|
686
793
|
sub = { tailer: null, clients: new Set(), pending: null, ready: Promise.resolve() };
|
|
687
794
|
subscriptions.set(id, sub);
|
|
795
|
+
startPromptPoller(id, sub);
|
|
688
796
|
return sub;
|
|
689
797
|
}
|
|
690
798
|
|
|
691
799
|
const tailer = new TranscriptTailer(session.transcriptPath, { maxBuffer: CONFIG.maxBuffer });
|
|
692
|
-
|
|
800
|
+
// Watch this session's sub-agent transcripts (Task/Agent). Discovery is polled
|
|
801
|
+
// when the parent transcript grows (when sub-agents spawn) + once at subscribe.
|
|
802
|
+
const subagents = new SubAgentsWatcher(session.transcriptPath);
|
|
803
|
+
sub = { tailer, subagents, clients: new Set(), pending: null };
|
|
693
804
|
subscriptions.set(id, sub);
|
|
694
805
|
|
|
695
|
-
|
|
806
|
+
subagents.on('change', (entry) =>
|
|
807
|
+
broadcastTo(id, { type: 'subagent', id, subagent: entry }),
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
tailer.on('append', (msgs) => {
|
|
811
|
+
broadcastTo(id, { type: 'append', id, messages: msgs });
|
|
812
|
+
// A sub-agent may have just spawned (poll for its files) or finished (its
|
|
813
|
+
// Task tool-call produced a tool_result → mark it done).
|
|
814
|
+
subagents.poll();
|
|
815
|
+
for (const m of msgs) {
|
|
816
|
+
for (const b of m.blocks ?? []) {
|
|
817
|
+
if (b.kind === 'tool_result' && b.forId) subagents.markDone(b.forId);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
});
|
|
696
821
|
tailer.on('pending', (pending) => {
|
|
697
822
|
sub.pending = pending;
|
|
698
823
|
registry.setPending(id, !!pending);
|
|
@@ -701,16 +826,59 @@ function ensureSubscription(id) {
|
|
|
701
826
|
tailer.on('error', (err) => broadcastTo(id, { type: 'ack', op: 'tail', ok: false, error: String(err?.message || err) }));
|
|
702
827
|
|
|
703
828
|
// Kick off the bounded tail load once; all clients await this same promise so
|
|
704
|
-
// the initial `messages` frame never races the first read.
|
|
705
|
-
sub
|
|
829
|
+
// the initial `messages` frame never races the first read. Poll sub-agents
|
|
830
|
+
// after the initial load so an already-running sub-agent shows immediately.
|
|
831
|
+
sub.ready = tailer.start().then(() => {
|
|
832
|
+
subagents.poll();
|
|
833
|
+
// Mark already-finished sub-agents done from the EXISTING buffer: their
|
|
834
|
+
// parent tool_result arrived before subscribe and is never re-streamed, so
|
|
835
|
+
// without this they'd be stuck showing "running".
|
|
836
|
+
const doneIds = new Set();
|
|
837
|
+
for (const m of tailer.getMessages()) {
|
|
838
|
+
for (const b of m.blocks ?? []) {
|
|
839
|
+
if (b.kind === 'tool_result' && b.forId) doneIds.add(b.forId);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (doneIds.size) subagents.markDone(doneIds);
|
|
843
|
+
});
|
|
706
844
|
sub.ready.catch(() => {}); // errors surface via the per-subscribe await below
|
|
845
|
+
startPromptPoller(id, sub);
|
|
707
846
|
return sub;
|
|
708
847
|
}
|
|
709
848
|
|
|
849
|
+
// Poll the live pane for a TUI selection prompt (permission/trust/numbered menu).
|
|
850
|
+
// These never reach the transcript, so without this the cockpit shows a pending
|
|
851
|
+
// tool-call and looks stuck. Broadcasts a `prompt` frame only when it changes.
|
|
852
|
+
function startPromptPoller(id, sub) {
|
|
853
|
+
if (sub.promptTimer) return;
|
|
854
|
+
sub._lastPrompt = undefined;
|
|
855
|
+
const tick = async () => {
|
|
856
|
+
const session = sessionById(id);
|
|
857
|
+
if (!session || !tmux.isValidTarget(session.target)) return;
|
|
858
|
+
let prompt = null;
|
|
859
|
+
try {
|
|
860
|
+
const cap = await tmux.capturePane(session.target, 40);
|
|
861
|
+
prompt = parsePanePrompt(cap);
|
|
862
|
+
} catch {
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const json = prompt ? JSON.stringify(prompt) : null;
|
|
866
|
+
if (json !== sub._lastPrompt) {
|
|
867
|
+
sub._lastPrompt = json;
|
|
868
|
+
broadcastTo(id, { type: 'prompt', id, prompt });
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
sub.promptTimer = setInterval(() => tick().catch(() => {}), 2000);
|
|
872
|
+
if (sub.promptTimer.unref) sub.promptTimer.unref();
|
|
873
|
+
tick().catch(() => {});
|
|
874
|
+
}
|
|
875
|
+
|
|
710
876
|
function maybeTeardown(id) {
|
|
711
877
|
const sub = subscriptions.get(id);
|
|
712
878
|
if (sub && sub.clients.size === 0) {
|
|
713
879
|
if (sub.tailer) sub.tailer.stop();
|
|
880
|
+
if (sub.subagents) sub.subagents.stop();
|
|
881
|
+
if (sub.promptTimer) clearInterval(sub.promptTimer);
|
|
714
882
|
subscriptions.delete(id);
|
|
715
883
|
}
|
|
716
884
|
}
|
|
@@ -759,6 +927,9 @@ async function handleClientMessage(ws, msg) {
|
|
|
759
927
|
messages: sub.tailer ? sub.tailer.getMessages() : [],
|
|
760
928
|
pending: sub.tailer ? sub.tailer.getPending() : null,
|
|
761
929
|
});
|
|
930
|
+
// Snapshot any already-running sub-agents for this session.
|
|
931
|
+
const subs = sub.subagents ? sub.subagents.snapshot() : [];
|
|
932
|
+
if (subs.length) send(ws, { type: 'subagents', id: msg.id, subagents: subs });
|
|
762
933
|
return;
|
|
763
934
|
}
|
|
764
935
|
case 'unsubscribe': {
|
|
@@ -798,6 +969,20 @@ async function handleClientMessage(ws, msg) {
|
|
|
798
969
|
const text = await tmux.capturePane(session.target, lines);
|
|
799
970
|
return send(ws, { type: 'capture', id: msg.id, text });
|
|
800
971
|
}
|
|
972
|
+
case 'promptkey': {
|
|
973
|
+
// Respond to a live TUI selection prompt (permission/menu). Whitelisted
|
|
974
|
+
// keys only — never arbitrary text — so this can't be used to inject input.
|
|
975
|
+
const session = sessionById(msg.id);
|
|
976
|
+
if (!session) throw new Error('unknown session');
|
|
977
|
+
if (!tmux.isValidTarget(session.target)) throw new Error('invalid tmux target');
|
|
978
|
+
const ALLOWED = new Set(['1', '2', '3', '4', '5', '6', '7', '8', '9', 'Enter', 'Escape', 'Up', 'Down']);
|
|
979
|
+
if (!ALLOWED.has(msg.key)) throw new Error('key not allowed');
|
|
980
|
+
await tmux.sendRawKeys(session.target, [msg.key]);
|
|
981
|
+
// Force the next poll tick to broadcast (the prompt should now change/clear).
|
|
982
|
+
const sub = subscriptions.get(msg.id);
|
|
983
|
+
if (sub) sub._lastPrompt = '__force__';
|
|
984
|
+
return send(ws, { type: 'ack', op: 'promptkey', ok: true });
|
|
985
|
+
}
|
|
801
986
|
default:
|
|
802
987
|
return;
|
|
803
988
|
}
|
|
@@ -872,6 +1057,7 @@ async function runUploadSweep() {
|
|
|
872
1057
|
}
|
|
873
1058
|
|
|
874
1059
|
async function main() {
|
|
1060
|
+
registry.setPins(pins); // apply persisted pins before the first refresh
|
|
875
1061
|
registry.start();
|
|
876
1062
|
resources.start();
|
|
877
1063
|
await registry.refresh().catch(() => {});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{g as Ve}from"./index-wrjqfzbL.js";function xe(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach(t=>{const i=e[t],u=typeof i;(u==="object"||u==="function")&&!Object.isFrozen(i)&&xe(i)}),e}class he{constructor(t){t.data===void 0&&(t.data={}),this.data=t.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function we(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function B(e,...t){const i=Object.create(null);for(const u in e)i[u]=e[u];return t.forEach(function(u){for(const b in u)i[b]=u[b]}),i}const qe="</span>",pe=e=>!!e.scope,Qe=(e,{prefix:t})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const i=e.split(".");return[`${t}${i.shift()}`,...i.map((u,b)=>`${u}${"_".repeat(b+1)}`)].join(" ")}return`${t}${e}`};class me{constructor(t,i){this.buffer="",this.classPrefix=i.classPrefix,t.walk(this)}addText(t){this.buffer+=we(t)}openNode(t){if(!pe(t))return;const i=Qe(t.scope,{prefix:this.classPrefix});this.span(i)}closeNode(t){pe(t)&&(this.buffer+=qe)}value(){return this.buffer}span(t){this.buffer+=`<span class="${t}">`}}const de=(e={})=>{const t={children:[]};return Object.assign(t,e),t};class te{constructor(){this.rootNode=de(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(t){this.top.children.push(t)}openNode(t){const i=de({scope:t});this.add(i),this.stack.push(i)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(t){return this.constructor._walk(t,this.rootNode)}static _walk(t,i){return typeof i=="string"?t.addText(i):i.children&&(t.openNode(i),i.children.forEach(u=>this._walk(t,u)),t.closeNode(i)),t}static _collapse(t){typeof t!="string"&&t.children&&(t.children.every(i=>typeof i=="string")?t.children=[t.children.join("")]:t.children.forEach(i=>{te._collapse(i)}))}}class et extends te{constructor(t){super(),this.options=t}addText(t){t!==""&&this.add(t)}startScope(t){this.openNode(t)}endScope(){this.closeNode()}__addSublanguage(t,i){const u=t.root;i&&(u.scope=`language:${i}`),this.add(u)}toHTML(){return new me(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function P(e){return e?typeof e=="string"?e:e.source:null}function Oe(e){return C("(?=",e,")")}function tt(e){return C("(?:",e,")*")}function nt(e){return C("(?:",e,")?")}function C(...e){return e.map(i=>P(i)).join("")}function it(e){const t=e[e.length-1];return typeof t=="object"&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}function ne(...e){return"("+(it(e).capture?"":"?:")+e.map(u=>P(u)).join("|")+")"}function Re(e){return new RegExp(e.toString()+"|").exec("").length-1}function st(e,t){const i=e&&e.exec(t);return i&&i.index===0}const rt=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function ie(e,{joinWith:t}){let i=0;return e.map(u=>{i+=1;const b=i;let _=P(u),c="";for(;_.length>0;){const r=rt.exec(_);if(!r){c+=_;break}c+=_.substring(0,r.index),_=_.substring(r.index+r[0].length),r[0][0]==="\\"&&r[1]?c+="\\"+String(Number(r[1])+b):(c+=r[0],r[0]==="("&&i++)}return c}).map(u=>`(${u})`).join(t)}const ct=/\b\B/,ye="[a-zA-Z]\\w*",se="[a-zA-Z_]\\w*",Se="\\b\\d+(\\.\\d+)?",Ne="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",Ae="\\b(0b[01]+)",ot="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",at=(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=C(t,/.*\b/,e.binary,/\b.*/)),B({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(i,u)=>{i.index!==0&&u.ignoreMatch()}},e)},U={begin:"\\\\[\\s\\S]",relevance:0},lt={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[U]},ut={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[U]},ft={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},Y=function(e,t,i={}){const u=B({scope:"comment",begin:e,end:t,contains:[]},i);u.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const b=ne("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return u.contains.push({begin:C(/[ ]+/,"(",b,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),u},gt=Y("//","$"),ht=Y("/\\*","\\*/"),pt=Y("#","$"),dt={scope:"number",begin:Se,relevance:0},Et={scope:"number",begin:Ne,relevance:0},bt={scope:"number",begin:Ae,relevance:0},_t={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[U,{begin:/\[/,end:/\]/,relevance:0,contains:[U]}]},Mt={scope:"title",begin:ye,relevance:0},xt={scope:"title",begin:se,relevance:0},wt={begin:"\\.\\s*"+se,relevance:0},Ot=function(e){return Object.assign(e,{"on:begin":(t,i)=>{i.data._beginMatch=t[1]},"on:end":(t,i)=>{i.data._beginMatch!==t[1]&&i.ignoreMatch()}})};var z=Object.freeze({__proto__:null,APOS_STRING_MODE:lt,BACKSLASH_ESCAPE:U,BINARY_NUMBER_MODE:bt,BINARY_NUMBER_RE:Ae,COMMENT:Y,C_BLOCK_COMMENT_MODE:ht,C_LINE_COMMENT_MODE:gt,C_NUMBER_MODE:Et,C_NUMBER_RE:Ne,END_SAME_AS_BEGIN:Ot,HASH_COMMENT_MODE:pt,IDENT_RE:ye,MATCH_NOTHING_RE:ct,METHOD_GUARD:wt,NUMBER_MODE:dt,NUMBER_RE:Se,PHRASAL_WORDS_MODE:ft,QUOTE_STRING_MODE:ut,REGEXP_MODE:_t,RE_STARTERS_RE:ot,SHEBANG:at,TITLE_MODE:Mt,UNDERSCORE_IDENT_RE:se,UNDERSCORE_TITLE_MODE:xt});function Rt(e,t){e.input[e.index-1]==="."&&t.ignoreMatch()}function yt(e,t){e.className!==void 0&&(e.scope=e.className,delete e.className)}function St(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=Rt,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,e.relevance===void 0&&(e.relevance=0))}function Nt(e,t){Array.isArray(e.illegal)&&(e.illegal=ne(...e.illegal))}function At(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function kt(e,t){e.relevance===void 0&&(e.relevance=1)}const Tt=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const i=Object.assign({},e);Object.keys(e).forEach(u=>{delete e[u]}),e.keywords=i.keywords,e.begin=C(i.beforeMatch,Oe(i.begin)),e.starts={relevance:0,contains:[Object.assign(i,{endsParent:!0})]},e.relevance=0,delete i.beforeMatch},It=["of","and","for","in","not","or","if","then","parent","list","value"],Bt="keyword";function ke(e,t,i=Bt){const u=Object.create(null);return typeof e=="string"?b(i,e.split(" ")):Array.isArray(e)?b(i,e):Object.keys(e).forEach(function(_){Object.assign(u,ke(e[_],t,_))}),u;function b(_,c){t&&(c=c.map(r=>r.toLowerCase())),c.forEach(function(r){const l=r.split("|");u[l[0]]=[_,Dt(l[0],l[1])]})}}function Dt(e,t){return t?Number(t):vt(e)?0:1}function vt(e){return It.includes(e.toLowerCase())}const Ee={},v=e=>{console.error(e)},be=(e,...t)=>{console.log(`WARN: ${e}`,...t)},L=(e,t)=>{Ee[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Ee[`${e}/${t}`]=!0)},X=new Error;function Te(e,t,{key:i}){let u=0;const b=e[i],_={},c={};for(let r=1;r<=t.length;r++)c[r+u]=b[r],_[r+u]=!0,u+=Re(t[r-1]);e[i]=c,e[i]._emit=_,e[i]._multi=!0}function Ct(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw v("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),X;if(typeof e.beginScope!="object"||e.beginScope===null)throw v("beginScope must be object"),X;Te(e,e.begin,{key:"beginScope"}),e.begin=ie(e.begin,{joinWith:""})}}function Lt(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw v("skip, excludeEnd, returnEnd not compatible with endScope: {}"),X;if(typeof e.endScope!="object"||e.endScope===null)throw v("endScope must be object"),X;Te(e,e.end,{key:"endScope"}),e.end=ie(e.end,{joinWith:""})}}function Ht(e){e.scope&&typeof e.scope=="object"&&e.scope!==null&&(e.beginScope=e.scope,delete e.scope)}function jt(e){Ht(e),typeof e.beginScope=="string"&&(e.beginScope={_wrap:e.beginScope}),typeof e.endScope=="string"&&(e.endScope={_wrap:e.endScope}),Ct(e),Lt(e)}function Pt(e){function t(c,r){return new RegExp(P(c),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(r?"g":""))}class i{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(r,l){l.position=this.position++,this.matchIndexes[this.matchAt]=l,this.regexes.push([l,r]),this.matchAt+=Re(r)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const r=this.regexes.map(l=>l[1]);this.matcherRe=t(ie(r,{joinWith:"|"}),!0),this.lastIndex=0}exec(r){this.matcherRe.lastIndex=this.lastIndex;const l=this.matcherRe.exec(r);if(!l)return null;const w=l.findIndex((j,Z)=>Z>0&&j!==void 0),M=this.matchIndexes[w];return l.splice(0,w),Object.assign(l,M)}}class u{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(r){if(this.multiRegexes[r])return this.multiRegexes[r];const l=new i;return this.rules.slice(r).forEach(([w,M])=>l.addRule(w,M)),l.compile(),this.multiRegexes[r]=l,l}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(r,l){this.rules.push([r,l]),l.type==="begin"&&this.count++}exec(r){const l=this.getMatcher(this.regexIndex);l.lastIndex=this.lastIndex;let w=l.exec(r);if(this.resumingScanAtSamePosition()&&!(w&&w.index===this.lastIndex)){const M=this.getMatcher(0);M.lastIndex=this.lastIndex+1,w=M.exec(r)}return w&&(this.regexIndex+=w.position+1,this.regexIndex===this.count&&this.considerAll()),w}}function b(c){const r=new u;return c.contains.forEach(l=>r.addRule(l.begin,{rule:l,type:"begin"})),c.terminatorEnd&&r.addRule(c.terminatorEnd,{type:"end"}),c.illegal&&r.addRule(c.illegal,{type:"illegal"}),r}function _(c,r){const l=c;if(c.isCompiled)return l;[yt,At,jt,Tt].forEach(M=>M(c,r)),e.compilerExtensions.forEach(M=>M(c,r)),c.__beforeBegin=null,[St,Nt,kt].forEach(M=>M(c,r)),c.isCompiled=!0;let w=null;return typeof c.keywords=="object"&&c.keywords.$pattern&&(c.keywords=Object.assign({},c.keywords),w=c.keywords.$pattern,delete c.keywords.$pattern),w=w||/\w+/,c.keywords&&(c.keywords=ke(c.keywords,e.case_insensitive)),l.keywordPatternRe=t(w,!0),r&&(c.begin||(c.begin=/\B|\b/),l.beginRe=t(l.begin),!c.end&&!c.endsWithParent&&(c.end=/\B|\b/),c.end&&(l.endRe=t(l.end)),l.terminatorEnd=P(l.end)||"",c.endsWithParent&&r.terminatorEnd&&(l.terminatorEnd+=(c.end?"|":"")+r.terminatorEnd)),c.illegal&&(l.illegalRe=t(c.illegal)),c.contains||(c.contains=[]),c.contains=[].concat(...c.contains.map(function(M){return Ut(M==="self"?c:M)})),c.contains.forEach(function(M){_(M,l)}),c.starts&&_(c.starts,r),l.matcher=b(l),l}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=B(e.classNameAliases||{}),_(e)}function Ie(e){return e?e.endsWithParent||Ie(e.starts):!1}function Ut(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(function(t){return B(e,{variants:null},t)})),e.cachedVariants?e.cachedVariants:Ie(e)?B(e,{starts:e.starts?B(e.starts):null}):Object.isFrozen(e)?B(e):e}var $t="11.11.1";class Gt extends Error{constructor(t,i){super(t),this.name="HTMLInjectionError",this.html=i}}const ee=we,_e=B,Me=Symbol("nomatch"),Wt=7,Be=function(e){const t=Object.create(null),i=Object.create(null),u=[];let b=!0;const _="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let r={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:et};function l(n){return r.noHighlightRe.test(n)}function w(n){let a=n.className+" ";a+=n.parentNode?n.parentNode.className:"";const h=r.languageDetectRe.exec(a);if(h){const d=T(h[1]);return d||(be(_.replace("{}",h[1])),be("Falling back to no-highlight mode for this block.",n)),d?h[1]:"no-highlight"}return a.split(/\s+/).find(d=>l(d)||T(d))}function M(n,a,h){let d="",x="";typeof a=="object"?(d=n,h=a.ignoreIllegals,x=a.language):(L("10.7.0","highlight(lang, code, ...args) has been deprecated."),L("10.7.0",`Please use highlight(code, options) instead.
|
|
1
|
+
import{g as Ve}from"./index-CmkTUTz_.js";function xe(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach(t=>{const i=e[t],u=typeof i;(u==="object"||u==="function")&&!Object.isFrozen(i)&&xe(i)}),e}class he{constructor(t){t.data===void 0&&(t.data={}),this.data=t.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function we(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function B(e,...t){const i=Object.create(null);for(const u in e)i[u]=e[u];return t.forEach(function(u){for(const b in u)i[b]=u[b]}),i}const qe="</span>",pe=e=>!!e.scope,Qe=(e,{prefix:t})=>{if(e.startsWith("language:"))return e.replace("language:","language-");if(e.includes(".")){const i=e.split(".");return[`${t}${i.shift()}`,...i.map((u,b)=>`${u}${"_".repeat(b+1)}`)].join(" ")}return`${t}${e}`};class me{constructor(t,i){this.buffer="",this.classPrefix=i.classPrefix,t.walk(this)}addText(t){this.buffer+=we(t)}openNode(t){if(!pe(t))return;const i=Qe(t.scope,{prefix:this.classPrefix});this.span(i)}closeNode(t){pe(t)&&(this.buffer+=qe)}value(){return this.buffer}span(t){this.buffer+=`<span class="${t}">`}}const de=(e={})=>{const t={children:[]};return Object.assign(t,e),t};class te{constructor(){this.rootNode=de(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(t){this.top.children.push(t)}openNode(t){const i=de({scope:t});this.add(i),this.stack.push(i)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(t){return this.constructor._walk(t,this.rootNode)}static _walk(t,i){return typeof i=="string"?t.addText(i):i.children&&(t.openNode(i),i.children.forEach(u=>this._walk(t,u)),t.closeNode(i)),t}static _collapse(t){typeof t!="string"&&t.children&&(t.children.every(i=>typeof i=="string")?t.children=[t.children.join("")]:t.children.forEach(i=>{te._collapse(i)}))}}class et extends te{constructor(t){super(),this.options=t}addText(t){t!==""&&this.add(t)}startScope(t){this.openNode(t)}endScope(){this.closeNode()}__addSublanguage(t,i){const u=t.root;i&&(u.scope=`language:${i}`),this.add(u)}toHTML(){return new me(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function P(e){return e?typeof e=="string"?e:e.source:null}function Oe(e){return C("(?=",e,")")}function tt(e){return C("(?:",e,")*")}function nt(e){return C("(?:",e,")?")}function C(...e){return e.map(i=>P(i)).join("")}function it(e){const t=e[e.length-1];return typeof t=="object"&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}function ne(...e){return"("+(it(e).capture?"":"?:")+e.map(u=>P(u)).join("|")+")"}function Re(e){return new RegExp(e.toString()+"|").exec("").length-1}function st(e,t){const i=e&&e.exec(t);return i&&i.index===0}const rt=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function ie(e,{joinWith:t}){let i=0;return e.map(u=>{i+=1;const b=i;let _=P(u),c="";for(;_.length>0;){const r=rt.exec(_);if(!r){c+=_;break}c+=_.substring(0,r.index),_=_.substring(r.index+r[0].length),r[0][0]==="\\"&&r[1]?c+="\\"+String(Number(r[1])+b):(c+=r[0],r[0]==="("&&i++)}return c}).map(u=>`(${u})`).join(t)}const ct=/\b\B/,ye="[a-zA-Z]\\w*",se="[a-zA-Z_]\\w*",Se="\\b\\d+(\\.\\d+)?",Ne="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",Ae="\\b(0b[01]+)",ot="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",at=(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=C(t,/.*\b/,e.binary,/\b.*/)),B({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(i,u)=>{i.index!==0&&u.ignoreMatch()}},e)},U={begin:"\\\\[\\s\\S]",relevance:0},lt={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[U]},ut={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[U]},ft={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},Y=function(e,t,i={}){const u=B({scope:"comment",begin:e,end:t,contains:[]},i);u.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const b=ne("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return u.contains.push({begin:C(/[ ]+/,"(",b,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),u},gt=Y("//","$"),ht=Y("/\\*","\\*/"),pt=Y("#","$"),dt={scope:"number",begin:Se,relevance:0},Et={scope:"number",begin:Ne,relevance:0},bt={scope:"number",begin:Ae,relevance:0},_t={scope:"regexp",begin:/\/(?=[^/\n]*\/)/,end:/\/[gimuy]*/,contains:[U,{begin:/\[/,end:/\]/,relevance:0,contains:[U]}]},Mt={scope:"title",begin:ye,relevance:0},xt={scope:"title",begin:se,relevance:0},wt={begin:"\\.\\s*"+se,relevance:0},Ot=function(e){return Object.assign(e,{"on:begin":(t,i)=>{i.data._beginMatch=t[1]},"on:end":(t,i)=>{i.data._beginMatch!==t[1]&&i.ignoreMatch()}})};var z=Object.freeze({__proto__:null,APOS_STRING_MODE:lt,BACKSLASH_ESCAPE:U,BINARY_NUMBER_MODE:bt,BINARY_NUMBER_RE:Ae,COMMENT:Y,C_BLOCK_COMMENT_MODE:ht,C_LINE_COMMENT_MODE:gt,C_NUMBER_MODE:Et,C_NUMBER_RE:Ne,END_SAME_AS_BEGIN:Ot,HASH_COMMENT_MODE:pt,IDENT_RE:ye,MATCH_NOTHING_RE:ct,METHOD_GUARD:wt,NUMBER_MODE:dt,NUMBER_RE:Se,PHRASAL_WORDS_MODE:ft,QUOTE_STRING_MODE:ut,REGEXP_MODE:_t,RE_STARTERS_RE:ot,SHEBANG:at,TITLE_MODE:Mt,UNDERSCORE_IDENT_RE:se,UNDERSCORE_TITLE_MODE:xt});function Rt(e,t){e.input[e.index-1]==="."&&t.ignoreMatch()}function yt(e,t){e.className!==void 0&&(e.scope=e.className,delete e.className)}function St(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=Rt,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,e.relevance===void 0&&(e.relevance=0))}function Nt(e,t){Array.isArray(e.illegal)&&(e.illegal=ne(...e.illegal))}function At(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function kt(e,t){e.relevance===void 0&&(e.relevance=1)}const Tt=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const i=Object.assign({},e);Object.keys(e).forEach(u=>{delete e[u]}),e.keywords=i.keywords,e.begin=C(i.beforeMatch,Oe(i.begin)),e.starts={relevance:0,contains:[Object.assign(i,{endsParent:!0})]},e.relevance=0,delete i.beforeMatch},It=["of","and","for","in","not","or","if","then","parent","list","value"],Bt="keyword";function ke(e,t,i=Bt){const u=Object.create(null);return typeof e=="string"?b(i,e.split(" ")):Array.isArray(e)?b(i,e):Object.keys(e).forEach(function(_){Object.assign(u,ke(e[_],t,_))}),u;function b(_,c){t&&(c=c.map(r=>r.toLowerCase())),c.forEach(function(r){const l=r.split("|");u[l[0]]=[_,Dt(l[0],l[1])]})}}function Dt(e,t){return t?Number(t):vt(e)?0:1}function vt(e){return It.includes(e.toLowerCase())}const Ee={},v=e=>{console.error(e)},be=(e,...t)=>{console.log(`WARN: ${e}`,...t)},L=(e,t)=>{Ee[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Ee[`${e}/${t}`]=!0)},X=new Error;function Te(e,t,{key:i}){let u=0;const b=e[i],_={},c={};for(let r=1;r<=t.length;r++)c[r+u]=b[r],_[r+u]=!0,u+=Re(t[r-1]);e[i]=c,e[i]._emit=_,e[i]._multi=!0}function Ct(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw v("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),X;if(typeof e.beginScope!="object"||e.beginScope===null)throw v("beginScope must be object"),X;Te(e,e.begin,{key:"beginScope"}),e.begin=ie(e.begin,{joinWith:""})}}function Lt(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw v("skip, excludeEnd, returnEnd not compatible with endScope: {}"),X;if(typeof e.endScope!="object"||e.endScope===null)throw v("endScope must be object"),X;Te(e,e.end,{key:"endScope"}),e.end=ie(e.end,{joinWith:""})}}function Ht(e){e.scope&&typeof e.scope=="object"&&e.scope!==null&&(e.beginScope=e.scope,delete e.scope)}function jt(e){Ht(e),typeof e.beginScope=="string"&&(e.beginScope={_wrap:e.beginScope}),typeof e.endScope=="string"&&(e.endScope={_wrap:e.endScope}),Ct(e),Lt(e)}function Pt(e){function t(c,r){return new RegExp(P(c),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(r?"g":""))}class i{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(r,l){l.position=this.position++,this.matchIndexes[this.matchAt]=l,this.regexes.push([l,r]),this.matchAt+=Re(r)+1}compile(){this.regexes.length===0&&(this.exec=()=>null);const r=this.regexes.map(l=>l[1]);this.matcherRe=t(ie(r,{joinWith:"|"}),!0),this.lastIndex=0}exec(r){this.matcherRe.lastIndex=this.lastIndex;const l=this.matcherRe.exec(r);if(!l)return null;const w=l.findIndex((j,Z)=>Z>0&&j!==void 0),M=this.matchIndexes[w];return l.splice(0,w),Object.assign(l,M)}}class u{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(r){if(this.multiRegexes[r])return this.multiRegexes[r];const l=new i;return this.rules.slice(r).forEach(([w,M])=>l.addRule(w,M)),l.compile(),this.multiRegexes[r]=l,l}resumingScanAtSamePosition(){return this.regexIndex!==0}considerAll(){this.regexIndex=0}addRule(r,l){this.rules.push([r,l]),l.type==="begin"&&this.count++}exec(r){const l=this.getMatcher(this.regexIndex);l.lastIndex=this.lastIndex;let w=l.exec(r);if(this.resumingScanAtSamePosition()&&!(w&&w.index===this.lastIndex)){const M=this.getMatcher(0);M.lastIndex=this.lastIndex+1,w=M.exec(r)}return w&&(this.regexIndex+=w.position+1,this.regexIndex===this.count&&this.considerAll()),w}}function b(c){const r=new u;return c.contains.forEach(l=>r.addRule(l.begin,{rule:l,type:"begin"})),c.terminatorEnd&&r.addRule(c.terminatorEnd,{type:"end"}),c.illegal&&r.addRule(c.illegal,{type:"illegal"}),r}function _(c,r){const l=c;if(c.isCompiled)return l;[yt,At,jt,Tt].forEach(M=>M(c,r)),e.compilerExtensions.forEach(M=>M(c,r)),c.__beforeBegin=null,[St,Nt,kt].forEach(M=>M(c,r)),c.isCompiled=!0;let w=null;return typeof c.keywords=="object"&&c.keywords.$pattern&&(c.keywords=Object.assign({},c.keywords),w=c.keywords.$pattern,delete c.keywords.$pattern),w=w||/\w+/,c.keywords&&(c.keywords=ke(c.keywords,e.case_insensitive)),l.keywordPatternRe=t(w,!0),r&&(c.begin||(c.begin=/\B|\b/),l.beginRe=t(l.begin),!c.end&&!c.endsWithParent&&(c.end=/\B|\b/),c.end&&(l.endRe=t(l.end)),l.terminatorEnd=P(l.end)||"",c.endsWithParent&&r.terminatorEnd&&(l.terminatorEnd+=(c.end?"|":"")+r.terminatorEnd)),c.illegal&&(l.illegalRe=t(c.illegal)),c.contains||(c.contains=[]),c.contains=[].concat(...c.contains.map(function(M){return Ut(M==="self"?c:M)})),c.contains.forEach(function(M){_(M,l)}),c.starts&&_(c.starts,r),l.matcher=b(l),l}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=B(e.classNameAliases||{}),_(e)}function Ie(e){return e?e.endsWithParent||Ie(e.starts):!1}function Ut(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(function(t){return B(e,{variants:null},t)})),e.cachedVariants?e.cachedVariants:Ie(e)?B(e,{starts:e.starts?B(e.starts):null}):Object.isFrozen(e)?B(e):e}var $t="11.11.1";class Gt extends Error{constructor(t,i){super(t),this.name="HTMLInjectionError",this.html=i}}const ee=we,_e=B,Me=Symbol("nomatch"),Wt=7,Be=function(e){const t=Object.create(null),i=Object.create(null),u=[];let b=!0;const _="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let r={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:et};function l(n){return r.noHighlightRe.test(n)}function w(n){let a=n.className+" ";a+=n.parentNode?n.parentNode.className:"";const h=r.languageDetectRe.exec(a);if(h){const d=T(h[1]);return d||(be(_.replace("{}",h[1])),be("Falling back to no-highlight mode for this block.",n)),d?h[1]:"no-highlight"}return a.split(/\s+/).find(d=>l(d)||T(d))}function M(n,a,h){let d="",x="";typeof a=="object"?(d=n,h=a.ignoreIllegals,x=a.language):(L("10.7.0","highlight(lang, code, ...args) has been deprecated."),L("10.7.0",`Please use highlight(code, options) instead.
|
|
2
2
|
https://github.com/highlightjs/highlight.js/issues/2277`),x=n,d=a),h===void 0&&(h=!0);const S={code:d,language:x};G("before:highlight",S);const I=S.result?S.result:j(S.language,S.code,h);return I.code=S.code,G("after:highlight",I),I}function j(n,a,h,d){const x=Object.create(null);function S(s,o){return s.keywords[o]}function I(){if(!f.keywords){O.addText(E);return}let s=0;f.keywordPatternRe.lastIndex=0;let o=f.keywordPatternRe.exec(E),g="";for(;o;){g+=E.substring(s,o.index);const p=A.case_insensitive?o[0].toLowerCase():o[0],R=S(f,p);if(R){const[k,Ze]=R;if(O.addText(g),g="",x[p]=(x[p]||0)+1,x[p]<=Wt&&(F+=Ze),k.startsWith("_"))g+=o[0];else{const Je=A.classNameAliases[k]||k;N(o[0],Je)}}else g+=o[0];s=f.keywordPatternRe.lastIndex,o=f.keywordPatternRe.exec(E)}g+=E.substring(s),O.addText(g)}function W(){if(E==="")return;let s=null;if(typeof f.subLanguage=="string"){if(!t[f.subLanguage]){O.addText(E);return}s=j(f.subLanguage,E,!0,ge[f.subLanguage]),ge[f.subLanguage]=s._top}else s=J(E,f.subLanguage.length?f.subLanguage:null);f.relevance>0&&(F+=s.relevance),O.__addSublanguage(s._emitter,s.language)}function y(){f.subLanguage!=null?W():I(),E=""}function N(s,o){s!==""&&(O.startScope(o),O.addText(s),O.endScope())}function ae(s,o){let g=1;const p=o.length-1;for(;g<=p;){if(!s._emit[g]){g++;continue}const R=A.classNameAliases[s[g]]||s[g],k=o[g];R?N(k,R):(E=k,I(),E=""),g++}}function le(s,o){return s.scope&&typeof s.scope=="string"&&O.openNode(A.classNameAliases[s.scope]||s.scope),s.beginScope&&(s.beginScope._wrap?(N(E,A.classNameAliases[s.beginScope._wrap]||s.beginScope._wrap),E=""):s.beginScope._multi&&(ae(s.beginScope,o),E="")),f=Object.create(s,{parent:{value:f}}),f}function ue(s,o,g){let p=st(s.endRe,g);if(p){if(s["on:end"]){const R=new he(s);s["on:end"](o,R),R.isMatchIgnored&&(p=!1)}if(p){for(;s.endsParent&&s.parent;)s=s.parent;return s}}if(s.endsWithParent)return ue(s.parent,o,g)}function Ke(s){return f.matcher.regexIndex===0?(E+=s[0],1):(m=!0,0)}function Fe(s){const o=s[0],g=s.rule,p=new he(g),R=[g.__beforeBegin,g["on:begin"]];for(const k of R)if(k&&(k(s,p),p.isMatchIgnored))return Ke(o);return g.skip?E+=o:(g.excludeBegin&&(E+=o),y(),!g.returnBegin&&!g.excludeBegin&&(E=o)),le(g,s),g.returnBegin?0:o.length}function ze(s){const o=s[0],g=a.substring(s.index),p=ue(f,s,g);if(!p)return Me;const R=f;f.endScope&&f.endScope._wrap?(y(),N(o,f.endScope._wrap)):f.endScope&&f.endScope._multi?(y(),ae(f.endScope,s)):R.skip?E+=o:(R.returnEnd||R.excludeEnd||(E+=o),y(),R.excludeEnd&&(E=o));do f.scope&&O.closeNode(),!f.skip&&!f.subLanguage&&(F+=f.relevance),f=f.parent;while(f!==p.parent);return p.starts&&le(p.starts,s),R.returnEnd?0:o.length}function Xe(){const s=[];for(let o=f;o!==A;o=o.parent)o.scope&&s.unshift(o.scope);s.forEach(o=>O.openNode(o))}let K={};function fe(s,o){const g=o&&o[0];if(E+=s,g==null)return y(),0;if(K.type==="begin"&&o.type==="end"&&K.index===o.index&&g===""){if(E+=a.slice(o.index,o.index+1),!b){const p=new Error(`0 width match regex (${n})`);throw p.languageName=n,p.badRule=K.rule,p}return 1}if(K=o,o.type==="begin")return Fe(o);if(o.type==="illegal"&&!h){const p=new Error('Illegal lexeme "'+g+'" for mode "'+(f.scope||"<unnamed>")+'"');throw p.mode=f,p}else if(o.type==="end"){const p=ze(o);if(p!==Me)return p}if(o.type==="illegal"&&g==="")return E+=`
|
|
3
3
|
`,1;if(Q>1e5&&Q>o.index*3)throw new Error("potential infinite loop, way more iterations than matches");return E+=g,g.length}const A=T(n);if(!A)throw v(_.replace("{}",n)),new Error('Unknown language: "'+n+'"');const Ye=Pt(A);let q="",f=d||Ye;const ge={},O=new r.__emitter(r);Xe();let E="",F=0,D=0,Q=0,m=!1;try{if(A.__emitTokens)A.__emitTokens(a,O);else{for(f.matcher.considerAll();;){Q++,m?m=!1:f.matcher.considerAll(),f.matcher.lastIndex=D;const s=f.matcher.exec(a);if(!s)break;const o=a.substring(D,s.index),g=fe(o,s);D=s.index+g}fe(a.substring(D))}return O.finalize(),q=O.toHTML(),{language:n,value:q,relevance:F,illegal:!1,_emitter:O,_top:f}}catch(s){if(s.message&&s.message.includes("Illegal"))return{language:n,value:ee(a),illegal:!0,relevance:0,_illegalBy:{message:s.message,index:D,context:a.slice(D-100,D+100),mode:s.mode,resultSoFar:q},_emitter:O};if(b)return{language:n,value:ee(a),illegal:!1,relevance:0,errorRaised:s,_emitter:O,_top:f};throw s}}function Z(n){const a={value:ee(n),illegal:!1,relevance:0,_top:c,_emitter:new r.__emitter(r)};return a._emitter.addText(n),a}function J(n,a){a=a||r.languages||Object.keys(t);const h=Z(n),d=a.filter(T).filter(oe).map(y=>j(y,n,!1));d.unshift(h);const x=d.sort((y,N)=>{if(y.relevance!==N.relevance)return N.relevance-y.relevance;if(y.language&&N.language){if(T(y.language).supersetOf===N.language)return 1;if(T(N.language).supersetOf===y.language)return-1}return 0}),[S,I]=x,W=S;return W.secondBest=I,W}function De(n,a,h){const d=a&&i[a]||h;n.classList.add("hljs"),n.classList.add(`language-${d}`)}function V(n){let a=null;const h=w(n);if(l(h))return;if(G("before:highlightElement",{el:n,language:h}),n.dataset.highlighted){console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",n);return}if(n.children.length>0&&(r.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),console.warn("The element with unescaped HTML:"),console.warn(n)),r.throwUnescapedHTML))throw new Gt("One of your code blocks includes unescaped HTML.",n.innerHTML);a=n;const d=a.textContent,x=h?M(d,{language:h,ignoreIllegals:!0}):J(d);n.innerHTML=x.value,n.dataset.highlighted="yes",De(n,h,x.language),n.result={language:x.language,re:x.relevance,relevance:x.relevance},x.secondBest&&(n.secondBest={language:x.secondBest.language,relevance:x.secondBest.relevance}),G("after:highlightElement",{el:n,result:x,text:d})}function ve(n){r=_e(r,n)}const Ce=()=>{$(),L("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")};function Le(){$(),L("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")}let re=!1;function $(){function n(){$()}if(document.readyState==="loading"){re||window.addEventListener("DOMContentLoaded",n,!1),re=!0;return}document.querySelectorAll(r.cssSelector).forEach(V)}function He(n,a){let h=null;try{h=a(e)}catch(d){if(v("Language definition for '{}' could not be registered.".replace("{}",n)),b)v(d);else throw d;h=c}h.name||(h.name=n),t[n]=h,h.rawDefinition=a.bind(null,e),h.aliases&&ce(h.aliases,{languageName:n})}function je(n){delete t[n];for(const a of Object.keys(i))i[a]===n&&delete i[a]}function Pe(){return Object.keys(t)}function T(n){return n=(n||"").toLowerCase(),t[n]||t[i[n]]}function ce(n,{languageName:a}){typeof n=="string"&&(n=[n]),n.forEach(h=>{i[h.toLowerCase()]=a})}function oe(n){const a=T(n);return a&&!a.disableAutodetect}function Ue(n){n["before:highlightBlock"]&&!n["before:highlightElement"]&&(n["before:highlightElement"]=a=>{n["before:highlightBlock"](Object.assign({block:a.el},a))}),n["after:highlightBlock"]&&!n["after:highlightElement"]&&(n["after:highlightElement"]=a=>{n["after:highlightBlock"](Object.assign({block:a.el},a))})}function $e(n){Ue(n),u.push(n)}function Ge(n){const a=u.indexOf(n);a!==-1&&u.splice(a,1)}function G(n,a){const h=n;u.forEach(function(d){d[h]&&d[h](a)})}function We(n){return L("10.7.0","highlightBlock will be removed entirely in v12.0"),L("10.7.0","Please use highlightElement now."),V(n)}Object.assign(e,{highlight:M,highlightAuto:J,highlightAll:$,highlightElement:V,highlightBlock:We,configure:ve,initHighlighting:Ce,initHighlightingOnLoad:Le,registerLanguage:He,unregisterLanguage:je,listLanguages:Pe,getLanguage:T,registerAliases:ce,autoDetection:oe,inherit:_e,addPlugin:$e,removePlugin:Ge}),e.debugMode=function(){b=!1},e.safeMode=function(){b=!0},e.versionString=$t,e.regex={concat:C,lookahead:Oe,either:ne,optional:nt,anyNumberOfTimes:tt};for(const n in z)typeof z[n]=="object"&&xe(z[n]);return Object.assign(e,z),e},H=Be({});H.newInstance=()=>Be({});var Kt=H;H.HighlightJS=H;H.default=H;const zt=Ve(Kt);export{zt as HighlightJS,zt as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--bg: oklch(16% .012 260);--bg-1: oklch(20% .014 260);--bg-2: oklch(24% .016 260);--bg-hover: oklch(27% .02 260);--line: oklch(32% .015 260);--text: oklch(92% .01 250);--text-dim: oklch(66% .012 250);--text-faint: oklch(52% .012 250);--accent: oklch(70% .15 250);--accent-2: oklch(74% .13 200);--amber: oklch(80% .16 80);--amber-bg: oklch(40% .1 80);--red: oklch(64% .2 25);--red-bg: oklch(30% .09 25);--green: oklch(72% .16 150);--user: oklch(34% .06 250);--txt-transcript: 12.5px;--txt-meta: 11px;--txt-composer: 16px;--hud-h: 34px;--radius: 10px;--ease: cubic-bezier(.16, 1, .3, 1);color-scheme:dark}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg);color:var(--text);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:var(--txt-transcript);line-height:1.45;-webkit-font-smoothing:antialiased;overscroll-behavior:none}.app{display:flex;flex-direction:column;height:100dvh}.hud{display:flex;align-items:center;gap:12px;height:var(--hud-h);padding:0 12px;padding-top:env(safe-area-inset-top);background:var(--bg-1);border-bottom:1px solid var(--line);font-size:var(--txt-meta);color:var(--text-dim);white-space:nowrap;overflow-x:auto;flex:0 0 auto;box-sizing:content-box}.hud[data-warn=true]{background:var(--amber-bg);color:var(--text)}.hud-brand{display:inline-flex;align-items:center;gap:6px;font-weight:700;color:var(--text);letter-spacing:.02em;flex:0 0 auto}.hud-group{display:inline-flex;gap:4px;align-items:baseline}.hud-k{color:var(--text-faint)}.hud-v{color:var(--text);font-variant-numeric:tabular-nums}.hud-warn-text{color:var(--amber);font-weight:600}.conn-dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto;background:var(--text-faint)}.conn-connected{background:var(--green)}.conn-connecting{background:var(--amber)}.conn-disconnected{background:var(--red)}.app-body{flex:1 1 auto;display:flex;min-height:0}.rail{width:100%;overflow-y:auto;background:var(--bg);-webkit-overflow-scrolling:touch}.detail{display:none;flex-direction:column;min-width:0;width:100%}.app[data-detail=open] .rail{display:none}.app[data-detail=open] .detail{display:flex}@media (min-width: 760px){.rail{width:300px;flex:0 0 300px;border-right:1px solid var(--line)}.detail,.app[data-detail=open] .detail{display:flex}.app[data-detail=open] .rail,.app[data-detail=closed] .rail{display:block}.back-btn{display:none!important}}.session-list{list-style:none;margin:0;padding:6px;display:flex;flex-direction:column;gap:4px}.session-item{padding:9px 11px;border-radius:var(--radius);border:1px solid transparent;cursor:pointer;background:var(--bg-1);transition:background var(--ease) .15s,border-color var(--ease) .15s}.session-item:hover{background:var(--bg-hover)}.session-item:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.session-item[data-selected=true]{border-color:var(--accent);background:var(--bg-2)}.session-top{display:flex;align-items:center;gap:7px}.active-dot{width:7px;height:7px;border-radius:50%;flex:0 0 auto;background:var(--text-faint)}.active-dot[data-on=true]{background:var(--green);box-shadow:0 0 6px var(--green)}.session-name{font-size:13px;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto}.ask-badge{flex:0 0 auto;font-size:9.5px;font-weight:800;letter-spacing:.06em;padding:2px 6px;border-radius:6px;background:var(--amber-bg);color:var(--amber);animation:pulse 1.6s var(--ease) infinite}@keyframes pulse{50%{opacity:.55}}.session-meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px;font-size:var(--txt-meta);color:var(--text-dim);align-items:center}.meta-prov{font-variant-numeric:tabular-nums;color:var(--text-faint)}.meta-model{color:var(--accent-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:60%}.meta-ctx{font-variant-numeric:tabular-nums;padding:1px 5px;border-radius:5px;background:var(--bg-2);color:var(--text-dim)}.session-empty{list-style:none;padding:16px;color:var(--text-faint);text-align:center}.detail-head{display:flex;align-items:center;gap:8px;padding:8px 10px;border-bottom:1px solid var(--line);background:var(--bg-1);flex:0 0 auto}.back-btn{font-size:22px;line-height:1;background:none;border:none;color:var(--accent);padding:2px 8px;cursor:pointer;flex:0 0 auto}.detail-title{display:flex;flex-direction:column;min-width:0}.detail-name{font-weight:700;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-cwd{font-size:var(--txt-meta);color:var(--text-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.detail-name-row{display:flex;align-items:center;gap:6px;min-width:0}.rename-btn{flex:0 0 auto;background:none;border:none;color:var(--text-faint);cursor:pointer;font-size:12px;line-height:1;padding:2px 4px;border-radius:4px}.rename-btn:hover{color:var(--text);background:var(--bg-hover)}.detail-rename-input{width:100%;font-weight:700;font-size:13px;color:var(--text);background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:3px 7px;outline:none}.detail-rename-input:focus{border-color:var(--accent)}.live-pane{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;padding:12px 12px 6px}.live-pane-head{flex:0 0 auto;font-size:var(--txt-meta);color:var(--text-faint);margin-bottom:6px}.live-pane-body{flex:1 1 auto;margin:0;padding:10px;border-radius:8px;background:var(--bg);border:1px solid var(--line);color:var(--text);font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:var(--txt-transcript);white-space:pre-wrap;overflow-wrap:anywhere;overflow:auto;-webkit-overflow-scrolling:touch}.thread-root{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}.thread-viewport{flex:1 1 auto;overflow-y:auto;padding:12px 12px 6px;display:flex;flex-direction:column;gap:10px;-webkit-overflow-scrolling:touch}.thread-empty{margin:auto;color:var(--text-faint);text-align:center}.load-earlier{align-self:center;flex:0 0 auto;margin:2px 0 4px;padding:5px 12px;font:inherit;font-size:11.5px;color:var(--text-dim);background:var(--bg-2);border:1px solid var(--line);border-radius:999px;cursor:pointer;transition:background .12s ease,color .12s ease}.load-earlier:hover{background:var(--bg-hover);color:var(--text)}.load-earlier:active{background:var(--line)}.msg-row{display:flex;flex-direction:column;gap:3px;max-width:92%}.msg-row[data-role=user]{align-self:flex-end;align-items:flex-end}.msg-role{font-size:9.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-faint);padding:0 2px}.msg-body{display:flex;flex-direction:column;gap:6px;background:var(--bg-1);border:1px solid var(--line);border-radius:var(--radius);padding:8px 10px;font-size:var(--txt-transcript)}.msg-row[data-role=user] .msg-body{background:var(--user);border-color:transparent}.msg-row[data-role=system] .msg-body{background:transparent;border-style:dashed;color:var(--text-dim)}.msg-row[data-queued=true]{opacity:.62}.msg-row[data-queued=true] .msg-body{border:1px dashed var(--line);background:transparent}.msg-row[data-queued=true] .msg-role{color:var(--accent, var(--text-dim))}.transcript-img{max-width:min(320px,100%);max-height:240px;border-radius:var(--radius);border:1px solid var(--line);cursor:zoom-in;display:block;object-fit:contain}.lightbox{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;background:#000000d1;padding:24px;cursor:zoom-out}.lightbox-img{max-width:100%;max-height:100%;border-radius:var(--radius);box-shadow:0 10px 40px #00000080;cursor:default}.lightbox-close{position:absolute;top:16px;right:20px;width:40px;height:40px;border-radius:50%;border:1px solid var(--line);background:var(--bg-1);color:var(--text);font-size:22px;line-height:1;cursor:pointer}.login{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:var(--bg, #0b0d10);padding:24px}.login-card{display:flex;flex-direction:column;gap:10px;width:min(340px,88vw)}.login-title{margin:0;font-size:18px;font-weight:700;letter-spacing:.02em}.login-sub{margin:0;font-size:13px;color:var(--text-dim)}.login-input{padding:12px 14px;border-radius:10px;border:1px solid var(--line);background:var(--bg-1);color:var(--text);font-size:16px}.login-err{min-height:16px;font-size:12px;color:#f87171}.login-btn{padding:12px 14px;border-radius:10px;border:0;background:var(--accent, #3b82f6);color:#fff;font-size:15px;font-weight:600;cursor:pointer}.login-btn:disabled{opacity:.55}.hud-logout{margin-left:8px;border:1px solid var(--line);background:transparent;color:var(--text-dim);border-radius:6px;padding:2px 7px;font-size:13px;cursor:pointer}.pin-modal{display:flex;flex-direction:column;gap:10px;max-height:80vh}.pin-search{width:100%;box-sizing:border-box;padding:10px 12px;border-radius:8px;border:1px solid var(--line);background:var(--bg-1);color:var(--text);font-size:14px}.pin-unpin{padding:8px 10px;border-radius:8px;cursor:pointer;border:1px solid var(--line);background:var(--bg-1);color:var(--text-dim);font-size:12px}.pin-list{overflow-y:auto;display:flex;flex-direction:column;gap:4px}.pin-empty{color:var(--text-faint);font-size:12px;padding:16px;text-align:center}.pin-row{display:flex;flex-direction:column;gap:2px;text-align:left;padding:8px 10px;border-radius:8px;cursor:pointer;border:1px solid var(--line);background:var(--bg-1);color:var(--text)}.pin-row:hover,.pin-row[data-current=true]{border-color:var(--accent, #3b82f6)}.pin-row-title{font-size:13px;font-weight:600}.pin-row-cur{color:var(--accent, #3b82f6);font-weight:400}.pin-row-meta{display:flex;gap:8px;font-size:11px;color:var(--text-faint)}.pin-row-cwd{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pin-row-when{margin-left:auto;white-space:nowrap}.pin-toggle{margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:30px;height:28px;border-radius:8px;border:1px solid var(--line);background:var(--bg-1);font-size:14px;cursor:pointer}.pin-toggle[aria-pressed=true]{border-color:var(--accent, #3b82f6)}.pin-toggle+.subagents-toggle{margin-left:6px}.subagents-toggle{margin-left:auto;display:inline-flex;align-items:center;gap:6px;padding:5px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg-1);color:var(--text-dim);font-size:12px;cursor:pointer}.subagents-toggle[aria-pressed=true]{color:var(--text);border-color:var(--accent, #3b82f6)}.subagents-badge{min-width:16px;padding:0 5px;border-radius:8px;background:var(--accent, #3b82f6);color:#fff;font-size:11px;text-align:center}.sa-panel{position:fixed;top:0;right:0;bottom:0;z-index:900;width:min(440px,92vw);display:flex;flex-direction:column;background:var(--bg);border-left:1px solid var(--line);box-shadow:-12px 0 40px #0006}.sa-panel-head{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--line)}.sa-panel-title{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600}.sa-count{padding:0 6px;border-radius:8px;background:var(--bg-1);color:var(--text-dim);font-size:11px}.sa-count-running{color:var(--accent, #3b82f6);font-size:11px}.sa-panel-close{margin-left:auto;width:30px;height:30px;border-radius:8px;border:1px solid var(--line);background:var(--bg-1);color:var(--text);font-size:18px;cursor:pointer}.sa-panel-body{flex:1 1 auto;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:8px}.sa-empty{color:var(--text-faint);font-size:12px;padding:12px}.sa-item{border:1px solid var(--line);border-radius:var(--radius);margin-bottom:8px;background:var(--bg-1)}.sa-summary{display:flex;align-items:center;gap:6px;padding:8px 10px;cursor:pointer;font-size:12px}.sa-dot{width:7px;height:7px;border-radius:50%;background:var(--text-faint);flex:0 0 auto}.sa-dot[data-status=running]{background:var(--accent, #3b82f6)}.sa-type{font-weight:600}.sa-desc{color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sa-status{margin-left:auto;color:var(--text-faint);white-space:nowrap}.sa-transcript{padding:6px 10px 10px;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:8px}.sa-msg{display:flex;flex-direction:column;gap:3px}.sa-msg-role{font-size:9px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-faint)}.sa-text{font-size:12.5px;white-space:pre-wrap;word-break:break-word}.sa-think{font-size:11.5px;color:var(--text-dim)}.sa-think-body{white-space:pre-wrap;color:var(--text-faint)}.sa-tool{font-size:11.5px;font-family:ui-monospace,monospace;color:var(--text-dim)}.sa-tool-arg{color:var(--text-faint)}.sa-result{font-size:11px;font-family:ui-monospace,monospace;white-space:pre-wrap;word-break:break-word;color:var(--text-faint);border-left:2px solid var(--line);padding-left:8px}.sa-result[data-error=true]{color:#f87171;border-left-color:#f87171}.aui-md{font-size:var(--txt-transcript);line-height:1.5;overflow-wrap:anywhere}.aui-md>:first-child{margin-top:0}.aui-md>:last-child{margin-bottom:0}.aui-md p{margin:0 0 8px}.aui-md h1,.aui-md h2,.aui-md h3,.aui-md h4,.aui-md h5,.aui-md h6{margin:14px 0 6px;line-height:1.3;font-weight:700}.aui-md h1{font-size:17px}.aui-md h2{font-size:15.5px}.aui-md h3{font-size:14px}.aui-md h4,.aui-md h5,.aui-md h6{font-size:13px;color:var(--text-dim)}.aui-md a{color:var(--accent);text-decoration:underline;text-underline-offset:2px}.aui-md strong{font-weight:700}.aui-md em{font-style:italic}.aui-md del{opacity:.7}.aui-md ul,.aui-md ol{margin:0 0 8px;padding-left:1.4em}.aui-md li{margin:2px 0}.aui-md li>ul,.aui-md li>ol{margin:2px 0}.aui-md li input[type=checkbox]{margin-right:6px;vertical-align:middle}.aui-md blockquote{margin:0 0 8px;padding:2px 10px;border-left:3px solid var(--line);color:var(--text-dim)}.aui-md hr{border:none;border-top:1px solid var(--line);margin:12px 0}.aui-md :not(pre)>code{font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:.88em;background:var(--bg-2);border:1px solid var(--line);border-radius:5px;padding:1px 5px;overflow-wrap:anywhere}.aui-md pre{margin:0 0 8px;background:var(--bg);border:1px solid var(--line);border-radius:8px;overflow:hidden}.aui-md pre code{display:block;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11.5px;line-height:1.5;color:var(--text);padding:8px 10px;overflow-x:auto;white-space:pre;-webkit-overflow-scrolling:touch}.aui-md-code-lang{font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:9.5px;text-transform:lowercase;letter-spacing:.04em;color:var(--text-faint);background:var(--bg-2);border-bottom:1px solid var(--line);padding:3px 10px}.aui-md table{display:block;width:max-content;max-width:100%;overflow-x:auto;border-collapse:collapse;margin:0 0 8px;font-size:11.5px;-webkit-overflow-scrolling:touch}.aui-md th,.aui-md td{border:1px solid var(--line);padding:4px 8px;text-align:left;vertical-align:top}.aui-md th{background:var(--bg-2);font-weight:700}.aui-md img{max-width:100%;height:auto;border-radius:6px}.block-thinking{font-size:11.5px;color:var(--text-dim)}.block-thinking>summary{cursor:pointer;color:var(--text-faint);font-style:italic;list-style:none}.block-thinking>summary:before{content:"▸ "}.block-thinking[open]>summary:before{content:"▾ "}.thinking-text{margin-top:4px;padding-left:8px;border-left:2px solid var(--line);white-space:pre-wrap;overflow-wrap:anywhere;opacity:.8}.block-tool{display:flex;flex-direction:column;gap:4px}.block-tool-use{display:flex;align-items:center;gap:6px;min-width:0;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11.5px;color:var(--text-dim);background:var(--bg-2);border-radius:6px;padding:4px 8px;list-style:none;cursor:default}details.block-tool>summary.block-tool-use{cursor:pointer}.block-tool-use::-webkit-details-marker{display:none}.tool-head{display:flex;align-items:center;gap:5px;min-width:0;flex:1 1 auto;overflow:hidden}.tool-arrow{color:var(--accent-2);flex:0 0 auto;transition:transform var(--ease) .15s}details.block-tool[open]>summary .tool-arrow{transform:rotate(90deg)}.tool-name{color:var(--accent-2);font-weight:600;white-space:nowrap;flex:0 0 auto}.tool-sep{color:var(--text-faint);flex:0 0 auto}.tool-input{color:var(--text-dim);min-width:0;flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.block-tool-body{display:flex;flex-direction:column;gap:4px}.block-tool-args{margin:0;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11px;color:var(--text-dim);background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:6px 8px;white-space:pre;overflow-x:auto;max-height:220px;overflow-y:auto;-webkit-overflow-scrolling:touch}.block-tool-result{font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11px;color:var(--text-dim);border-left:2px solid var(--line);padding:4px 8px;white-space:pre-wrap;overflow-wrap:anywhere;max-height:220px;overflow-y:auto;-webkit-overflow-scrolling:touch}.block-tool-result[data-error=true]{border-left-color:var(--red);background:var(--red-bg);color:var(--text)}.composer{display:flex;flex-direction:column;gap:6px;padding:8px;padding-bottom:calc(8px + env(safe-area-inset-bottom));border-top:1px solid var(--line);background:var(--bg-1);flex:0 0 auto}.composer-row{display:flex;align-items:flex-end;gap:6px}.composer-attachments{display:flex;flex-wrap:wrap;gap:6px}.composer-attachments:empty{display:none}.attach-chip{display:inline-flex;align-items:center;gap:6px;max-width:220px;padding:4px 4px 4px 8px;border-radius:8px;border:1px solid var(--line);background:var(--bg-2);font-size:11.5px;color:var(--text)}.attach-chip[data-pending=true]{opacity:.85;border-style:dashed}.chip-thumb{width:26px;height:26px;border-radius:5px;object-fit:cover;flex:0 0 auto;background:var(--bg)}.chip-thumb-empty{border:1px solid var(--line)}.chip-icon{font-size:14px;flex:0 0 auto}.chip-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chip-spinner{width:11px;height:11px;flex:0 0 auto;border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:chip-spin .7s linear infinite}@keyframes chip-spin{to{transform:rotate(360deg)}}.chip-remove{flex:0 0 auto;width:20px;height:20px;border:none;border-radius:6px;background:transparent;color:var(--text-dim);font-size:15px;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.chip-remove:hover{background:var(--bg-hover);color:var(--text)}.composer-attach,.composer-send{flex:0 0 auto;height:38px;min-width:38px;border-radius:9px;border:1px solid var(--line);background:var(--bg-2);color:var(--text);font-size:17px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background var(--ease) .15s}.composer-attach:hover,.composer-send:hover{background:var(--bg-hover)}.composer-send{background:var(--accent);color:#0c121a;border-color:transparent;font-weight:700}.composer-attach:disabled,.composer-send:disabled{opacity:.45;cursor:not-allowed}.composer-input{flex:1 1 auto;resize:none;max-height:40dvh;min-height:38px;padding:9px 11px;border-radius:9px;border:1px solid var(--line);background:var(--bg);color:var(--text);font:inherit;font-size:var(--txt-composer);line-height:1.4}.composer-input:focus{outline:none;border-color:var(--accent)}.composer-input::placeholder{color:var(--text-faint)}.modal-backdrop{position:fixed;inset:0;background:#01020399;backdrop-filter:blur(2px);display:flex;align-items:flex-end;justify-content:center;z-index:50;padding:0}@media (min-width: 760px){.modal-backdrop{align-items:center;padding:20px}}.modal{width:100%;max-width:560px;max-height:90dvh;display:flex;flex-direction:column;background:var(--bg-1);border:1px solid var(--line);border-radius:16px 16px 0 0;overflow:hidden}@media (min-width: 760px){.modal{border-radius:16px}}.modal:focus{outline:none}.modal-head{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid var(--line)}.modal-title{font-weight:700;color:var(--amber)}.modal-close{background:none;border:none;color:var(--text-dim);font-size:16px;cursor:pointer;padding:4px 8px}.modal-body{padding:14px;overflow-y:auto;display:flex;flex-direction:column;gap:18px}.question{display:flex;flex-direction:column;gap:6px}.q-header{font-size:var(--txt-meta);text-transform:uppercase;letter-spacing:.06em;color:var(--accent-2)}.q-text{font-size:14px;font-weight:600}.q-hint{font-size:var(--txt-meta);color:var(--text-faint)}.q-options{display:flex;flex-direction:column;gap:7px;margin-top:2px}.option-btn{display:flex;flex-direction:column;gap:2px;text-align:left;padding:10px 12px;border-radius:10px;border:1px solid var(--line);background:var(--bg-2);color:var(--text);cursor:pointer;font:inherit;transition:border-color var(--ease) .15s,background var(--ease) .15s}.option-btn:hover{background:var(--bg-hover)}.option-btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.option-btn[data-on=true]{border-color:var(--accent);background:var(--bg);box-shadow:inset 0 0 0 1px var(--accent)}.option-label{font-size:13px;font-weight:600}.option-desc{font-size:var(--txt-meta);color:var(--text-dim)}.capture-output{margin:0;padding:10px;border-radius:8px;background:var(--bg);border:1px solid var(--line);font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11px;color:var(--text-dim);white-space:pre-wrap;overflow-wrap:anywhere;max-height:40dvh;overflow:auto}.modal-foot{display:flex;align-items:center;gap:8px;padding:12px 14px;padding-bottom:calc(12px + env(safe-area-inset-bottom));border-top:1px solid var(--line)}.modal-foot-spacer{flex:1 1 auto}.btn-secondary,.btn-primary{padding:9px 14px;border-radius:9px;font:inherit;font-size:13px;font-weight:600;cursor:pointer;border:1px solid var(--line)}.btn-secondary{background:var(--bg-2);color:var(--text)}.btn-secondary:hover{background:var(--bg-hover)}.btn-primary{background:var(--accent);color:#0c121a;border-color:transparent}.btn-primary:disabled{opacity:.4;cursor:not-allowed}.toast{position:fixed;left:50%;top:calc(56px + env(safe-area-inset-top));transform:translate(-50%,-12px);background:var(--bg-2);border:1px solid var(--line);color:var(--text);padding:8px 14px;border-radius:10px;font-size:12px;opacity:0;pointer-events:none;transition:opacity var(--ease) .2s,transform var(--ease) .2s;z-index:60;max-width:90vw;text-align:center}.toast.show{opacity:1;transform:translate(-50%)}.toast-ok{border-color:var(--green)}.toast-error{border-color:var(--red);color:var(--text)}.working-indicator{display:inline-flex;align-items:center;gap:7px;color:var(--text-faint);font-size:13px}.working-spinner{width:12px;height:12px;border:2px solid var(--line);border-top-color:var(--accent-2, var(--text));border-radius:50%;animation:cockpit-spin .7s linear infinite;flex:none}@keyframes cockpit-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.working-spinner{animation-duration:1.6s}}.update-banner{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:6px 12px;background:var(--amber-bg);color:var(--amber);border-bottom:1px solid var(--line);font-size:13px}.update-banner code{background:var(--bg-hover);color:var(--text);padding:1px 6px;border-radius:4px;font-size:12px}.update-dismiss{flex:none;background:transparent;border:0;color:var(--amber);font-size:16px;line-height:1;cursor:pointer;padding:2px 6px}.update-dismiss:hover{color:var(--text)}.update-actions{display:inline-flex;align-items:center;gap:6px;flex:none}.update-now{background:var(--amber);color:var(--bg);border:0;border-radius:6px;font-size:12px;font-weight:700;padding:4px 10px;cursor:pointer}.update-now:hover{filter:brightness(1.08)}.chip-thumb{cursor:zoom-in}.hud-spacer{flex:1 1 auto}.notify-bell{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--text-dim);cursor:pointer;flex:0 0 auto;-webkit-tap-highlight-color:transparent}.notify-bell:hover:not(:disabled){color:var(--text);border-color:var(--text-faint)}.notify-bell[data-state=on]{color:var(--amber);border-color:var(--amber)}.notify-bell:disabled{opacity:.4;cursor:default}.ios-push-hint{padding:8px 12px;font-size:var(--txt-meta);line-height:1.4;color:var(--text);background:var(--amber-bg);border-bottom:1px solid var(--line);flex:0 0 auto}.rail-head{position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:6px;padding:6px 8px;background:var(--bg);border-bottom:1px solid var(--line)}.rail-new{flex:1;height:var(--hud-h);display:inline-flex;align-items:center;justify-content:center;background:var(--bg-2);color:var(--text);border:1px solid var(--line);border-radius:8px;font-size:13px;font-weight:600;padding:0 10px;cursor:pointer;transition:border-color var(--ease) .15s,background var(--ease) .15s}.rail-new:hover:not(:disabled){border-color:var(--accent);background:var(--bg-hover)}.rail-new:disabled{opacity:.6;cursor:default}.rail-gear{flex:none;width:var(--hud-h);height:var(--hud-h);display:inline-flex;align-items:center;justify-content:center;background:var(--bg-2);color:var(--text-dim);border:1px solid var(--line);border-radius:8px;font-size:15px;cursor:pointer;transition:color var(--ease) .15s,border-color var(--ease) .15s}.rail-gear:hover{color:var(--text);border-color:var(--accent)}.rail-new:focus-visible,.rail-gear:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.rail-new-form{position:sticky;top:0;z-index:2;display:flex;flex-direction:column;gap:8px;padding:8px;background:var(--bg);border-bottom:1px solid var(--line)}.rail-new-name{background:var(--bg-2);color:var(--text);border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:var(--txt-composer);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.rail-new-name:focus{outline:none;border-color:var(--accent)}.rail-new-name:disabled{opacity:.6}.rail-new-actions{display:flex;justify-content:flex-end;gap:6px}.rail-new-cancel,.rail-new-create{border-radius:8px;font-size:12px;font-weight:600;padding:6px 12px;cursor:pointer}.rail-new-cancel{background:transparent;color:var(--text-dim);border:1px solid var(--line)}.rail-new-cancel:hover:not(:disabled){color:var(--text)}.rail-new-create{background:var(--accent);color:var(--bg);border:0}.rail-new-create:hover:not(:disabled){filter:brightness(1.08)}.rail-new-cancel:disabled,.rail-new-create:disabled{opacity:.6;cursor:default}.transcript-attachments{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}.transcript-thumb-btn{background:none;border:1px solid var(--line);border-radius:8px;padding:0;cursor:zoom-in;overflow:hidden;flex:0 0 auto;display:block;transition:border-color var(--ease) .15s}.transcript-thumb-btn:hover{border-color:var(--accent)}.transcript-thumb-btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.transcript-thumb{display:block;max-width:140px;max-height:100px;width:auto;height:auto;border-radius:7px;object-fit:cover}.transcript-file-chip{display:inline-flex;align-items:center;gap:5px;max-width:200px;padding:4px 8px;border-radius:8px;border:1px solid var(--line);background:var(--bg-2);font-size:11.5px;color:var(--text);text-decoration:none;transition:border-color var(--ease) .15s,background var(--ease) .15s}.transcript-file-chip:hover{border-color:var(--accent);background:var(--bg-hover)}.transcript-file-chip:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.lightbox-backdrop{position:fixed;inset:0;z-index:100;background:#010203d9;display:flex;align-items:center;justify-content:center;cursor:zoom-out;padding:16px}.lightbox-backdrop:focus{outline:none}.lightbox-img{max-width:100%;max-height:90dvh;width:auto;height:auto;border-radius:10px;cursor:default;box-shadow:0 8px 40px #0009}.config-overlay{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center;padding:16px;background:#0000008c}.config-modal{width:100%;max-width:420px;background:var(--bg-1);border:1px solid var(--line);border-radius:var(--radius);padding:16px;display:flex;flex-direction:column;gap:14px;box-shadow:0 12px 40px #0006}.config-head{display:flex;align-items:center;justify-content:space-between}.config-title{font-size:15px;font-weight:700;color:var(--text)}.config-close{background:transparent;border:0;color:var(--text-dim);font-size:18px;line-height:1;cursor:pointer;padding:2px 6px}.config-close:hover{color:var(--text)}.config-field{display:flex;flex-direction:column;gap:5px}.config-label{font-size:12px;font-weight:600;color:var(--text-dim)}.config-input{background:var(--bg-2);color:var(--text);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-size:var(--txt-composer);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.config-input:focus{outline:none;border-color:var(--accent)}.config-input:disabled{opacity:.6}.config-hint{font-size:11px;color:var(--text-faint)}.config-hint code{background:var(--bg-hover);color:var(--text-dim);padding:0 4px;border-radius:4px}.config-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:2px}.config-cancel,.config-save{border-radius:8px;font-size:13px;font-weight:600;padding:7px 14px;cursor:pointer}.config-cancel{background:transparent;color:var(--text-dim);border:1px solid var(--line)}.config-cancel:hover:not(:disabled){color:var(--text)}.config-save{background:var(--accent);color:var(--bg);border:0}.config-save:hover:not(:disabled){filter:brightness(1.08)}.config-save:disabled,.config-cancel:disabled{opacity:.6;cursor:default}.term-btn{font-size:13px}.term-btn:hover{color:var(--amber);background:var(--bg-hover)}.term-overlay{position:fixed;inset:0;z-index:60;display:flex;flex-direction:column;background:var(--bg)}.term-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px 12px;border-bottom:1px solid var(--line);background:var(--bg-1);flex:0 0 auto}.term-title{font-weight:700;font-size:13px;color:var(--amber);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.term-actions{display:flex;align-items:center;gap:10px;flex:0 0 auto}.term-newtab{font-size:12px;color:var(--accent);text-decoration:none;padding:4px 8px;border:1px solid var(--line);border-radius:6px}.term-newtab:hover{background:var(--bg-hover)}.term-close{background:none;border:none;color:var(--text-dim);font-size:16px;line-height:1;cursor:pointer;padding:4px 8px;border-radius:6px}.term-close:hover{color:var(--text);background:var(--bg-hover)}.term-frame{flex:1 1 auto;width:100%;border:none;background:var(--bg)}.gate-root{display:flex;align-items:center;justify-content:center;min-height:100dvh;padding:24px;padding-top:max(24px,env(safe-area-inset-top));background:radial-gradient(120% 80% at 50% -10%,var(--bg-1),var(--bg) 60%)}.gate-card{display:flex;flex-direction:column;gap:12px;width:100%;max-width:320px;padding:24px;background:var(--bg-1);border:1px solid var(--line);border-radius:calc(var(--radius) + 4px);box-shadow:0 1px #ffffff0a inset,0 20px 50px -20px #000000b3}.gate-card--probing{flex-direction:row;align-items:center;justify-content:center;gap:10px;color:var(--text-dim)}.gate-brand{display:flex;align-items:center;gap:10px;margin-bottom:6px}.gate-glyph{font-size:22px;line-height:1;color:var(--accent)}.gate-title{font-size:17px;font-weight:650;letter-spacing:.01em;color:var(--text)}.gate-label{font-size:var(--txt-meta);text-transform:uppercase;letter-spacing:.08em;color:var(--text-faint)}.gate-input{width:100%;padding:11px 12px;font-size:var(--txt-composer);color:var(--text);background:var(--bg);border:1px solid var(--line);border-radius:var(--radius);outline:none;transition:border-color .15s var(--ease),background .15s var(--ease)}.gate-input:focus{border-color:var(--accent);background:var(--bg-2)}.gate-input[aria-invalid=true]{border-color:var(--red)}.gate-error{margin:0;font-size:var(--txt-meta);color:var(--red)}.gate-submit{margin-top:4px;padding:11px 12px;font-size:14px;font-weight:600;color:var(--bg);background:var(--accent);border:none;border-radius:var(--radius);cursor:pointer;transition:filter .15s var(--ease),opacity .15s var(--ease)}.gate-submit:hover:not(:disabled){filter:brightness(1.08)}.gate-submit:active:not(:disabled){filter:brightness(.95)}.gate-submit:disabled{opacity:.6;cursor:default}.gate-spinner{width:16px;height:16px;border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:gate-spin .7s linear infinite}@keyframes gate-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion: reduce){.gate-spinner{animation:none}}.transcript-thumb-loading{display:inline-block;background:var(--bg-2);border:1px solid var(--line)}.aui-md pre code.hljs{background:transparent;color:var(--text)}.aui-md .hljs-doctag,.aui-md .hljs-keyword,.aui-md .hljs-meta .hljs-keyword,.aui-md .hljs-template-tag,.aui-md .hljs-template-variable,.aui-md .hljs-type,.aui-md .hljs-variable.language_{color:#ff7b72}.aui-md .hljs-title,.aui-md .hljs-title.class_,.aui-md .hljs-title.class_.inherited__,.aui-md .hljs-title.function_{color:#d2a8ff}.aui-md .hljs-attr,.aui-md .hljs-attribute,.aui-md .hljs-literal,.aui-md .hljs-meta,.aui-md .hljs-number,.aui-md .hljs-operator,.aui-md .hljs-variable,.aui-md .hljs-selector-attr,.aui-md .hljs-selector-class,.aui-md .hljs-selector-id{color:#79c0ff}.aui-md .hljs-regexp,.aui-md .hljs-string,.aui-md .hljs-meta .hljs-string{color:#a5d6ff}.aui-md .hljs-built_in,.aui-md .hljs-symbol{color:#ffa657}.aui-md .hljs-comment,.aui-md .hljs-code,.aui-md .hljs-formula{color:#8b949e}.aui-md .hljs-name,.aui-md .hljs-quote,.aui-md .hljs-selector-tag,.aui-md .hljs-selector-pseudo{color:#7ee787}.aui-md .hljs-subst{color:var(--text)}.aui-md .hljs-section{color:#1f6feb;font-weight:700}.aui-md .hljs-bullet{color:#f2cc60}.aui-md .hljs-emphasis{color:var(--text);font-style:italic}.aui-md .hljs-strong{color:var(--text);font-weight:700}.aui-md .hljs-addition{color:#aff5b4;background-color:#033a16}.aui-md .hljs-deletion{color:#ffdcd7;background-color:#67060c}
|