@idl3/claude-control 0.1.13 → 0.1.20
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/answer.js +64 -33
- package/lib/sessions.js +45 -2
- package/lib/tmux.js +5 -5
- package/lib/transcript.js +33 -4
- package/lib/tui.js +11 -4
- package/package.json +1 -1
- package/server.js +107 -3
- package/web/dist/apple-touch-icon.png +0 -0
- package/web/dist/assets/{core-BtNQjjpA.js → core-BP70UsO-.js} +1 -1
- package/web/dist/assets/index-D2hrAUsb.js +78 -0
- package/web/dist/assets/index-DM_QgpOD.css +1 -0
- package/web/dist/icon-192.png +0 -0
- package/web/dist/icon-512.png +0 -0
- package/web/dist/icon.svg +12 -0
- package/web/dist/index.html +3 -3
- package/web/dist/manifest.webmanifest +2 -2
- package/web/dist/assets/index-BaAkOPA8.css +0 -1
- package/web/dist/assets/index-DRMuP6dA.js +0 -77
package/lib/answer.js
CHANGED
|
@@ -1,53 +1,85 @@
|
|
|
1
1
|
// Translate an AskUserQuestion selection into Claude Code TUI picker keystrokes.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// -
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
3
|
+
// Key model — matches the live picker footer and the CONTRACT.md spec:
|
|
4
|
+
// footer: "Enter to select · ↑/↓ to navigate · n to add notes · Tab to switch
|
|
5
|
+
// questions · Esc to cancel"
|
|
6
|
+
// spec: single-select = ['Down'*index, 'Enter'];
|
|
7
|
+
// multi-select = navigate Down to each chosen index, press Space, then Enter.
|
|
8
|
+
//
|
|
9
|
+
// - Each question lists its options vertically; a cursor starts on the FIRST
|
|
10
|
+
// option (index 0) and moves with Up/Down. There are NO number shortcuts —
|
|
11
|
+
// digits do not select, so the previous number-key model was a no-op against
|
|
12
|
+
// this UI (the cause of "answer sent but nothing happened").
|
|
13
|
+
// - SINGLE-select: navigate Down to the chosen option, then press Enter. Enter
|
|
14
|
+
// commits the answer and advances to the next question (or submits on the last).
|
|
15
|
+
// - MULTI-select: navigate Down to each chosen option (top-to-bottom, so a
|
|
16
|
+
// monotonic run of Downs) pressing Space to toggle it, then press Enter to
|
|
17
|
+
// confirm the question and advance/submit.
|
|
18
|
+
// - There is no separate "Submit" step: the final question's Enter submits the
|
|
19
|
+
// whole picker. (The old `Right`-to-Submit-tab + `'1'` model was stale.)
|
|
20
|
+
//
|
|
21
|
+
// We deliberately avoid the `n` (add notes) key: it opens a free-text input that
|
|
22
|
+
// would swallow every subsequent keystroke. Navigation is arrows + Space/Enter only.
|
|
23
|
+
//
|
|
24
|
+
// Keys are sent one at a time with a delay (see tmux.sendRawKeysSequenced) so the
|
|
25
|
+
// picker's re-render settles between keys and none are dropped.
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
|
-
*
|
|
27
|
-
* @param {{
|
|
28
|
+
* Resolve the selected labels to option indices, in top-to-bottom order.
|
|
29
|
+
* @param {{options: {label:string}[]}} question
|
|
28
30
|
* @param {string[]} selectedLabels
|
|
29
|
-
* @returns {
|
|
31
|
+
* @returns {number[]} ascending option indices
|
|
30
32
|
*/
|
|
31
|
-
|
|
33
|
+
function selectedIndices(question, selectedLabels) {
|
|
32
34
|
const options = Array.isArray(question?.options) ? question.options : [];
|
|
33
35
|
const indices = (selectedLabels || [])
|
|
34
36
|
.map((label) => options.findIndex((o) => o.label === label))
|
|
35
37
|
.filter((i) => i >= 0)
|
|
36
38
|
.sort((a, b) => a - b);
|
|
37
|
-
|
|
38
39
|
if (indices.length === 0) throw new Error('no valid option selected for question');
|
|
40
|
+
return indices;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Keys to answer ONE question, including the trailing Enter that confirms it and
|
|
45
|
+
* advances the picker (or submits, on the final question).
|
|
46
|
+
*
|
|
47
|
+
* Single-select: Down×index, Enter.
|
|
48
|
+
* Multi-select: for each chosen index (ascending) Down to it (relative to the
|
|
49
|
+
* current cursor) then Space to toggle; finally Enter to confirm.
|
|
50
|
+
*
|
|
51
|
+
* @param {{multiSelect?: boolean, options: {label:string}[]}} question
|
|
52
|
+
* @param {string[]} selectedLabels
|
|
53
|
+
* @returns {string[]}
|
|
54
|
+
*/
|
|
55
|
+
export function buildAnswerKeys(question, selectedLabels) {
|
|
56
|
+
const indices = selectedIndices(question, selectedLabels);
|
|
57
|
+
const keys = [];
|
|
39
58
|
|
|
40
59
|
if (!question.multiSelect) {
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
for (let i = 0; i < indices[0]; i += 1) keys.push('Down');
|
|
61
|
+
keys.push('Enter');
|
|
62
|
+
return keys;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Multi-select: walk down through the chosen options in order, toggling each
|
|
66
|
+
// with Space; the cursor starts at option 0, so move only the delta between
|
|
67
|
+
// successive targets. A trailing Enter confirms the question.
|
|
68
|
+
let cursor = 0;
|
|
69
|
+
for (const target of indices) {
|
|
70
|
+
for (let i = cursor; i < target; i += 1) keys.push('Down');
|
|
71
|
+
keys.push('Space');
|
|
72
|
+
cursor = target;
|
|
43
73
|
}
|
|
44
|
-
|
|
45
|
-
return
|
|
74
|
+
keys.push('Enter');
|
|
75
|
+
return keys;
|
|
46
76
|
}
|
|
47
77
|
|
|
48
78
|
/**
|
|
49
|
-
* Full key program for a (possibly multi-question) AskUserQuestion
|
|
50
|
-
* the
|
|
79
|
+
* Full key program for a (possibly multi-question) AskUserQuestion. Each question
|
|
80
|
+
* ends with the Enter that confirms it and advances; the last question's Enter
|
|
81
|
+
* submits the whole picker.
|
|
82
|
+
*
|
|
51
83
|
* @param {{questions: object[]}} pending
|
|
52
84
|
* @param {string[][]} selections selections[i] = chosen labels for questions[i]
|
|
53
85
|
* @returns {string[]}
|
|
@@ -59,6 +91,5 @@ export function buildAnswerProgram(pending, selections) {
|
|
|
59
91
|
for (let i = 0; i < questions.length; i += 1) {
|
|
60
92
|
program.push(...buildAnswerKeys(questions[i], selections?.[i] || []));
|
|
61
93
|
}
|
|
62
|
-
program.push('1'); // "Submit answers" on the Submit tab
|
|
63
94
|
return program;
|
|
64
95
|
}
|
package/lib/sessions.js
CHANGED
|
@@ -31,6 +31,7 @@ function isClaudeCmd(cmd) {
|
|
|
31
31
|
const TAIL_BYTES = 64 * 1024; // 64 KB max tail read
|
|
32
32
|
const REFRESH_INTERVAL_MS = 4000;
|
|
33
33
|
const CTX_POLL_INTERVAL_MS = 12000; // TUI ctx%/model capture — slower than refresh
|
|
34
|
+
const THINKING_POLL_INTERVAL_MS = 2000; // bottom-5-line capture for the live "thinking" flag
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* Encode an absolute cwd the way Claude Code names its transcript project
|
|
@@ -329,10 +330,14 @@ export class SessionRegistry extends EventEmitter {
|
|
|
329
330
|
this._pendingMap = new Map();
|
|
330
331
|
/** @type {Map<string, {ctxPct:number|null, model:string|null}>} target -> TUI status */
|
|
331
332
|
this._ctxMap = new Map();
|
|
333
|
+
/** @type {Map<string, boolean>} target -> actively-generating flag */
|
|
334
|
+
this._thinkingMap = new Map();
|
|
332
335
|
/** @type {ReturnType<setInterval>|null} */
|
|
333
336
|
this._interval = null;
|
|
334
337
|
/** @type {ReturnType<setInterval>|null} */
|
|
335
338
|
this._ctxInterval = null;
|
|
339
|
+
/** @type {ReturnType<setInterval>|null} */
|
|
340
|
+
this._thinkingInterval = null;
|
|
336
341
|
}
|
|
337
342
|
|
|
338
343
|
// -------------------------------------------------------------------------
|
|
@@ -465,6 +470,7 @@ export class SessionRegistry extends EventEmitter {
|
|
|
465
470
|
isClaude: true,
|
|
466
471
|
model: ctx.model || prettyModel(transcript?.model) || null,
|
|
467
472
|
ctxPct: ctx.ctxPct ?? null,
|
|
473
|
+
thinking: this._thinkingMap.get(win.target) ?? false,
|
|
468
474
|
};
|
|
469
475
|
});
|
|
470
476
|
|
|
@@ -500,17 +506,50 @@ export class SessionRegistry extends EventEmitter {
|
|
|
500
506
|
this._maybeEmit();
|
|
501
507
|
}
|
|
502
508
|
|
|
503
|
-
/**
|
|
509
|
+
/**
|
|
510
|
+
* Fast, cheap poll for the live "thinking" flag. Captures only the bottom ~5
|
|
511
|
+
* lines of each Claude pane and updates ONLY the thinking flag — the
|
|
512
|
+
* model/ctx values are left to the slower _pollCtx(). Best-effort.
|
|
513
|
+
*/
|
|
514
|
+
async _pollThinking() {
|
|
515
|
+
const sessions = this._sessions;
|
|
516
|
+
await Promise.all(
|
|
517
|
+
sessions.map(async (s) => {
|
|
518
|
+
if (!this._tmux.isValidTarget(s.target)) return;
|
|
519
|
+
try {
|
|
520
|
+
const cap = await this._tmux.capturePane(s.target, 5);
|
|
521
|
+
const { thinking } = parseTuiStatus(cap);
|
|
522
|
+
this._thinkingMap.set(s.target, thinking);
|
|
523
|
+
// Merge into the live session object without a full rebuild.
|
|
524
|
+
s.thinking = thinking;
|
|
525
|
+
} catch {
|
|
526
|
+
// pane gone / capture failed — leave previous value
|
|
527
|
+
}
|
|
528
|
+
}),
|
|
529
|
+
);
|
|
530
|
+
this._maybeEmit();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Start periodic refresh (every 4 s) + a slower ctx poll + a fast thinking
|
|
535
|
+
* poll, and fire each once.
|
|
536
|
+
*/
|
|
504
537
|
start() {
|
|
505
|
-
this.refresh()
|
|
538
|
+
this.refresh()
|
|
539
|
+
.then(() => Promise.all([this._pollCtx(), this._pollThinking()]))
|
|
540
|
+
.catch(() => {});
|
|
506
541
|
this._interval = setInterval(() => {
|
|
507
542
|
this.refresh().catch(() => {});
|
|
508
543
|
}, REFRESH_INTERVAL_MS);
|
|
509
544
|
this._ctxInterval = setInterval(() => {
|
|
510
545
|
this._pollCtx().catch(() => {});
|
|
511
546
|
}, CTX_POLL_INTERVAL_MS);
|
|
547
|
+
this._thinkingInterval = setInterval(() => {
|
|
548
|
+
this._pollThinking().catch(() => {});
|
|
549
|
+
}, THINKING_POLL_INTERVAL_MS);
|
|
512
550
|
if (this._interval.unref) this._interval.unref();
|
|
513
551
|
if (this._ctxInterval.unref) this._ctxInterval.unref();
|
|
552
|
+
if (this._thinkingInterval.unref) this._thinkingInterval.unref();
|
|
514
553
|
}
|
|
515
554
|
|
|
516
555
|
/** Stop periodic refresh. */
|
|
@@ -523,6 +562,10 @@ export class SessionRegistry extends EventEmitter {
|
|
|
523
562
|
clearInterval(this._ctxInterval);
|
|
524
563
|
this._ctxInterval = null;
|
|
525
564
|
}
|
|
565
|
+
if (this._thinkingInterval) {
|
|
566
|
+
clearInterval(this._thinkingInterval);
|
|
567
|
+
this._thinkingInterval = null;
|
|
568
|
+
}
|
|
526
569
|
}
|
|
527
570
|
|
|
528
571
|
// -------------------------------------------------------------------------
|
package/lib/tmux.js
CHANGED
|
@@ -436,16 +436,16 @@ export async function sendRawKeys(target, keys) {
|
|
|
436
436
|
|
|
437
437
|
/**
|
|
438
438
|
* Send key names ONE AT A TIME with a delay between each. Needed for the
|
|
439
|
-
* AskUserQuestion picker
|
|
440
|
-
*
|
|
441
|
-
*
|
|
439
|
+
* AskUserQuestion picker: each Down/Space/Enter triggers an async re-render, and
|
|
440
|
+
* firing the next key before it settles drops the key (so the picker stalls and
|
|
441
|
+
* the answer silently never lands).
|
|
442
442
|
*
|
|
443
443
|
* @param {string} target
|
|
444
444
|
* @param {string[]} keys
|
|
445
|
-
* @param {number} [delayMs=
|
|
445
|
+
* @param {number} [delayMs=160]
|
|
446
446
|
* @returns {Promise<void>}
|
|
447
447
|
*/
|
|
448
|
-
export async function sendRawKeysSequenced(target, keys, delayMs =
|
|
448
|
+
export async function sendRawKeysSequenced(target, keys, delayMs = 160) {
|
|
449
449
|
assertTarget(target);
|
|
450
450
|
if (!Array.isArray(keys) || keys.length === 0) return;
|
|
451
451
|
for (let i = 0; i < keys.length; i += 1) {
|
package/lib/transcript.js
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
// lib/transcript.js — bounded transcript tailing for claude-cockpit.
|
|
2
2
|
// Resource doctrine: NEVER read a whole file. Initial load reads only the last
|
|
3
|
-
// min(size,
|
|
4
|
-
// Files can be 200 MB+; whole-file reads will blow RAM.
|
|
3
|
+
// min(size, TAIL_MAX_BYTES) bytes (tail), then watches and reads ONLY new bytes
|
|
4
|
+
// via offset. Files can be 200 MB+; whole-file reads will blow RAM.
|
|
5
5
|
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import { EventEmitter } from 'node:events';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// Env lookup mirroring server.js: prefer CLAUDE_CONTROL_<X>, fall back to the
|
|
10
|
+
// legacy COCKPIT_<X> so existing launchers keep working after the rename.
|
|
11
|
+
function envInt(name) {
|
|
12
|
+
const raw =
|
|
13
|
+
process.env[`CLAUDE_CONTROL_${name}`] ?? process.env[`COCKPIT_${name}`];
|
|
14
|
+
if (raw == null) return null;
|
|
15
|
+
const n = Number(raw);
|
|
16
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Initial-tail byte cap. A fresh subscribe reads only the last min(size,
|
|
20
|
+
// TAIL_MAX_BYTES) bytes of the JSONL (NEVER the whole file — transcripts reach
|
|
21
|
+
// 200 MB+). In busy sessions a single assistant turn can carry hundreds of KB
|
|
22
|
+
// of tool output, so the old 1 MB window held only a handful of messages and
|
|
23
|
+
// the user's own recent turns fell outside it — they vanished on reload.
|
|
24
|
+
//
|
|
25
|
+
// 8 MB is the balance point: at a few KB/record it yields several hundred to a
|
|
26
|
+
// few thousand messages (enough that the message-count cap, not bytes, governs
|
|
27
|
+
// what a fresh subscribe serves), while staying ~25x below the largest real
|
|
28
|
+
// files and bounded per open session. A phone renders a capped subset anyway.
|
|
29
|
+
// Override with CLAUDE_CONTROL_TAIL_BYTES (legacy: COCKPIT_TAIL_BYTES).
|
|
30
|
+
const TAIL_MAX_BYTES = envInt('TAIL_BYTES') ?? 8 * 1024 * 1024; // 8 MB
|
|
31
|
+
|
|
32
|
+
// Default message-count cap for the in-memory buffer. Raised 1500 → 4000 so a
|
|
33
|
+
// fresh subscribe serves deeper scrollback (within the 8 MB tail window the
|
|
34
|
+
// count cap, not the byte window, governs how much history is served). At a few
|
|
35
|
+
// hundred bytes/normalized message this is a few MB resident per open session,
|
|
36
|
+
// well within the server's RSS budget.
|
|
37
|
+
// Override with CLAUDE_CONTROL_MAX_BUFFER (legacy: COCKPIT_MAX_BUFFER).
|
|
38
|
+
const DEFAULT_MAX_BUFFER = envInt('MAX_BUFFER') ?? 4000;
|
|
10
39
|
|
|
11
40
|
// ---------------------------------------------------------------------------
|
|
12
41
|
// Internal helper: read the last `maxBytes` of a file without loading it all.
|
|
@@ -167,7 +196,7 @@ export class TranscriptTailer extends EventEmitter {
|
|
|
167
196
|
* @param {string} filePath
|
|
168
197
|
* @param {{ maxBuffer?: number, debounceMs?: number }} options
|
|
169
198
|
*/
|
|
170
|
-
constructor(filePath, { maxBuffer =
|
|
199
|
+
constructor(filePath, { maxBuffer = DEFAULT_MAX_BUFFER, debounceMs = 150 } = {}) {
|
|
171
200
|
super();
|
|
172
201
|
this._filePath = filePath;
|
|
173
202
|
this._maxBuffer = maxBuffer;
|
package/lib/tui.js
CHANGED
|
@@ -4,16 +4,21 @@
|
|
|
4
4
|
// /claude-cockpit Opus 4.8 (1M context) ctx:35% Remote Control active
|
|
5
5
|
// and a title rule line such as:
|
|
6
6
|
// ───────────────────── auto-cleanup-uploads ──
|
|
7
|
-
// We extract the model label, the context-remaining percentage, and
|
|
8
|
-
//
|
|
7
|
+
// We extract the model label, the context-remaining percentage, and whether
|
|
8
|
+
// Claude is actively generating ("esc to interrupt" working line). All fields
|
|
9
|
+
// are optional — older/narrower panes may omit them.
|
|
9
10
|
|
|
10
11
|
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
11
12
|
const CTX_RE = /ctx:\s*(\d+)\s*%/i;
|
|
12
13
|
const MODEL_RE = /\b(Opus|Sonnet|Haiku)\s+[\d.]+(?:\s*\([^)]*\))?/i;
|
|
14
|
+
// Claude is actively generating when the TUI shows the working line, which
|
|
15
|
+
// always ends with "esc to interrupt" (the verb/glyph vary). Crucially we must
|
|
16
|
+
// NOT trip on the AskUserQuestion picker, which shows "esc to cancel".
|
|
17
|
+
const THINKING_RE = /esc to interrupt/i;
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
20
|
* @param {string} capture raw `tmux capture-pane -p` output (ANSI ok)
|
|
16
|
-
* @returns {{ ctxPct: number|null, model: string|null }}
|
|
21
|
+
* @returns {{ ctxPct: number|null, model: string|null, thinking: boolean }}
|
|
17
22
|
*/
|
|
18
23
|
export function parseTuiStatus(capture) {
|
|
19
24
|
const text = String(capture || '').replace(ANSI_RE, '');
|
|
@@ -29,7 +34,9 @@ export function parseTuiStatus(capture) {
|
|
|
29
34
|
const modelMatch = text.match(MODEL_RE);
|
|
30
35
|
if (modelMatch) model = modelMatch[0].replace(/\s+/g, ' ').trim();
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
const thinking = THINKING_RE.test(text);
|
|
38
|
+
|
|
39
|
+
return { ctxPct, model, thinking };
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idl3/claude-control",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
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
|
@@ -67,13 +67,21 @@ const CONFIG = {
|
|
|
67
67
|
// tripped "over limit" permanently. Override with CLAUDE_CONTROL_RSS_LIMIT_MB.
|
|
68
68
|
rssLimitMB: Number(env('RSS_LIMIT_MB')) || 768,
|
|
69
69
|
token: env('TOKEN') || readPersistedToken() || null,
|
|
70
|
-
|
|
70
|
+
// 4000: within lib/transcript's 8 MB byte tail, the message-count cap governs
|
|
71
|
+
// how much history a fresh subscribe serves. Raised 1500 → 4000 for deeper
|
|
72
|
+
// scrollback. Shares the CLAUDE_CONTROL_MAX_BUFFER override with lib/transcript.
|
|
73
|
+
maxBuffer: Number(env('MAX_BUFFER')) || 4000,
|
|
71
74
|
maxUploadMB: Number(env('MAX_UPLOAD_MB')) || 25,
|
|
72
75
|
uploadsDir:
|
|
73
76
|
env('UPLOADS') || path.join(os.homedir(), '.claude-control', 'uploads'),
|
|
74
77
|
uploadTtlHours: Number(env('UPLOAD_TTL_HOURS')) || 24,
|
|
75
78
|
pinsFile:
|
|
76
79
|
env('PINS') || path.join(os.homedir(), '.claude-control', 'pins.json'),
|
|
80
|
+
// Custom PWA home-screen icon (PNG). When present it overrides the bundled
|
|
81
|
+
// default robot logo for the manifest icons + apple-touch-icon. Uploaded via
|
|
82
|
+
// POST /api/icon, removed via DELETE /api/icon.
|
|
83
|
+
iconFile:
|
|
84
|
+
env('ICON') || path.join(os.homedir(), '.claude-control', 'icon.png'),
|
|
77
85
|
};
|
|
78
86
|
|
|
79
87
|
const MIME = {
|
|
@@ -253,6 +261,22 @@ const server = http.createServer((req, res) => {
|
|
|
253
261
|
return handleServeUpload(req, res, u);
|
|
254
262
|
}
|
|
255
263
|
|
|
264
|
+
// PWA home-screen icon. GET is token-FREE: the OS fetches manifest icons and
|
|
265
|
+
// the apple-touch-icon with no Authorization header, so this surface must be
|
|
266
|
+
// open (it only ever returns an image). POST/DELETE (replace/reset the custom
|
|
267
|
+
// icon) are token-gated.
|
|
268
|
+
if (u.pathname === '/api/icon') {
|
|
269
|
+
if (req.method === 'POST') {
|
|
270
|
+
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
271
|
+
return handleIconUpload(req, res);
|
|
272
|
+
}
|
|
273
|
+
if (req.method === 'DELETE') {
|
|
274
|
+
if (!checkToken(req)) return endJson(res, 401, { error: 'unauthorized' });
|
|
275
|
+
return handleIconReset(res);
|
|
276
|
+
}
|
|
277
|
+
return handleServeIcon(res, u);
|
|
278
|
+
}
|
|
279
|
+
|
|
256
280
|
// Raw-terminal escape hatch: token-gated reverse proxy to an on-demand,
|
|
257
281
|
// loopback-bound ttyd attached to this session's tmux pane. ttyd itself runs
|
|
258
282
|
// with no auth; this branch (and the matching upgrade branch) is the gate.
|
|
@@ -594,6 +618,70 @@ function handleServeFile(res, u) {
|
|
|
594
618
|
});
|
|
595
619
|
}
|
|
596
620
|
|
|
621
|
+
// 8-byte PNG file signature.
|
|
622
|
+
const PNG_SIG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
623
|
+
|
|
624
|
+
// GET /api/icon[?size=192|512] — serve the custom icon if one was uploaded,
|
|
625
|
+
// else the bundled default robot logo at the closest bundled size. Token-free
|
|
626
|
+
// (see the route guard) because the OS fetches it without auth headers.
|
|
627
|
+
function handleServeIcon(res, u) {
|
|
628
|
+
const size = Number(u.searchParams.get('size')) || 192;
|
|
629
|
+
const fallback = path.join(PUBLIC_DIR, size >= 512 ? 'icon-512.png' : 'icon-192.png');
|
|
630
|
+
const file = fs.existsSync(CONFIG.iconFile) ? CONFIG.iconFile : fallback;
|
|
631
|
+
fs.readFile(file, (err, data) => {
|
|
632
|
+
if (err) { res.writeHead(404); return res.end('not found'); }
|
|
633
|
+
res.writeHead(200, {
|
|
634
|
+
'content-type': 'image/png',
|
|
635
|
+
// The home-screen icon may change at runtime; never let the phone pin a
|
|
636
|
+
// stale one (it already re-reads the manifest on reinstall).
|
|
637
|
+
'cache-control': 'no-store, must-revalidate',
|
|
638
|
+
});
|
|
639
|
+
res.end(data);
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// POST /api/icon — replace the custom home-screen icon with the raw PNG body.
|
|
644
|
+
// PNG-only (validated by signature) so handleServeIcon's image/png is honest.
|
|
645
|
+
function handleIconUpload(req, res) {
|
|
646
|
+
const maxBytes = 4 * 1024 * 1024;
|
|
647
|
+
const chunks = [];
|
|
648
|
+
let size = 0;
|
|
649
|
+
let aborted = false;
|
|
650
|
+
req.on('data', (c) => {
|
|
651
|
+
if (aborted) return;
|
|
652
|
+
size += c.length;
|
|
653
|
+
if (size > maxBytes) {
|
|
654
|
+
aborted = true;
|
|
655
|
+
endJson(res, 413, { error: 'icon exceeds 4 MB limit' });
|
|
656
|
+
req.destroy();
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
chunks.push(c);
|
|
660
|
+
});
|
|
661
|
+
req.on('end', async () => {
|
|
662
|
+
if (aborted) return;
|
|
663
|
+
const buf = Buffer.concat(chunks);
|
|
664
|
+
if (buf.length < 8 || !buf.subarray(0, 8).equals(PNG_SIG)) {
|
|
665
|
+
return endJson(res, 400, { error: 'icon must be a PNG image' });
|
|
666
|
+
}
|
|
667
|
+
try {
|
|
668
|
+
await fs.promises.mkdir(path.dirname(CONFIG.iconFile), { recursive: true });
|
|
669
|
+
await fs.promises.writeFile(CONFIG.iconFile, buf, { mode: 0o600 });
|
|
670
|
+
endJson(res, 200, { ok: true, custom: true });
|
|
671
|
+
} catch (err) {
|
|
672
|
+
endJson(res, 500, { error: String(err?.message || err) });
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// DELETE /api/icon — drop the custom icon, reverting to the bundled default.
|
|
678
|
+
function handleIconReset(res) {
|
|
679
|
+
fs.promises
|
|
680
|
+
.rm(CONFIG.iconFile, { force: true })
|
|
681
|
+
.then(() => endJson(res, 200, { ok: true, custom: false }))
|
|
682
|
+
.catch((err) => endJson(res, 500, { error: String(err?.message || err) }));
|
|
683
|
+
}
|
|
684
|
+
|
|
597
685
|
// Set or clear a manual transcript pin. Body: { id, transcriptPath }.
|
|
598
686
|
// transcriptPath null/empty clears the pin. The pin is keyed by the session's
|
|
599
687
|
// stable windowId.paneIndex so it survives tmux window renumbering.
|
|
@@ -957,8 +1045,24 @@ async function handleClientMessage(ws, msg) {
|
|
|
957
1045
|
throw new Error('stale question (already answered or changed)');
|
|
958
1046
|
}
|
|
959
1047
|
const keys = buildAnswerProgram(pending, msg.selections || []);
|
|
960
|
-
//
|
|
961
|
-
|
|
1048
|
+
// Log the resolved key program so a failure to drive the picker is
|
|
1049
|
+
// diagnosable from ~/.claude-control/logs/out.log (no logging existed before).
|
|
1050
|
+
console.log(
|
|
1051
|
+
`[answer] toolUseId=${msg.toolUseId} target=${session.target} keys=${JSON.stringify(keys)}`,
|
|
1052
|
+
);
|
|
1053
|
+
try {
|
|
1054
|
+
// Sequenced (with delays) so the picker's re-render settles between keys.
|
|
1055
|
+
await tmux.sendRawKeysSequenced(session.target, keys);
|
|
1056
|
+
} catch (err) {
|
|
1057
|
+
// Surface the failure to the log and re-throw so the outer handler nacks
|
|
1058
|
+
// (ok:false) — never let an "answer sent" ack imply success when the keys
|
|
1059
|
+
// never landed in the pane.
|
|
1060
|
+
console.error(
|
|
1061
|
+
`[answer] FAILED toolUseId=${msg.toolUseId} target=${session.target}: ${String(err?.message || err)}`,
|
|
1062
|
+
);
|
|
1063
|
+
throw err;
|
|
1064
|
+
}
|
|
1065
|
+
console.log(`[answer] sent toolUseId=${msg.toolUseId} (${keys.length} keys)`);
|
|
962
1066
|
return send(ws, { type: 'ack', op: 'answer', ok: true });
|
|
963
1067
|
}
|
|
964
1068
|
case 'capture': {
|
|
Binary file
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{g as Ve}from"./index-DRMuP6dA.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-D2hrAUsb.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};
|