claude-code-watch 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -11
- package/README.zh-CN.md +1 -1
- package/bin/claude-watch.js +108 -2
- package/package.json +5 -4
- package/public/index.html +63 -15
- package/public/vendor/github-dark.min.css +10 -0
- package/public/vendor/highlight.min.js +1213 -0
- package/public/vendor/marked.min.js +6 -0
- package/public/vendor/purify.min.js +3 -0
- package/src/parser/parser.js +34 -83
- package/src/server/server.js +99 -60
- package/src/watcher/watcher.js +286 -239
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Claude Code writes detailed JSONL logs under `~/.claude/projects/` as it works
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx claude-watch
|
|
21
|
+
npx claude-code-watch
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
This starts the dashboard at `http://localhost:23000` and opens it in your browser.
|
|
@@ -28,19 +28,21 @@ It will auto-discover active Claude Code sessions from `~/.claude/projects/` and
|
|
|
28
28
|
## Installation
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npm install -g claude-watch
|
|
31
|
+
npm install -g claude-code-watch
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
Then run:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
claude-watch
|
|
37
|
+
claude-code-watch
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Usage
|
|
41
41
|
|
|
42
42
|
```
|
|
43
|
-
claude-watch [OPTIONS]
|
|
43
|
+
claude-code-watch [OPTIONS]
|
|
44
|
+
|
|
45
|
+
Shorter alias: `cc-watch` (equivalent to `claude-code-watch`).
|
|
44
46
|
|
|
45
47
|
OPTIONS:
|
|
46
48
|
-p, --port <port> HTTP port (default: 23000)
|
|
@@ -62,25 +64,25 @@ OPTIONS:
|
|
|
62
64
|
|
|
63
65
|
```bash
|
|
64
66
|
# List recent sessions
|
|
65
|
-
claude-watch -l
|
|
67
|
+
claude-code-watch -l
|
|
66
68
|
|
|
67
69
|
# List active sessions from last 10 minutes
|
|
68
|
-
claude-watch -a -w 10m
|
|
70
|
+
claude-code-watch -a -w 10m
|
|
69
71
|
|
|
70
72
|
# Watch a specific session
|
|
71
|
-
claude-watch -s abc123-def456
|
|
73
|
+
claude-code-watch -s abc123-def456
|
|
72
74
|
|
|
73
75
|
# Live-only mode (don't replay history)
|
|
74
|
-
claude-watch -n
|
|
76
|
+
claude-code-watch -n
|
|
75
77
|
|
|
76
78
|
# Custom port and host
|
|
77
|
-
claude-watch -p 8080 -h 0.0.0.0
|
|
79
|
+
claude-code-watch -p 8080 -h 0.0.0.0
|
|
78
80
|
|
|
79
81
|
# Limit tree to 5 most recent sessions, auto-collapse after 2m of inactivity
|
|
80
|
-
claude-watch -m 5 -c 2m
|
|
82
|
+
claude-code-watch -m 5 -c 2m
|
|
81
83
|
|
|
82
84
|
# Debug mode: see every unknown JSONL line type
|
|
83
|
-
claude-watch -D
|
|
85
|
+
claude-code-watch -D
|
|
84
86
|
```
|
|
85
87
|
|
|
86
88
|
## How It Works
|
package/README.zh-CN.md
CHANGED
package/bin/claude-watch.js
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const cp = require('child_process');
|
|
7
|
+
|
|
5
8
|
const { startServer } = require('../src/server/server');
|
|
6
9
|
const { listSessions, listActiveSessions } = require('../src/watcher/watcher');
|
|
7
10
|
|
|
8
|
-
const VERSION = '
|
|
11
|
+
const { version: VERSION } = require('../package.json');
|
|
9
12
|
|
|
10
13
|
function printHelp() {
|
|
11
14
|
console.log(`claude-watch v${VERSION}
|
|
@@ -15,6 +18,7 @@ to a web browser.
|
|
|
15
18
|
|
|
16
19
|
USAGE:
|
|
17
20
|
claude-watch [OPTIONS]
|
|
21
|
+
claude-watch update Check for updates and install latest
|
|
18
22
|
|
|
19
23
|
OPTIONS:
|
|
20
24
|
-p, --port <port> HTTP port (default: 23000)
|
|
@@ -28,6 +32,7 @@ OPTIONS:
|
|
|
28
32
|
-c <dur> Auto-collapse sessions inactive for this duration (e.g. 2m)
|
|
29
33
|
-D Debug: show raw type:subtype for every JSONL line we'd drop
|
|
30
34
|
--poll <ms> Polling interval in milliseconds (default: 500)
|
|
35
|
+
--no-open Do not auto-open browser on start
|
|
31
36
|
-v Show version
|
|
32
37
|
--help Show this help
|
|
33
38
|
|
|
@@ -36,6 +41,93 @@ ENVIRONMENT:
|
|
|
36
41
|
`);
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
function compareVersions(a, b) {
|
|
45
|
+
const pa = a.split('.').map(Number);
|
|
46
|
+
const pb = b.split('.').map(Number);
|
|
47
|
+
for (let i = 0; i < 3; i++) {
|
|
48
|
+
if (pa[i] > pb[i]) return 1;
|
|
49
|
+
if (pa[i] < pb[i]) return -1;
|
|
50
|
+
}
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function fetchLatestVersion() {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const opts = {
|
|
57
|
+
hostname: 'registry.npmjs.org',
|
|
58
|
+
path: '/claude-code-watch/latest',
|
|
59
|
+
timeout: 5000,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const req = https.get(opts, (res) => {
|
|
63
|
+
if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode}`)); return; }
|
|
64
|
+
let data = '';
|
|
65
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
66
|
+
res.on('end', () => {
|
|
67
|
+
try {
|
|
68
|
+
const json = JSON.parse(data);
|
|
69
|
+
resolve(json.version);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
reject(err);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
req.on('error', reject);
|
|
77
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
78
|
+
req.end();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function checkForUpdate() {
|
|
83
|
+
fetchLatestVersion().then((latest) => {
|
|
84
|
+
if (compareVersions(latest, VERSION) > 0) {
|
|
85
|
+
console.log(`\n New version available: v${latest} (current: v${VERSION})`);
|
|
86
|
+
console.log(' Updating in background...\n');
|
|
87
|
+
const child = cp.spawn('npm', ['install', '-g', 'claude-code-watch@latest'], {
|
|
88
|
+
stdio: 'ignore',
|
|
89
|
+
detached: true,
|
|
90
|
+
});
|
|
91
|
+
child.unref();
|
|
92
|
+
child.on('exit', (code) => {
|
|
93
|
+
if (code === 0) {
|
|
94
|
+
console.log(` Updated to v${latest}. Changes take effect on next start.\n`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}).catch(() => { /* network unavailable, skip */ });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function runUpdate() {
|
|
102
|
+
console.log(` Current version: v${VERSION}`);
|
|
103
|
+
console.log(' Checking for latest version...\n');
|
|
104
|
+
|
|
105
|
+
let latest;
|
|
106
|
+
try {
|
|
107
|
+
latest = await fetchLatestVersion();
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error(` Failed to check for updates: ${err.message}`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (compareVersions(latest, VERSION) <= 0) {
|
|
114
|
+
console.log(` Already up to date (v${VERSION}).`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log(` Latest version: v${latest}`);
|
|
119
|
+
console.log(' Running npm install -g claude-code-watch@latest...\n');
|
|
120
|
+
|
|
121
|
+
const { execSync } = require('child_process');
|
|
122
|
+
try {
|
|
123
|
+
execSync('npm install -g claude-code-watch@latest', { stdio: 'inherit' });
|
|
124
|
+
console.log(`\n Updated to v${latest}. Restart to use the new version.`);
|
|
125
|
+
} catch {
|
|
126
|
+
console.error('\n Update failed. Try manually: npm install -g claude-code-watch@latest');
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
39
131
|
function parseDuration(s) {
|
|
40
132
|
const match = s.match(/^(\d+)(ms|s|m|h)$/);
|
|
41
133
|
if (!match) throw new Error(`Invalid duration: ${s}`);
|
|
@@ -62,6 +154,7 @@ async function main() {
|
|
|
62
154
|
maxSessions: 0,
|
|
63
155
|
collapseAfter: 0,
|
|
64
156
|
debugAll: false,
|
|
157
|
+
openBrowser: true,
|
|
65
158
|
};
|
|
66
159
|
|
|
67
160
|
// First pass: collect all option values
|
|
@@ -74,7 +167,7 @@ async function main() {
|
|
|
74
167
|
options.skipHistory = true;
|
|
75
168
|
break;
|
|
76
169
|
case '-p':
|
|
77
|
-
case '--port':
|
|
170
|
+
case '--port': {
|
|
78
171
|
if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
|
|
79
172
|
console.error(`Error: ${args[i]} requires a port number`);
|
|
80
173
|
process.exit(1);
|
|
@@ -86,6 +179,7 @@ async function main() {
|
|
|
86
179
|
}
|
|
87
180
|
options.port = pv;
|
|
88
181
|
break;
|
|
182
|
+
}
|
|
89
183
|
case '-h':
|
|
90
184
|
case '--host':
|
|
91
185
|
if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
|
|
@@ -117,6 +211,9 @@ async function main() {
|
|
|
117
211
|
case '--poll':
|
|
118
212
|
options.pollMs = parseInt(args[++i], 10) || 500;
|
|
119
213
|
break;
|
|
214
|
+
case '--no-open':
|
|
215
|
+
options.openBrowser = false;
|
|
216
|
+
break;
|
|
120
217
|
default:
|
|
121
218
|
break;
|
|
122
219
|
}
|
|
@@ -169,6 +266,14 @@ async function main() {
|
|
|
169
266
|
case '--help':
|
|
170
267
|
printHelp();
|
|
171
268
|
return;
|
|
269
|
+
case 'update':
|
|
270
|
+
await runUpdate();
|
|
271
|
+
return;
|
|
272
|
+
// Skip option flags already handled in first pass
|
|
273
|
+
case '-s': case '-n': case '-p': case '--port':
|
|
274
|
+
case '-h': case '--host': case '-w': case '-c':
|
|
275
|
+
case '-m': case '-D': case '--poll': case '--no-open':
|
|
276
|
+
break;
|
|
172
277
|
default:
|
|
173
278
|
if (args[i].startsWith('-')) {
|
|
174
279
|
console.error(`Unknown option: ${args[i]}`);
|
|
@@ -178,6 +283,7 @@ async function main() {
|
|
|
178
283
|
}
|
|
179
284
|
}
|
|
180
285
|
|
|
286
|
+
checkForUpdate();
|
|
181
287
|
startServer(options);
|
|
182
288
|
}
|
|
183
289
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-watch",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Web-based real-time monitor for Claude Code.",
|
|
5
5
|
"main": "./src/server/server.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"claude-code-watch": "
|
|
7
|
+
"claude-code-watch": "bin/claude-watch.js",
|
|
8
|
+
"cc-watch": "bin/claude-watch.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"start": "node bin/claude-watch.js",
|
|
11
|
-
"dev": "node --watch bin/claude-watch.js",
|
|
12
|
+
"dev": "node --watch bin/claude-watch.js --no-open",
|
|
12
13
|
"test": "node --test tests/all.test.js tests/watcher.test.js tests/server.test.js"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"license": "MIT",
|
|
27
28
|
"repository": {
|
|
28
29
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/shuxuecode/claude-watch"
|
|
30
|
+
"url": "git+https://github.com/shuxuecode/claude-watch.git"
|
|
30
31
|
},
|
|
31
32
|
"engines": {
|
|
32
33
|
"node": ">=18.0.0"
|
package/public/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>claude-watch</title>
|
|
7
|
-
<link rel="stylesheet" href="
|
|
7
|
+
<link rel="stylesheet" href="vendor/github-dark.min.css">
|
|
8
8
|
<style>
|
|
9
9
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
10
10
|
|
|
@@ -269,8 +269,9 @@ body {
|
|
|
269
269
|
<span class="sep">│</span>
|
|
270
270
|
</div>
|
|
271
271
|
|
|
272
|
-
<script src="
|
|
273
|
-
<script src="
|
|
272
|
+
<script src="vendor/highlight.min.js"></script>
|
|
273
|
+
<script src="vendor/marked.min.js"></script>
|
|
274
|
+
<script src="vendor/purify.min.js"></script>
|
|
274
275
|
<script>
|
|
275
276
|
// ══════════════════════════════════════════════════════════════════════════════
|
|
276
277
|
// DOM refs
|
|
@@ -290,6 +291,8 @@ let ws = null;
|
|
|
290
291
|
let reconnectTimer = null;
|
|
291
292
|
let reconnectDelay = 1000;
|
|
292
293
|
const MaxReconnectDelay = 30000;
|
|
294
|
+
const MaxReconnectAttempts = 20;
|
|
295
|
+
let reconnectAttempts = 0;
|
|
293
296
|
let showTree = true;
|
|
294
297
|
let autoScroll = true;
|
|
295
298
|
let lastMsgTime = 0;
|
|
@@ -299,8 +302,40 @@ let sessions = [];
|
|
|
299
302
|
let treeNodes = [];
|
|
300
303
|
let treeCursor = 0;
|
|
301
304
|
let streamItems = [];
|
|
302
|
-
|
|
305
|
+
const seenToolIDsKeys = [];
|
|
306
|
+
const seenToolIDsSet = new Set();
|
|
307
|
+
const seenToolIDsMax = 5000;
|
|
308
|
+
|
|
309
|
+
function seenToolIDsHas(key) {
|
|
310
|
+
return seenToolIDsSet.has(key);
|
|
311
|
+
}
|
|
312
|
+
function seenToolIDsAdd(key) {
|
|
313
|
+
seenToolIDsSet.add(key);
|
|
314
|
+
seenToolIDsKeys.push(key);
|
|
315
|
+
if (seenToolIDsKeys.length > seenToolIDsMax) {
|
|
316
|
+
const evictCount = seenToolIDsKeys.length >> 1;
|
|
317
|
+
for (let i = 0; i < evictCount; i++) {
|
|
318
|
+
seenToolIDsSet.delete(seenToolIDsKeys[i]);
|
|
319
|
+
}
|
|
320
|
+
seenToolIDsKeys.splice(0, evictCount);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const toolNameMapMax = 2000;
|
|
303
324
|
let toolNameMap = new Map(); // toolID -> toolName
|
|
325
|
+
let toolNameMapKeys = [];
|
|
326
|
+
|
|
327
|
+
function toolNameMapSet(toolID, toolName) {
|
|
328
|
+
if (toolNameMap.has(toolID)) return;
|
|
329
|
+
toolNameMap.set(toolID, toolName);
|
|
330
|
+
toolNameMapKeys.push(toolID);
|
|
331
|
+
if (toolNameMapKeys.length > toolNameMapMax) {
|
|
332
|
+
const evictCount = toolNameMapKeys.length >> 1;
|
|
333
|
+
for (let i = 0; i < evictCount; i++) {
|
|
334
|
+
toolNameMap.delete(toolNameMapKeys[i]);
|
|
335
|
+
}
|
|
336
|
+
toolNameMapKeys.splice(0, evictCount);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
304
339
|
let filters = new Map();
|
|
305
340
|
|
|
306
341
|
let showThinking = true;
|
|
@@ -314,6 +349,16 @@ let renderPending = false;
|
|
|
314
349
|
let totalInput = 0, totalOutput = 0, totalCacheCreate = 0, totalCacheRead = 0;
|
|
315
350
|
let contextData = {};
|
|
316
351
|
|
|
352
|
+
function computeTokensFromContext() {
|
|
353
|
+
totalInput = 0; totalOutput = 0; totalCacheCreate = 0; totalCacheRead = 0;
|
|
354
|
+
for (const ctx of Object.values(contextData)) {
|
|
355
|
+
totalInput += ctx.inputTokens || 0;
|
|
356
|
+
totalOutput += ctx.outputTokens || 0;
|
|
357
|
+
totalCacheCreate += ctx.cacheCreationTokens || 0;
|
|
358
|
+
totalCacheRead += ctx.cacheReadTokens || 0;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
317
362
|
let collapseAfter = 0;
|
|
318
363
|
let collapseTimer = null;
|
|
319
364
|
let activeRefreshTimer = null;
|
|
@@ -349,7 +394,7 @@ marked.setOptions({ renderer: mdRenderer, breaks: true, gfm: true });
|
|
|
349
394
|
|
|
350
395
|
function mdRender(text) {
|
|
351
396
|
try {
|
|
352
|
-
return marked.parse(text);
|
|
397
|
+
return DOMPurify.sanitize(marked.parse(text));
|
|
353
398
|
} catch {
|
|
354
399
|
return esc(text);
|
|
355
400
|
}
|
|
@@ -367,10 +412,16 @@ function connect() {
|
|
|
367
412
|
sessionInfo.textContent = 'Connected';
|
|
368
413
|
lastMsgTime = Date.now();
|
|
369
414
|
reconnectDelay = 1000;
|
|
415
|
+
reconnectAttempts = 0;
|
|
370
416
|
startStaleCheck();
|
|
371
417
|
startActiveRefresh();
|
|
372
418
|
};
|
|
373
419
|
ws.onclose = () => {
|
|
420
|
+
reconnectAttempts++;
|
|
421
|
+
if (reconnectAttempts >= MaxReconnectAttempts) {
|
|
422
|
+
sessionInfo.textContent = 'Disconnected. Please refresh to reconnect.';
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
374
425
|
sessionInfo.textContent = 'Disconnected, reconnecting...';
|
|
375
426
|
stopStaleCheck();
|
|
376
427
|
if (activeRefreshTimer) { clearInterval(activeRefreshTimer); activeRefreshTimer = null; }
|
|
@@ -544,10 +595,8 @@ function handleItemBatch(items) {
|
|
|
544
595
|
}
|
|
545
596
|
|
|
546
597
|
function pushItem(item) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if (item.cacheCreationTokens > 0) totalCacheCreate += item.cacheCreationTokens;
|
|
550
|
-
if (item.cacheReadTokens > 0) totalCacheRead += item.cacheReadTokens;
|
|
598
|
+
// Token counts are sourced exclusively from server context messages
|
|
599
|
+
// to avoid divergence between frontend accumulation and server tracking
|
|
551
600
|
|
|
552
601
|
if (item.model) {
|
|
553
602
|
const s = sessions.find(s => s.id === item.sessionID);
|
|
@@ -555,14 +604,13 @@ function pushItem(item) {
|
|
|
555
604
|
}
|
|
556
605
|
|
|
557
606
|
if (item.type === 'tool_input' && item.toolID && item.toolName) {
|
|
558
|
-
|
|
607
|
+
toolNameMapSet(item.toolID, item.toolName);
|
|
559
608
|
}
|
|
560
609
|
|
|
561
610
|
if (item.toolID) {
|
|
562
611
|
const key = `${item.toolID}:${item.type}`;
|
|
563
|
-
if (
|
|
564
|
-
|
|
565
|
-
if (seenToolIDs.size > 5000) seenToolIDs.clear();
|
|
612
|
+
if (seenToolIDsHas(key)) return;
|
|
613
|
+
seenToolIDsAdd(key);
|
|
566
614
|
}
|
|
567
615
|
|
|
568
616
|
streamItems.push(item);
|
|
@@ -639,7 +687,6 @@ function getNodeHTML(node, idx) {
|
|
|
639
687
|
if (node.type === 'session') {
|
|
640
688
|
const displayName = node.title || folderName(node.projectPath) || node.id.slice(0, 14);
|
|
641
689
|
const parts = [];
|
|
642
|
-
if (node.folder) parts.push(`📂 ${esc(node.folder)}`);
|
|
643
690
|
if (node.model) parts.push(`🧠 ${esc(node.model)}`);
|
|
644
691
|
const activeDot = isSessionActive(node) ? '<span class="active-dot on">🟢</span>' : '<span class="active-dot off">⚪</span>';
|
|
645
692
|
const subInfo = parts.length > 0 ? ` <span style="color:#6b7280;font-size:10px">${parts.join(' · ')}</span>` : '';
|
|
@@ -881,6 +928,7 @@ function refreshButtons() {
|
|
|
881
928
|
sessionInfo.textContent = info;
|
|
882
929
|
|
|
883
930
|
// Token info
|
|
931
|
+
computeTokensFromContext();
|
|
884
932
|
let tokStr = '';
|
|
885
933
|
if (totalInput > 0 || totalOutput > 0) {
|
|
886
934
|
tokStr = `${fmtTok(totalInput)} in / ${fmtTok(totalOutput)} out`;
|
|
@@ -1155,7 +1203,7 @@ function folderName(projectPath) {
|
|
|
1155
1203
|
}
|
|
1156
1204
|
|
|
1157
1205
|
function esc(s) {
|
|
1158
|
-
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1206
|
+
return (s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
1159
1207
|
}
|
|
1160
1208
|
|
|
1161
1209
|
function fmtDur(ms) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
|
2
|
+
Theme: GitHub Dark
|
|
3
|
+
Description: Dark theme as seen on github.com
|
|
4
|
+
Author: github.com
|
|
5
|
+
Maintainer: @Hirse
|
|
6
|
+
Updated: 2021-05-15
|
|
7
|
+
|
|
8
|
+
Outdated base version: https://github.com/primer/github-syntax-dark
|
|
9
|
+
Current colors taken from GitHub's CSS
|
|
10
|
+
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
|