claude-highlight 1.0.0 → 1.0.2
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 +54 -392
- package/dist/claude-highlight-egzaxzbg.js +72 -0
- package/dist/claude-highlight.js +164 -308
- package/dist/pty-bun-2y4wmvve.js +159 -0
- package/dist/pty-node-6j2dj9ta.js +63 -0
- package/dist/pty-read-worker.js +2 -1
- package/dist/pty-write-worker.js +2 -1
- package/image.png +0 -0
- package/man/claude-highlight.1 +6 -3
- package/package.json +17 -12
package/dist/claude-highlight.js
CHANGED
|
@@ -1,78 +1,16 @@
|
|
|
1
|
-
#!/
|
|
2
|
-
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
':' //; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
3
|
+
import {
|
|
4
|
+
configFile,
|
|
5
|
+
decodeIgnore,
|
|
6
|
+
environ
|
|
7
|
+
} from "./claude-highlight-egzaxzbg.js";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
10
|
|
|
4
11
|
// src/claude-highlight.ts
|
|
5
|
-
import { existsSync, mkdirSync, openSync, readFileSync, statSync, writeFileSync, writeSync } from "fs";
|
|
6
|
-
import { basename, dirname, resolve } from "path";
|
|
7
|
-
|
|
8
|
-
// src/util.ts
|
|
9
|
-
import { homedir } from "os";
|
|
10
|
-
import { join } from "path";
|
|
11
|
-
function configFile(app) {
|
|
12
|
-
const base = process.env["XDG_CONFIG_HOME"] || join(process.env["HOME"] || homedir(), ".config");
|
|
13
|
-
return join(base, app, "config.json");
|
|
14
|
-
}
|
|
15
|
-
function decodeIgnore(buf) {
|
|
16
|
-
let out = "";
|
|
17
|
-
const n = buf.length;
|
|
18
|
-
let i = 0;
|
|
19
|
-
while (i < n) {
|
|
20
|
-
const b = buf[i] ?? 0;
|
|
21
|
-
if (b < 128) {
|
|
22
|
-
out += String.fromCharCode(b);
|
|
23
|
-
i++;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
let len, cp;
|
|
27
|
-
if (b >= 194 && b <= 223) {
|
|
28
|
-
len = 2;
|
|
29
|
-
cp = b & 31;
|
|
30
|
-
} else if (b >= 224 && b <= 239) {
|
|
31
|
-
len = 3;
|
|
32
|
-
cp = b & 15;
|
|
33
|
-
} else if (b >= 240 && b <= 244) {
|
|
34
|
-
len = 4;
|
|
35
|
-
cp = b & 7;
|
|
36
|
-
} else {
|
|
37
|
-
i++;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (i + len > n)
|
|
41
|
-
break;
|
|
42
|
-
let ok = true;
|
|
43
|
-
for (let k = 1;k < len; k++) {
|
|
44
|
-
const c = buf[i + k] ?? 128;
|
|
45
|
-
if ((c & 192) !== 128) {
|
|
46
|
-
ok = false;
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
cp = cp << 6 | c & 63;
|
|
50
|
-
}
|
|
51
|
-
if (!ok) {
|
|
52
|
-
i++;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (len === 3 && (cp < 2048 || cp >= 55296 && cp <= 57343))
|
|
56
|
-
ok = false;
|
|
57
|
-
if (len === 4 && (cp < 65536 || cp > 1114111))
|
|
58
|
-
ok = false;
|
|
59
|
-
if (!ok) {
|
|
60
|
-
i++;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
out += String.fromCodePoint(cp);
|
|
64
|
-
i += len;
|
|
65
|
-
}
|
|
66
|
-
return out;
|
|
67
|
-
}
|
|
68
|
-
function environ() {
|
|
69
|
-
const out = {};
|
|
70
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
71
|
-
if (value !== undefined)
|
|
72
|
-
out[key] = value;
|
|
73
|
-
}
|
|
74
|
-
return out;
|
|
75
|
-
}
|
|
12
|
+
import { existsSync as existsSync2, mkdirSync, openSync, readFileSync, statSync as statSync2, writeFileSync, writeSync as writeSync2 } from "node:fs";
|
|
13
|
+
import { basename, dirname, resolve } from "node:path";
|
|
76
14
|
|
|
77
15
|
// src/highlight_filter.ts
|
|
78
16
|
var WORD_TAIL = /[A-Za-z0-9'_-]+$/;
|
|
@@ -93,7 +31,7 @@ function finditer(pat, s) {
|
|
|
93
31
|
return out;
|
|
94
32
|
}
|
|
95
33
|
function asciiReplace(b) {
|
|
96
|
-
return b.toString("latin1").replace(/[\u0080-\u00ff]/g, "
|
|
34
|
+
return b.toString("latin1").replace(/[\u0080-\u00ff]/g, "�");
|
|
97
35
|
}
|
|
98
36
|
var isDigits = (s) => /^[0-9]+$/.test(s);
|
|
99
37
|
|
|
@@ -576,7 +514,7 @@ var FRAME_END = /\x1b\[\?2026l/g;
|
|
|
576
514
|
var PARTIAL_CSI = /^\x1b\[[\x30-\x3f]*[\x20-\x2f]*$/;
|
|
577
515
|
var MAX_FIX = 4096;
|
|
578
516
|
var MAX_PENDING = 4096;
|
|
579
|
-
var RULE_CHARS = new Set("
|
|
517
|
+
var RULE_CHARS = new Set("─━╌╍┄┅┈┉│┃╭╮╰╯┌┐└┘├┤┬┴┼");
|
|
580
518
|
var BOTTOM_CHROME = 10;
|
|
581
519
|
var MAX_COMPOSER = 24;
|
|
582
520
|
var WIDE = [
|
|
@@ -880,11 +818,13 @@ class ScreenModel {
|
|
|
880
818
|
cols;
|
|
881
819
|
chars;
|
|
882
820
|
fg;
|
|
821
|
+
bg;
|
|
883
822
|
ours;
|
|
884
823
|
rule;
|
|
885
824
|
x;
|
|
886
825
|
y;
|
|
887
826
|
curFg;
|
|
827
|
+
curBg;
|
|
888
828
|
curOurs;
|
|
889
829
|
saved;
|
|
890
830
|
top;
|
|
@@ -906,12 +846,14 @@ class ScreenModel {
|
|
|
906
846
|
invalidate() {
|
|
907
847
|
this.chars = Array.from({ length: this.rows }, () => new Array(this.cols).fill(null));
|
|
908
848
|
this.fg = Array.from({ length: this.rows }, () => new Array(this.cols).fill(null));
|
|
849
|
+
this.bg = Array.from({ length: this.rows }, () => new Array(this.cols).fill(null));
|
|
909
850
|
this.ours = Array.from({ length: this.rows }, () => new Array(this.cols).fill(false));
|
|
910
851
|
this.rule = new Array(this.rows).fill(null);
|
|
911
852
|
this.x = this.y = 0;
|
|
912
853
|
this.curFg = null;
|
|
854
|
+
this.curBg = null;
|
|
913
855
|
this.curOurs = false;
|
|
914
|
-
this.saved = [0, 0, null];
|
|
856
|
+
this.saved = [0, 0, null, null];
|
|
915
857
|
this.top = 0;
|
|
916
858
|
this.bot = this.rows - 1;
|
|
917
859
|
this.alt = false;
|
|
@@ -922,6 +864,7 @@ class ScreenModel {
|
|
|
922
864
|
}
|
|
923
865
|
blankRow() {
|
|
924
866
|
return [
|
|
867
|
+
new Array(this.cols).fill(null),
|
|
925
868
|
new Array(this.cols).fill(null),
|
|
926
869
|
new Array(this.cols).fill(null),
|
|
927
870
|
new Array(this.cols).fill(false)
|
|
@@ -930,6 +873,7 @@ class ScreenModel {
|
|
|
930
873
|
set(x, ch) {
|
|
931
874
|
this.chars[this.y][x] = ch;
|
|
932
875
|
this.fg[this.y][x] = this.curFg;
|
|
876
|
+
this.bg[this.y][x] = this.curBg;
|
|
933
877
|
this.ours[this.y][x] = this.curOurs;
|
|
934
878
|
this.dirty.add(this.y);
|
|
935
879
|
this.rule[this.y] = null;
|
|
@@ -962,12 +906,14 @@ class ScreenModel {
|
|
|
962
906
|
}
|
|
963
907
|
scroll(n, up = true) {
|
|
964
908
|
for (let k = 0;k < Math.max(n, 1); k++) {
|
|
965
|
-
const [c, f, o] = this.blankRow();
|
|
909
|
+
const [c, f, bg, o] = this.blankRow();
|
|
966
910
|
if (up) {
|
|
967
911
|
this.chars.splice(this.top, 1);
|
|
968
912
|
this.chars.splice(this.bot, 0, c);
|
|
969
913
|
this.fg.splice(this.top, 1);
|
|
970
914
|
this.fg.splice(this.bot, 0, f);
|
|
915
|
+
this.bg.splice(this.top, 1);
|
|
916
|
+
this.bg.splice(this.bot, 0, bg);
|
|
971
917
|
this.ours.splice(this.top, 1);
|
|
972
918
|
this.ours.splice(this.bot, 0, o);
|
|
973
919
|
} else {
|
|
@@ -975,6 +921,8 @@ class ScreenModel {
|
|
|
975
921
|
this.chars.splice(this.top, 0, c);
|
|
976
922
|
this.fg.splice(this.bot, 1);
|
|
977
923
|
this.fg.splice(this.top, 0, f);
|
|
924
|
+
this.bg.splice(this.bot, 1);
|
|
925
|
+
this.bg.splice(this.top, 0, bg);
|
|
978
926
|
this.ours.splice(this.bot, 1);
|
|
979
927
|
this.ours.splice(this.top, 0, o);
|
|
980
928
|
}
|
|
@@ -989,10 +937,17 @@ class ScreenModel {
|
|
|
989
937
|
while (i < toks.length) {
|
|
990
938
|
const t = toks[i] || "0";
|
|
991
939
|
const n = /^[0-9]+$/.test(t) ? parseInt(t, 10) : -1;
|
|
992
|
-
if (n === 0
|
|
940
|
+
if (n === 0) {
|
|
993
941
|
this.curFg = null;
|
|
942
|
+
this.curBg = null;
|
|
943
|
+
} else if (n === 39) {
|
|
944
|
+
this.curFg = null;
|
|
945
|
+
} else if (n === 49) {
|
|
946
|
+
this.curBg = null;
|
|
994
947
|
} else if (n >= 30 && n <= 37 || n >= 90 && n <= 97) {
|
|
995
948
|
this.curFg = t;
|
|
949
|
+
} else if (n >= 40 && n <= 47 || n >= 100 && n <= 107) {
|
|
950
|
+
this.curBg = t;
|
|
996
951
|
} else if (n === 38) {
|
|
997
952
|
if (toks[i + 1] === "5") {
|
|
998
953
|
this.curFg = toks.slice(i, i + 3).join(";");
|
|
@@ -1001,6 +956,14 @@ class ScreenModel {
|
|
|
1001
956
|
this.curFg = toks.slice(i, i + 5).join(";");
|
|
1002
957
|
i += 4;
|
|
1003
958
|
}
|
|
959
|
+
} else if (n === 48) {
|
|
960
|
+
if (toks[i + 1] === "5") {
|
|
961
|
+
this.curBg = toks.slice(i, i + 3).join(";");
|
|
962
|
+
i += 2;
|
|
963
|
+
} else if (toks[i + 1] === "2") {
|
|
964
|
+
this.curBg = toks.slice(i, i + 5).join(";");
|
|
965
|
+
i += 4;
|
|
966
|
+
}
|
|
1004
967
|
}
|
|
1005
968
|
i += 1;
|
|
1006
969
|
}
|
|
@@ -1010,6 +973,7 @@ class ScreenModel {
|
|
|
1010
973
|
for (const x of cells) {
|
|
1011
974
|
this.chars[this.y][x] = " ";
|
|
1012
975
|
this.fg[this.y][x] = null;
|
|
976
|
+
this.bg[this.y][x] = null;
|
|
1013
977
|
this.ours[this.y][x] = false;
|
|
1014
978
|
}
|
|
1015
979
|
this.dirty.add(this.y);
|
|
@@ -1087,13 +1051,15 @@ class ScreenModel {
|
|
|
1087
1051
|
} else if (fin === "L" || fin === "M") {
|
|
1088
1052
|
const n = Math.max(a, 1);
|
|
1089
1053
|
for (let k = 0;k < n; k++) {
|
|
1090
|
-
const [c, f, o] = this.blankRow();
|
|
1054
|
+
const [c, f, bg, o] = this.blankRow();
|
|
1091
1055
|
const at = fin === "L" ? this.y : this.bot;
|
|
1092
1056
|
const from = fin === "L" ? this.bot : this.y;
|
|
1093
1057
|
this.chars.splice(from, 1);
|
|
1094
1058
|
this.chars.splice(at, 0, c);
|
|
1095
1059
|
this.fg.splice(from, 1);
|
|
1096
1060
|
this.fg.splice(at, 0, f);
|
|
1061
|
+
this.bg.splice(from, 1);
|
|
1062
|
+
this.bg.splice(at, 0, bg);
|
|
1097
1063
|
this.ours.splice(from, 1);
|
|
1098
1064
|
this.ours.splice(at, 0, o);
|
|
1099
1065
|
}
|
|
@@ -1102,7 +1068,7 @@ class ScreenModel {
|
|
|
1102
1068
|
this.rule = new Array(this.rows).fill(null);
|
|
1103
1069
|
} else if (fin === "P" || fin === "@") {
|
|
1104
1070
|
const n = Math.max(a, 1);
|
|
1105
|
-
const chars = this.chars[this.y], fg = this.fg[this.y], ours = this.ours[this.y];
|
|
1071
|
+
const chars = this.chars[this.y], fg = this.fg[this.y], bg = this.bg[this.y], ours = this.ours[this.y];
|
|
1106
1072
|
if (fin === "P") {
|
|
1107
1073
|
chars.splice(this.x, n);
|
|
1108
1074
|
while (chars.length < this.cols)
|
|
@@ -1110,6 +1076,9 @@ class ScreenModel {
|
|
|
1110
1076
|
fg.splice(this.x, n);
|
|
1111
1077
|
while (fg.length < this.cols)
|
|
1112
1078
|
fg.push(null);
|
|
1079
|
+
bg.splice(this.x, n);
|
|
1080
|
+
while (bg.length < this.cols)
|
|
1081
|
+
bg.push(null);
|
|
1113
1082
|
ours.splice(this.x, n);
|
|
1114
1083
|
while (ours.length < this.cols)
|
|
1115
1084
|
ours.push(false);
|
|
@@ -1119,6 +1088,8 @@ class ScreenModel {
|
|
|
1119
1088
|
chars.pop();
|
|
1120
1089
|
fg.splice(this.x, 0, null);
|
|
1121
1090
|
fg.pop();
|
|
1091
|
+
bg.splice(this.x, 0, null);
|
|
1092
|
+
bg.pop();
|
|
1122
1093
|
ours.splice(this.x, 0, false);
|
|
1123
1094
|
ours.pop();
|
|
1124
1095
|
}
|
|
@@ -1193,9 +1164,9 @@ class ScreenModel {
|
|
|
1193
1164
|
continue;
|
|
1194
1165
|
}
|
|
1195
1166
|
if (nxt === 55) {
|
|
1196
|
-
this.saved = [this.x, this.y, this.curFg];
|
|
1167
|
+
this.saved = [this.x, this.y, this.curFg, this.curBg];
|
|
1197
1168
|
} else if (nxt === 56) {
|
|
1198
|
-
[this.x, this.y, this.curFg] = this.saved;
|
|
1169
|
+
[this.x, this.y, this.curFg, this.curBg] = this.saved;
|
|
1199
1170
|
this.curOurs = this.curFg !== null && this.palette.has(this.curFg);
|
|
1200
1171
|
} else if (nxt === 77) {
|
|
1201
1172
|
if (this.y === this.top)
|
|
@@ -1322,7 +1293,7 @@ class ScreenModel {
|
|
|
1322
1293
|
const cells = span(idx[start], idx[end - 1] + 1);
|
|
1323
1294
|
if (cells.some((c) => want[c] !== null))
|
|
1324
1295
|
continue;
|
|
1325
|
-
if (onlyUnstyled && cells.some((c) => this.fg[y][c] !== null && !this.ours[y][c])) {
|
|
1296
|
+
if (onlyUnstyled && cells.some((c) => this.fg[y][c] !== null && !this.ours[y][c] || this.bg[y][c] !== null)) {
|
|
1326
1297
|
continue;
|
|
1327
1298
|
}
|
|
1328
1299
|
for (const c of cells)
|
|
@@ -1332,30 +1303,40 @@ class ScreenModel {
|
|
|
1332
1303
|
return want;
|
|
1333
1304
|
}
|
|
1334
1305
|
corrections(rules, onlyUnstyled = true) {
|
|
1335
|
-
if (!this.alt
|
|
1306
|
+
if (!this.alt)
|
|
1336
1307
|
return Buffer.alloc(0);
|
|
1337
|
-
const rows = [...this.dirty].sort((p, q) => p - q);
|
|
1338
|
-
this.dirty = new Set;
|
|
1339
1308
|
const offLimits = this.composerRows();
|
|
1309
|
+
const todo = new Set(this.dirty);
|
|
1310
|
+
for (const y of offLimits)
|
|
1311
|
+
todo.add(y);
|
|
1312
|
+
if (todo.size === 0)
|
|
1313
|
+
return Buffer.alloc(0);
|
|
1314
|
+
const rows = [...todo].sort((p, q) => p - q);
|
|
1315
|
+
this.dirty = new Set;
|
|
1340
1316
|
const out = [];
|
|
1341
1317
|
let len = 0, spent = false;
|
|
1318
|
+
const needsFix = (y, x, want) => {
|
|
1319
|
+
if (this.fg[y][x] === want[x])
|
|
1320
|
+
return false;
|
|
1321
|
+
return this.ours[y][x] || want[x] !== null && this.fg[y][x] === null;
|
|
1322
|
+
};
|
|
1342
1323
|
for (const y of rows) {
|
|
1343
1324
|
if (spent) {
|
|
1344
1325
|
this.dirty.add(y);
|
|
1345
1326
|
continue;
|
|
1346
1327
|
}
|
|
1347
|
-
if (!this.ours[y].some((v) => v))
|
|
1348
|
-
continue;
|
|
1349
1328
|
const want = this.desired(y, rules, onlyUnstyled, offLimits);
|
|
1329
|
+
if (!this.ours[y].some((v) => v) && !want.some((v) => v !== null))
|
|
1330
|
+
continue;
|
|
1350
1331
|
let x = 0;
|
|
1351
1332
|
while (x < this.cols) {
|
|
1352
|
-
if (!(
|
|
1333
|
+
if (!needsFix(y, x, want)) {
|
|
1353
1334
|
x += 1;
|
|
1354
1335
|
continue;
|
|
1355
1336
|
}
|
|
1356
1337
|
let start = x;
|
|
1357
1338
|
const target = want[x];
|
|
1358
|
-
while (x < this.cols &&
|
|
1339
|
+
while (x < this.cols && needsFix(y, x, want) && want[x] === target)
|
|
1359
1340
|
x += 1;
|
|
1360
1341
|
if (this.chars[y][start] === "" && start)
|
|
1361
1342
|
start -= 1;
|
|
@@ -1420,186 +1401,66 @@ function span(a, b) {
|
|
|
1420
1401
|
}
|
|
1421
1402
|
|
|
1422
1403
|
// src/pty.ts
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
FFIType.u64,
|
|
1432
|
-
FFIType.u64,
|
|
1433
|
-
FFIType.u64,
|
|
1434
|
-
FFIType.u64
|
|
1435
|
-
];
|
|
1436
|
-
var IOCTL = {
|
|
1437
|
-
ioctl: {
|
|
1438
|
-
args: APPLE_VARARGS ? [FFIType.i32, FFIType.u64, ...IOCTL_PAD, FFIType.ptr] : [FFIType.i32, FFIType.u64, FFIType.ptr],
|
|
1439
|
-
returns: FFIType.i32
|
|
1440
|
-
}
|
|
1441
|
-
};
|
|
1442
|
-
var CORE = {
|
|
1443
|
-
read: { args: [FFIType.i32, FFIType.ptr, FFIType.u64], returns: FFIType.i64 },
|
|
1444
|
-
write: { args: [FFIType.i32, FFIType.ptr, FFIType.u64], returns: FFIType.i64 },
|
|
1445
|
-
close: { args: [FFIType.i32], returns: FFIType.i32 },
|
|
1446
|
-
kill: { args: [FFIType.i32, FFIType.i32], returns: FFIType.i32 },
|
|
1447
|
-
tcgetattr: { args: [FFIType.i32, FFIType.ptr], returns: FFIType.i32 },
|
|
1448
|
-
tcsetattr: { args: [FFIType.i32, FFIType.i32, FFIType.ptr], returns: FFIType.i32 },
|
|
1449
|
-
cfmakeraw: { args: [FFIType.ptr], returns: FFIType.i32 }
|
|
1450
|
-
};
|
|
1451
|
-
var OPENPTY = {
|
|
1452
|
-
openpty: {
|
|
1453
|
-
args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr],
|
|
1454
|
-
returns: FFIType.i32
|
|
1455
|
-
}
|
|
1456
|
-
};
|
|
1457
|
-
function bind(symbols) {
|
|
1458
|
-
return symbols;
|
|
1404
|
+
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
1405
|
+
async function ptySpawn(file, args, opts) {
|
|
1406
|
+
if (IS_BUN) {
|
|
1407
|
+
const { ptySpawnBun } = await import("./pty-bun-2y4wmvve.js");
|
|
1408
|
+
return ptySpawnBun(file, args, opts);
|
|
1409
|
+
}
|
|
1410
|
+
const { ptySpawnNode } = await import("./pty-node-6j2dj9ta.js");
|
|
1411
|
+
return ptySpawnNode(file, args, opts);
|
|
1459
1412
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1413
|
+
|
|
1414
|
+
// src/sys.ts
|
|
1415
|
+
import { existsSync, statSync, writeSync } from "node:fs";
|
|
1416
|
+
import { join } from "node:path";
|
|
1417
|
+
import { isatty } from "node:tty";
|
|
1418
|
+
function setRawStdin() {
|
|
1419
|
+
if (!isatty(0) || typeof process.stdin.setRawMode !== "function")
|
|
1420
|
+
return null;
|
|
1421
|
+
process.stdin.setRawMode(true);
|
|
1422
|
+
return () => {
|
|
1468
1423
|
try {
|
|
1469
|
-
|
|
1424
|
+
process.stdin.setRawMode(false);
|
|
1470
1425
|
} catch {}
|
|
1471
|
-
}
|
|
1472
|
-
throw new Error("openpty not found in libc.so.6, libutil.so.1 or libutil.so");
|
|
1473
|
-
}
|
|
1474
|
-
var ffi = load();
|
|
1475
|
-
var TCSANOW = 0;
|
|
1476
|
-
var TIOCGWINSZ = process.platform === "darwin" ? 1074295912 : 21523;
|
|
1477
|
-
var TIOCSWINSZ = process.platform === "darwin" ? 2148037735 : 21524;
|
|
1478
|
-
var SIGWINCH = 28;
|
|
1479
|
-
var SIGHUP = 1;
|
|
1480
|
-
var SIGTERM = 15;
|
|
1481
|
-
function saveAndSetRawStdin() {
|
|
1482
|
-
const cur = Buffer.alloc(128);
|
|
1483
|
-
if (ffi.core.tcgetattr(0, cur) !== 0)
|
|
1484
|
-
return null;
|
|
1485
|
-
const saved = Buffer.from(cur);
|
|
1486
|
-
ffi.core.cfmakeraw(cur);
|
|
1487
|
-
ffi.core.tcsetattr(0, TCSANOW, cur);
|
|
1488
|
-
return saved;
|
|
1489
|
-
}
|
|
1490
|
-
function restoreStdin(saved) {
|
|
1491
|
-
ffi.core.tcsetattr(0, TCSANOW, saved);
|
|
1492
|
-
}
|
|
1493
|
-
function setWinsize(master, rows, cols) {
|
|
1494
|
-
const ws = Buffer.alloc(8);
|
|
1495
|
-
ws.writeUInt16LE(Math.max(rows, 1) & 65535, 0);
|
|
1496
|
-
ws.writeUInt16LE(Math.max(cols, 1) & 65535, 2);
|
|
1497
|
-
return ioctlPtr(master, TIOCSWINSZ, ws);
|
|
1426
|
+
};
|
|
1498
1427
|
}
|
|
1499
|
-
function
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
if (ioctlPtr(fd, TIOCGWINSZ, ws) !== 0)
|
|
1505
|
-
return null;
|
|
1506
|
-
return [ws.readUInt16LE(0), ws.readUInt16LE(2)];
|
|
1428
|
+
function winsize() {
|
|
1429
|
+
const rows = process.stdout.rows, cols = process.stdout.columns;
|
|
1430
|
+
if (rows && cols)
|
|
1431
|
+
return [rows, cols];
|
|
1432
|
+
return [24, Number(process.env["COLUMNS"]) || 80];
|
|
1507
1433
|
}
|
|
1508
1434
|
function writeAll(fd, data) {
|
|
1509
1435
|
let off = 0;
|
|
1510
1436
|
while (off < data.length) {
|
|
1511
|
-
|
|
1512
|
-
|
|
1437
|
+
try {
|
|
1438
|
+
off += writeSync(fd, data, off);
|
|
1439
|
+
} catch (e) {
|
|
1440
|
+
const code = e.code;
|
|
1441
|
+
if (code === "EAGAIN")
|
|
1442
|
+
continue;
|
|
1513
1443
|
return;
|
|
1514
|
-
|
|
1444
|
+
}
|
|
1515
1445
|
}
|
|
1516
1446
|
}
|
|
1517
1447
|
var SLEEPER = new Int32Array(new SharedArrayBuffer(4));
|
|
1518
1448
|
function sleepSync(ms) {
|
|
1519
1449
|
Atomics.wait(SLEEPER, 0, 0, ms);
|
|
1520
1450
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
if (rc !== 0)
|
|
1535
|
-
throw new Error(`openpty failed (${rc})`);
|
|
1536
|
-
const master = m.readInt32LE(0);
|
|
1537
|
-
const slave = s.readInt32LE(0);
|
|
1538
|
-
const proc = Bun.spawn([file, ...args], {
|
|
1539
|
-
stdin: slave,
|
|
1540
|
-
stdout: slave,
|
|
1541
|
-
stderr: slave,
|
|
1542
|
-
env: opts.env ?? environ(),
|
|
1543
|
-
...opts.cwd === undefined ? {} : { cwd: opts.cwd }
|
|
1544
|
-
});
|
|
1545
|
-
closeSync(slave);
|
|
1546
|
-
const reader = new Worker(workerUrl("pty-read-worker"));
|
|
1547
|
-
const writer = new Worker(workerUrl("pty-write-worker"));
|
|
1548
|
-
let dataCb = null;
|
|
1549
|
-
let markDrained = () => {};
|
|
1550
|
-
const drained = new Promise((res) => {
|
|
1551
|
-
markDrained = res;
|
|
1552
|
-
});
|
|
1553
|
-
reader.addEventListener("message", (event) => {
|
|
1554
|
-
const chunk = event.data;
|
|
1555
|
-
if (chunk === null)
|
|
1556
|
-
markDrained();
|
|
1557
|
-
else
|
|
1558
|
-
dataCb?.(Buffer.from(chunk));
|
|
1559
|
-
});
|
|
1560
|
-
const exited = (async () => Number(await proc.exited))();
|
|
1561
|
-
let reading = false;
|
|
1562
|
-
return {
|
|
1563
|
-
pid: proc.pid,
|
|
1564
|
-
get exited() {
|
|
1565
|
-
return exited;
|
|
1566
|
-
},
|
|
1567
|
-
get drained() {
|
|
1568
|
-
return drained;
|
|
1569
|
-
},
|
|
1570
|
-
write(data) {
|
|
1571
|
-
writer.postMessage({ fd: master, data: new Uint8Array(data) });
|
|
1572
|
-
},
|
|
1573
|
-
onData(cb) {
|
|
1574
|
-
dataCb = cb;
|
|
1575
|
-
if (!reading) {
|
|
1576
|
-
reading = true;
|
|
1577
|
-
reader.postMessage({ fd: master });
|
|
1578
|
-
}
|
|
1579
|
-
},
|
|
1580
|
-
resize(rows, cols) {
|
|
1581
|
-
setWinsize(master, rows, cols);
|
|
1582
|
-
try {
|
|
1583
|
-
proc.kill(SIGWINCH);
|
|
1584
|
-
} catch {}
|
|
1585
|
-
},
|
|
1586
|
-
kill(signal = SIGTERM) {
|
|
1587
|
-
try {
|
|
1588
|
-
proc.kill(signal);
|
|
1589
|
-
} catch {}
|
|
1590
|
-
},
|
|
1591
|
-
destroy() {
|
|
1592
|
-
try {
|
|
1593
|
-
proc.kill(SIGHUP);
|
|
1594
|
-
} catch {}
|
|
1595
|
-
try {
|
|
1596
|
-
closeSync(master);
|
|
1597
|
-
} catch {}
|
|
1598
|
-
markDrained();
|
|
1599
|
-
reader.terminate();
|
|
1600
|
-
writer.terminate();
|
|
1601
|
-
}
|
|
1602
|
-
};
|
|
1451
|
+
function which(name) {
|
|
1452
|
+
if (name.includes("/"))
|
|
1453
|
+
return null;
|
|
1454
|
+
for (const dir of (process.env["PATH"] ?? "").split(":")) {
|
|
1455
|
+
if (!dir)
|
|
1456
|
+
continue;
|
|
1457
|
+
const candidate = join(dir, name);
|
|
1458
|
+
try {
|
|
1459
|
+
if (existsSync(candidate) && statSync(candidate).isFile())
|
|
1460
|
+
return candidate;
|
|
1461
|
+
} catch {}
|
|
1462
|
+
}
|
|
1463
|
+
return null;
|
|
1603
1464
|
}
|
|
1604
1465
|
|
|
1605
1466
|
// src/json.ts
|
|
@@ -1628,15 +1489,16 @@ function truthy(v) {
|
|
|
1628
1489
|
}
|
|
1629
1490
|
|
|
1630
1491
|
// src/claude-highlight.ts
|
|
1631
|
-
|
|
1492
|
+
":";
|
|
1493
|
+
var DOC = `claude-highlight — run Claude Code behind a PTY that colors epistemic markers.
|
|
1632
1494
|
|
|
1633
1495
|
claude-highlight [any claude args...]
|
|
1634
1496
|
|
|
1635
1497
|
Spawns the real \`claude\` on a pseudo-terminal and forwards bytes both ways,
|
|
1636
1498
|
injecting SGR color around matched words on the way out. Because SGR is
|
|
1637
|
-
zero-width, the child's layout math is untouched
|
|
1499
|
+
zero-width, the child's layout math is untouched — see highlight_filter.ts.
|
|
1638
1500
|
|
|
1639
|
-
Hotkey (default F9, or bind cmd+/ in Ghostty
|
|
1501
|
+
Hotkey (default F9, or bind cmd+/ in Ghostty — see README) opens a plugin
|
|
1640
1502
|
menu to toggle categories live. Config lives at
|
|
1641
1503
|
~/.config/claude-highlight/config.json and is re-read whenever it changes.
|
|
1642
1504
|
`;
|
|
@@ -1667,7 +1529,7 @@ function text(v, fallback) {
|
|
|
1667
1529
|
return v === undefined ? fallback : String(v);
|
|
1668
1530
|
}
|
|
1669
1531
|
function loadConfig() {
|
|
1670
|
-
const parsed =
|
|
1532
|
+
const parsed = existsSync2(CONFIG) ? parseJson(readFileSync(CONFIG, "utf8")) : null;
|
|
1671
1533
|
const user = isObject(parsed) ? parsed : {};
|
|
1672
1534
|
const catsRaw = user["categories"];
|
|
1673
1535
|
const cats = isObject(catsRaw) ? catsRaw : {};
|
|
@@ -1709,7 +1571,7 @@ function loadConfig() {
|
|
|
1709
1571
|
prose_only: truthy(user["prose_only"] ?? true),
|
|
1710
1572
|
idle_repaint: truthy(user["idle_repaint"] ?? false)
|
|
1711
1573
|
};
|
|
1712
|
-
if (!
|
|
1574
|
+
if (!existsSync2(CONFIG)) {
|
|
1713
1575
|
mkdirSync(dirname(CONFIG), { recursive: true });
|
|
1714
1576
|
writeFileSync(CONFIG, JSON.stringify(cfgAll, null, 2));
|
|
1715
1577
|
}
|
|
@@ -1725,11 +1587,11 @@ function selfName() {
|
|
|
1725
1587
|
base.replace(/\.[jt]s$/, "")
|
|
1726
1588
|
];
|
|
1727
1589
|
for (const name of candidates) {
|
|
1728
|
-
const found =
|
|
1590
|
+
const found = which(name);
|
|
1729
1591
|
if (!found)
|
|
1730
1592
|
continue;
|
|
1731
1593
|
try {
|
|
1732
|
-
const a =
|
|
1594
|
+
const a = statSync2(found), b = statSync2(resolve(argv0));
|
|
1733
1595
|
if (a.ino === b.ino && a.dev === b.dev)
|
|
1734
1596
|
return name;
|
|
1735
1597
|
} catch {}
|
|
@@ -1819,17 +1681,17 @@ var SEL_BG = c_("48;2;64;74;112", "48;5;238");
|
|
|
1819
1681
|
var SEL_FG = c_("38;2;255;255;255", "38;5;255");
|
|
1820
1682
|
var SEL_BAR = c_("38;2;255;214;102", "38;5;221");
|
|
1821
1683
|
var G = UNICODE ? {
|
|
1822
|
-
tl: "
|
|
1823
|
-
tr: "
|
|
1824
|
-
bl: "
|
|
1825
|
-
br: "
|
|
1826
|
-
h: "
|
|
1827
|
-
v: "
|
|
1828
|
-
vsel: "
|
|
1829
|
-
mark: "
|
|
1830
|
-
swatch: "
|
|
1831
|
-
up: "
|
|
1832
|
-
dot: "
|
|
1684
|
+
tl: "╭",
|
|
1685
|
+
tr: "╮",
|
|
1686
|
+
bl: "╰",
|
|
1687
|
+
br: "╯",
|
|
1688
|
+
h: "─",
|
|
1689
|
+
v: "│",
|
|
1690
|
+
vsel: "┃",
|
|
1691
|
+
mark: " ▸ ",
|
|
1692
|
+
swatch: "██",
|
|
1693
|
+
up: "↑↓",
|
|
1694
|
+
dot: "·"
|
|
1833
1695
|
} : {
|
|
1834
1696
|
tl: "+",
|
|
1835
1697
|
tr: "+",
|
|
@@ -1902,37 +1764,37 @@ function drawMenu(cfg, rows, cols, sel) {
|
|
|
1902
1764
|
}
|
|
1903
1765
|
var PALETTE = [
|
|
1904
1766
|
[
|
|
1905
|
-
"reds
|
|
1767
|
+
"reds — loudest, for the claim you most want to catch",
|
|
1906
1768
|
"inference",
|
|
1907
1769
|
[203, 196, 202, 209, 167, 174, 210, 168]
|
|
1908
1770
|
],
|
|
1909
1771
|
[
|
|
1910
|
-
"pinks and magentas
|
|
1772
|
+
"pinks and magentas — loud but not alarming",
|
|
1911
1773
|
"unknown",
|
|
1912
1774
|
[170, 176, 177, 183, 213, 205, 141, 218]
|
|
1913
1775
|
],
|
|
1914
1776
|
[
|
|
1915
|
-
"ambers and yellows
|
|
1777
|
+
"ambers and yellows — warm, reads as 'check this'",
|
|
1916
1778
|
"assumption",
|
|
1917
1779
|
[214, 215, 220, 221, 222, 178, 136, 223]
|
|
1918
1780
|
],
|
|
1919
1781
|
[
|
|
1920
|
-
"golds and tans
|
|
1782
|
+
"golds and tans — quieter warmth, good for a busy category",
|
|
1921
1783
|
"appearance",
|
|
1922
1784
|
[179, 180, 187, 144, 137, 173, 143, 229]
|
|
1923
1785
|
],
|
|
1924
1786
|
[
|
|
1925
|
-
"cyans and teals
|
|
1787
|
+
"cyans and teals — cold, opposite end from the reds",
|
|
1926
1788
|
"overclaim",
|
|
1927
1789
|
[51, 45, 44, 80, 87, 116, 73, 37]
|
|
1928
1790
|
],
|
|
1929
1791
|
[
|
|
1930
1792
|
"greens",
|
|
1931
|
-
"unused
|
|
1793
|
+
"unused — free for a category of your own",
|
|
1932
1794
|
[71, 78, 108, 114, 150, 84, 42, 155]
|
|
1933
1795
|
],
|
|
1934
1796
|
[
|
|
1935
|
-
"blues and violets
|
|
1797
|
+
"blues and violets — cool, and nothing in warm prose competes with them",
|
|
1936
1798
|
"modal, vagueness, softener",
|
|
1937
1799
|
[33, 39, 99, 105, 27, 63, 93, 135]
|
|
1938
1800
|
]
|
|
@@ -1959,7 +1821,7 @@ function paletteLines(cfg, cols, full = false) {
|
|
|
1959
1821
|
for (let base = 16;base < 256; base += 12) {
|
|
1960
1822
|
let line = "";
|
|
1961
1823
|
for (let n = base;n < Math.min(base + 12, 256); n++) {
|
|
1962
|
-
line += `\x1B[38;5;${n}m
|
|
1824
|
+
line += `\x1B[38;5;${n}m██\x1B[39m${String(n).padEnd(4)}`;
|
|
1963
1825
|
}
|
|
1964
1826
|
out.push(" " + line);
|
|
1965
1827
|
}
|
|
@@ -2072,10 +1934,7 @@ function parseArgs(argv) {
|
|
|
2072
1934
|
return a;
|
|
2073
1935
|
}
|
|
2074
1936
|
function stdoutCols() {
|
|
2075
|
-
|
|
2076
|
-
if (sz && sz[1])
|
|
2077
|
-
return sz[1];
|
|
2078
|
-
return Number(process.env["COLUMNS"]) || 100;
|
|
1937
|
+
return process.stdout.columns || Number(process.env["COLUMNS"]) || 100;
|
|
2079
1938
|
}
|
|
2080
1939
|
async function main(argv) {
|
|
2081
1940
|
const known = parseArgs(argv);
|
|
@@ -2100,7 +1959,7 @@ async function main(argv) {
|
|
|
2100
1959
|
`, "utf8"));
|
|
2101
1960
|
return 0;
|
|
2102
1961
|
}
|
|
2103
|
-
let mtime =
|
|
1962
|
+
let mtime = existsSync2(CONFIG) ? statSync2(CONFIG).mtimeMs : 0;
|
|
2104
1963
|
const hl = new AnsiHighlighter(buildRules(cfg), cfg.prose_only);
|
|
2105
1964
|
hl.rewrites = buildRewrites();
|
|
2106
1965
|
hl.holdPrefixes = growablePrefixes(userFragments(cfg));
|
|
@@ -2108,13 +1967,12 @@ async function main(argv) {
|
|
|
2108
1967
|
const child = process.env["CLAUDE_HIGHLIGHT_CMD"] ?? "claude";
|
|
2109
1968
|
const recRaw = known.hlRecord === null ? null : openSync(known.hlRecord + ".raw", "w");
|
|
2110
1969
|
const recOut = known.hlRecord === null ? null : openSync(known.hlRecord + ".out", "w");
|
|
2111
|
-
let [rows, cols] =
|
|
2112
|
-
const pty = ptySpawn(child, known.rest, { rows, cols, env: environ() });
|
|
1970
|
+
let [rows, cols] = winsize();
|
|
1971
|
+
const pty = await ptySpawn(child, known.rest, { rows, cols, env: environ() });
|
|
2113
1972
|
screen.resize(rows, cols);
|
|
2114
|
-
const
|
|
1973
|
+
const restoreStdin = setRawStdin();
|
|
2115
1974
|
process.on("SIGWINCH", () => {
|
|
2116
|
-
|
|
2117
|
-
[rows, cols] = sz ?? [24, 80];
|
|
1975
|
+
[rows, cols] = winsize();
|
|
2118
1976
|
pty.resize(rows, cols);
|
|
2119
1977
|
screen.resize(rows, cols);
|
|
2120
1978
|
});
|
|
@@ -2128,14 +1986,14 @@ async function main(argv) {
|
|
|
2128
1986
|
return;
|
|
2129
1987
|
writeAll(1, out);
|
|
2130
1988
|
if (recOut !== null)
|
|
2131
|
-
|
|
1989
|
+
writeSync2(recOut, out);
|
|
2132
1990
|
};
|
|
2133
1991
|
const closeMenu = () => {
|
|
2134
1992
|
menu = false;
|
|
2135
1993
|
screen.invalidate();
|
|
2136
1994
|
writeAll(1, Buffer.from("\x1B[?25h", "latin1"));
|
|
2137
1995
|
writeFileSync(CONFIG, JSON.stringify(cfg, null, 2));
|
|
2138
|
-
mtime =
|
|
1996
|
+
mtime = statSync2(CONFIG).mtimeMs;
|
|
2139
1997
|
forceRepaint(pty, rows, cols);
|
|
2140
1998
|
};
|
|
2141
1999
|
pty.onData((data) => {
|
|
@@ -2143,7 +2001,7 @@ async function main(argv) {
|
|
|
2143
2001
|
lastData = performance.now();
|
|
2144
2002
|
repainted = false;
|
|
2145
2003
|
if (recRaw !== null)
|
|
2146
|
-
|
|
2004
|
+
writeSync2(recRaw, data);
|
|
2147
2005
|
if (!menu) {
|
|
2148
2006
|
emit(screen.reconcile(hl.feed(data), hl.rules, hl.onlyUnstyled));
|
|
2149
2007
|
} else {
|
|
@@ -2216,15 +2074,14 @@ async function main(argv) {
|
|
|
2216
2074
|
if (toChild.length)
|
|
2217
2075
|
pty.write(Buffer.from(toChild.join(""), "latin1"));
|
|
2218
2076
|
};
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
onStdin(Buffer.from(chunk));
|
|
2077
|
+
process.stdin.on("data", (chunk) => {
|
|
2078
|
+
if (stdinOpen)
|
|
2079
|
+
onStdin(chunk);
|
|
2080
|
+
});
|
|
2081
|
+
process.stdin.on("end", () => {
|
|
2082
|
+
stdinOpen = false;
|
|
2226
2083
|
});
|
|
2227
|
-
|
|
2084
|
+
process.stdin.resume();
|
|
2228
2085
|
const tick = () => {
|
|
2229
2086
|
if (sawData) {
|
|
2230
2087
|
sawData = false;
|
|
@@ -2236,8 +2093,8 @@ async function main(argv) {
|
|
|
2236
2093
|
repainted = true;
|
|
2237
2094
|
forceRepaint(pty, rows, cols);
|
|
2238
2095
|
}
|
|
2239
|
-
if (
|
|
2240
|
-
mtime =
|
|
2096
|
+
if (existsSync2(CONFIG) && statSync2(CONFIG).mtimeMs !== mtime) {
|
|
2097
|
+
mtime = statSync2(CONFIG).mtimeMs;
|
|
2241
2098
|
cfg = loadConfig();
|
|
2242
2099
|
hl.rules = buildRules(cfg);
|
|
2243
2100
|
hl.onlyUnstyled = cfg.prose_only;
|
|
@@ -2249,9 +2106,8 @@ async function main(argv) {
|
|
|
2249
2106
|
const timer = setInterval(tick, IDLE);
|
|
2250
2107
|
await pty.drained;
|
|
2251
2108
|
clearInterval(timer);
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
restoreStdin(old);
|
|
2109
|
+
process.stdin.pause();
|
|
2110
|
+
restoreStdin?.();
|
|
2255
2111
|
try {
|
|
2256
2112
|
emit(hl.drain(true));
|
|
2257
2113
|
} catch {}
|
|
@@ -2259,7 +2115,7 @@ async function main(argv) {
|
|
|
2259
2115
|
pty.destroy();
|
|
2260
2116
|
return code;
|
|
2261
2117
|
}
|
|
2262
|
-
if (
|
|
2118
|
+
if (__require.main == __require.module) {
|
|
2263
2119
|
process.exit(await main(process.argv.slice(2)));
|
|
2264
2120
|
}
|
|
2265
2121
|
export {
|