@dunkinfrunkin/mdcat 0.1.11 → 0.1.12
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 +20 -1
- package/package.json +1 -1
- package/src/cli.js +30 -18
- package/src/render.js +91 -39
- package/src/theme.js +54 -0
- package/src/tui.js +39 -13
package/README.md
CHANGED
|
@@ -32,6 +32,8 @@ npx @dunkinfrunkin/mdcat README.md
|
|
|
32
32
|
```sh
|
|
33
33
|
mdcat README.md # open a file
|
|
34
34
|
mdcat --web README.md # render and open in browser
|
|
35
|
+
mdcat --light README.md # force light theme
|
|
36
|
+
mdcat --dark README.md # force dark theme
|
|
35
37
|
cat CHANGELOG.md | mdcat # pipe from stdin
|
|
36
38
|
curl -s https://… | mdcat # pipe from curl
|
|
37
39
|
mdcat --help # show help
|
|
@@ -54,6 +56,23 @@ mdcat --version # show version
|
|
|
54
56
|
| `Esc` | Clear search |
|
|
55
57
|
| Mouse wheel | Scroll three lines |
|
|
56
58
|
|
|
59
|
+
## Theme
|
|
60
|
+
|
|
61
|
+
mdcat auto-detects your terminal's light or dark background and adjusts colors accordingly.
|
|
62
|
+
|
|
63
|
+
**Auto-detection** checks (in order):
|
|
64
|
+
1. `MDCAT_THEME` env var (`light` or `dark`)
|
|
65
|
+
2. `COLORFGBG` env var (set by many terminals)
|
|
66
|
+
3. macOS system appearance (light/dark mode)
|
|
67
|
+
4. Falls back to dark
|
|
68
|
+
|
|
69
|
+
**Override manually:**
|
|
70
|
+
```sh
|
|
71
|
+
mdcat --light README.md # force light theme
|
|
72
|
+
mdcat --dark README.md # force dark theme
|
|
73
|
+
MDCAT_THEME=light mdcat file.md # env var override
|
|
74
|
+
```
|
|
75
|
+
|
|
57
76
|
## What it renders
|
|
58
77
|
|
|
59
78
|
| Element | Rendering |
|
|
@@ -62,7 +81,7 @@ mdcat --version # show version
|
|
|
62
81
|
| H2 | Bold blue with underline |
|
|
63
82
|
| H3–H6 | Green → yellow → cyan → dim |
|
|
64
83
|
| **Bold** / _italic_ / ~~strike~~ | Standard ANSI |
|
|
65
|
-
| `inline code` | Amber on dark
|
|
84
|
+
| `inline code` | Amber on dark bg / brown on light bg |
|
|
66
85
|
| Fenced code | Bordered box with syntax highlighting |
|
|
67
86
|
| Blockquotes | Amber `▌` bar, dim italic |
|
|
68
87
|
| Unordered lists | `●` / `○` / `‣` bullets |
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4,9 +4,10 @@ import { resolve, basename } from "path";
|
|
|
4
4
|
import { tmpdir } from "os";
|
|
5
5
|
import { execFileSync } from "child_process";
|
|
6
6
|
import { marked, Marked } from "marked";
|
|
7
|
-
import { renderTokens } from "./render.js";
|
|
7
|
+
import { renderTokens, setTheme } from "./render.js";
|
|
8
8
|
import { launch } from "./tui.js";
|
|
9
9
|
import { toDocx } from "./docx.js";
|
|
10
|
+
import { detectTheme, themeFromArgs, stripThemeArgs } from "./theme.js";
|
|
10
11
|
|
|
11
12
|
marked.use({ gfm: true });
|
|
12
13
|
|
|
@@ -20,7 +21,7 @@ const bold = s => `${E}[1m${s}${E}[0m`;
|
|
|
20
21
|
|
|
21
22
|
const CAT = ` ${gray("/\\")}${blue("(o.o)")}${gray("/\\")} `;
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
let args = process.argv.slice(2);
|
|
24
25
|
|
|
25
26
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
26
27
|
console.log(`\n${CAT} ${bold("mdcat")} ${dim(`v${pkg.version}`)}`);
|
|
@@ -29,7 +30,12 @@ if (args[0] === "--help" || args[0] === "-h") {
|
|
|
29
30
|
console.log(` mdcat ${dim("<file.md>")}`);
|
|
30
31
|
console.log(` mdcat ${dim("--web <file.md>")} ${dim("# open in browser")}`);
|
|
31
32
|
console.log(` mdcat ${dim("--doc <file.md>")} ${dim("# export to .docx")}`);
|
|
33
|
+
console.log(` mdcat ${dim("--light")} ${dim("# force light theme")}`);
|
|
34
|
+
console.log(` mdcat ${dim("--dark")} ${dim("# force dark theme")}`);
|
|
32
35
|
console.log(` cat file.md ${dim("|")} mdcat\n`);
|
|
36
|
+
console.log(`${bold("Theme:")}`);
|
|
37
|
+
console.log(` Auto-detects terminal theme. Override with ${blue("--light")} / ${blue("--dark")}`);
|
|
38
|
+
console.log(` or set ${blue("MDCAT_THEME")}=light|dark\n`);
|
|
33
39
|
console.log(`${bold("Keys:")}`);
|
|
34
40
|
console.log(` ${blue("/")} search ${blue("n/N")} next/prev match`);
|
|
35
41
|
console.log(` ${blue("j/k")} ${dim("↑↓")} scroll line ${blue("space/b")} page down/up`);
|
|
@@ -38,6 +44,11 @@ if (args[0] === "--help" || args[0] === "-h") {
|
|
|
38
44
|
process.exit(0);
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
// Resolve theme: CLI flag > env var > auto-detect
|
|
48
|
+
const activeTheme = themeFromArgs(args) ?? detectTheme();
|
|
49
|
+
args = stripThemeArgs(args);
|
|
50
|
+
setTheme(activeTheme);
|
|
51
|
+
|
|
41
52
|
if (args[0] === "--version" || args[0] === "-v") {
|
|
42
53
|
console.log(`${CAT} ${bold("mdcat")} ${dim(`v${pkg.version}`)}`);
|
|
43
54
|
process.exit(0);
|
|
@@ -61,6 +72,7 @@ function openInBrowser(title, content) {
|
|
|
61
72
|
},
|
|
62
73
|
});
|
|
63
74
|
const html = webMarked.parse(content);
|
|
75
|
+
const isLight = activeTheme === "light";
|
|
64
76
|
const page = `<!DOCTYPE html>
|
|
65
77
|
<html lang="en">
|
|
66
78
|
<head>
|
|
@@ -70,31 +82,31 @@ function openInBrowser(title, content) {
|
|
|
70
82
|
<style>
|
|
71
83
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
72
84
|
body {
|
|
73
|
-
background: #282c34;
|
|
74
|
-
color: #abb2bf;
|
|
85
|
+
background: ${isLight ? "#fafafa" : "#282c34"};
|
|
86
|
+
color: ${isLight ? "#383a42" : "#abb2bf"};
|
|
75
87
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
76
88
|
font-size: 16px;
|
|
77
89
|
line-height: 1.7;
|
|
78
90
|
padding: 2rem 1rem;
|
|
79
91
|
}
|
|
80
92
|
.wrap { max-width: 760px; margin: 0 auto; }
|
|
81
|
-
h1, h2, h3, h4, h5, h6 { color: #e5c07b; margin: 1.5rem 0 0.5rem; font-weight: 700; }
|
|
82
|
-
h1 { font-size: 2rem; color: #c678dd; border-bottom: 2px solid #3e4451; padding-bottom: 0.4rem; }
|
|
83
|
-
h2 { font-size: 1.4rem; color: #61afef; border-bottom: 1px solid #3e4451; padding-bottom: 0.3rem; }
|
|
84
|
-
h3 { font-size: 1.15rem; color: #98c379; }
|
|
93
|
+
h1, h2, h3, h4, h5, h6 { color: ${isLight ? "#c18401" : "#e5c07b"}; margin: 1.5rem 0 0.5rem; font-weight: 700; }
|
|
94
|
+
h1 { font-size: 2rem; color: ${isLight ? "#a626a4" : "#c678dd"}; border-bottom: 2px solid ${isLight ? "#d3d3d8" : "#3e4451"}; padding-bottom: 0.4rem; }
|
|
95
|
+
h2 { font-size: 1.4rem; color: ${isLight ? "#4078f2" : "#61afef"}; border-bottom: 1px solid ${isLight ? "#d3d3d8" : "#3e4451"}; padding-bottom: 0.3rem; }
|
|
96
|
+
h3 { font-size: 1.15rem; color: ${isLight ? "#50a14f" : "#98c379"}; }
|
|
85
97
|
p { margin: 0.75rem 0; }
|
|
86
|
-
a { color: #61afef; text-decoration: underline; }
|
|
87
|
-
code { background: #2c313a; color: #e5c07b; padding: 0.15em 0.4em; border-radius: 4px; font-size: 0.9em; font-family: 'JetBrains Mono', 'Fira Code', monospace; }
|
|
88
|
-
pre { background: #21252b; border: 1px solid #3e4451; border-radius: 8px; padding: 1rem 1.25rem; overflow-x: auto; margin: 1rem 0; }
|
|
89
|
-
pre code { background: none; color: #abb2bf; padding: 0; font-size: 0.875rem; }
|
|
90
|
-
blockquote { border-left: 3px solid #e5c07b; padding: 0.5rem 1rem; margin: 1rem 0; color: #5c6370; font-style: italic; }
|
|
98
|
+
a { color: ${isLight ? "#4078f2" : "#61afef"}; text-decoration: underline; }
|
|
99
|
+
code { background: ${isLight ? "#e8e8e8" : "#2c313a"}; color: ${isLight ? "#986801" : "#e5c07b"}; padding: 0.15em 0.4em; border-radius: 4px; font-size: 0.9em; font-family: 'JetBrains Mono', 'Fira Code', monospace; }
|
|
100
|
+
pre { background: ${isLight ? "#f0f0f0" : "#21252b"}; border: 1px solid ${isLight ? "#d3d3d8" : "#3e4451"}; border-radius: 8px; padding: 1rem 1.25rem; overflow-x: auto; margin: 1rem 0; }
|
|
101
|
+
pre code { background: none; color: ${isLight ? "#383a42" : "#abb2bf"}; padding: 0; font-size: 0.875rem; }
|
|
102
|
+
blockquote { border-left: 3px solid ${isLight ? "#c18401" : "#e5c07b"}; padding: 0.5rem 1rem; margin: 1rem 0; color: ${isLight ? "#696c77" : "#5c6370"}; font-style: italic; }
|
|
91
103
|
ul, ol { padding-left: 1.5rem; margin: 0.75rem 0; }
|
|
92
104
|
li { margin: 0.25rem 0; }
|
|
93
105
|
table { border-collapse: collapse; width: 100%; margin: 1rem 0; }
|
|
94
|
-
th { background: #21252b; color: #61afef; padding: 0.5rem 0.75rem; border: 1px solid #3e4451; text-align: left; }
|
|
95
|
-
td { padding: 0.5rem 0.75rem; border: 1px solid #3e4451; }
|
|
96
|
-
tr:nth-child(even) { background: #2c313a; }
|
|
97
|
-
hr { border: none; border-top: 1px solid #3e4451; margin: 1.5rem 0; }
|
|
106
|
+
th { background: ${isLight ? "#e8e8e8" : "#21252b"}; color: ${isLight ? "#4078f2" : "#61afef"}; padding: 0.5rem 0.75rem; border: 1px solid ${isLight ? "#d3d3d8" : "#3e4451"}; text-align: left; }
|
|
107
|
+
td { padding: 0.5rem 0.75rem; border: 1px solid ${isLight ? "#d3d3d8" : "#3e4451"}; }
|
|
108
|
+
tr:nth-child(even) { background: ${isLight ? "#f0f0f0" : "#2c313a"}; }
|
|
109
|
+
hr { border: none; border-top: 1px solid ${isLight ? "#d3d3d8" : "#3e4451"}; margin: 1.5rem 0; }
|
|
98
110
|
img { max-width: 100%; border-radius: 6px; }
|
|
99
111
|
</style>
|
|
100
112
|
</head>
|
|
@@ -116,7 +128,7 @@ function runTUI(title, content) {
|
|
|
116
128
|
const cols = Math.min(termCols, MAX_COLS);
|
|
117
129
|
const tokens = marked.lexer(content);
|
|
118
130
|
const lines = renderTokens(tokens, cols, termCols > MAX_COLS ? termCols : undefined);
|
|
119
|
-
launch(title, lines);
|
|
131
|
+
launch(title, lines, activeTheme);
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
// --web / --doc flags
|
package/src/render.js
CHANGED
|
@@ -4,44 +4,96 @@ import { highlight as cliHighlight } from "cli-highlight";
|
|
|
4
4
|
|
|
5
5
|
const chalk = new Chalk({ level: 3 });
|
|
6
6
|
|
|
7
|
-
// ───
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
7
|
+
// ─── Color palettes ─────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
function darkPalette() {
|
|
10
|
+
return {
|
|
11
|
+
// Headings
|
|
12
|
+
h1: chalk.hex("#c678dd").bold, // purple
|
|
13
|
+
h2: chalk.hex("#61afef").bold, // blue
|
|
14
|
+
h3: chalk.hex("#98c379").bold, // green
|
|
15
|
+
h4: chalk.hex("#e5c07b").bold, // yellow
|
|
16
|
+
h5: chalk.hex("#56b6c2"), // cyan
|
|
17
|
+
h6: chalk.dim,
|
|
18
|
+
// Inline
|
|
19
|
+
strong: chalk.bold.white,
|
|
20
|
+
em: chalk.italic,
|
|
21
|
+
del: chalk.strikethrough.dim,
|
|
22
|
+
code: chalk.hex("#e5c07b").bgHex("#2a2a2a"), // amber on dark bg
|
|
23
|
+
link: chalk.hex("#61afef").underline,
|
|
24
|
+
image: chalk.dim,
|
|
25
|
+
badge: chalk.hex("#abb2bf"),
|
|
26
|
+
// Code block
|
|
27
|
+
border: chalk.dim,
|
|
28
|
+
codeLang: chalk.hex("#e5c07b").dim,
|
|
29
|
+
// Blockquote
|
|
30
|
+
bqBar: chalk.hex("#e5c07b"),
|
|
31
|
+
bqText: chalk.dim.italic,
|
|
32
|
+
// Lists
|
|
33
|
+
bullet0: chalk.hex("#61afef"), // blue ● depth 0
|
|
34
|
+
bullet1: chalk.dim, // dim ○ depth 1
|
|
35
|
+
bullet2: chalk.dim, // very dim ‣ depth 2+
|
|
36
|
+
ordered: chalk.hex("#56b6c2"), // cyan number
|
|
37
|
+
taskDone: chalk.hex("#98c379"), // green ☑
|
|
38
|
+
taskTodo: chalk.dim, // dim ☐
|
|
39
|
+
// Table
|
|
40
|
+
tableBorder: chalk.dim,
|
|
41
|
+
tableHead: chalk.hex("#61afef").bold,
|
|
42
|
+
tableCell: chalk.reset,
|
|
43
|
+
// HR
|
|
44
|
+
hr: chalk.dim,
|
|
45
|
+
// Paragraph text
|
|
46
|
+
fg: chalk.reset,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function lightPalette() {
|
|
51
|
+
return {
|
|
52
|
+
// Headings
|
|
53
|
+
h1: chalk.hex("#a626a4").bold, // magenta
|
|
54
|
+
h2: chalk.hex("#4078f2").bold, // blue
|
|
55
|
+
h3: chalk.hex("#50a14f").bold, // green
|
|
56
|
+
h4: chalk.hex("#c18401").bold, // dark yellow
|
|
57
|
+
h5: chalk.hex("#0184bc"), // teal
|
|
58
|
+
h6: chalk.hex("#696c77"), // gray
|
|
59
|
+
// Inline
|
|
60
|
+
strong: chalk.bold.black,
|
|
61
|
+
em: chalk.italic,
|
|
62
|
+
del: chalk.strikethrough.hex("#696c77"),
|
|
63
|
+
code: chalk.hex("#986801").bgHex("#e8e8e8"), // brown on light gray
|
|
64
|
+
link: chalk.hex("#4078f2").underline,
|
|
65
|
+
image: chalk.hex("#696c77"),
|
|
66
|
+
badge: chalk.hex("#383a42"),
|
|
67
|
+
// Code block
|
|
68
|
+
border: chalk.hex("#a0a1a7"),
|
|
69
|
+
codeLang: chalk.hex("#986801"),
|
|
70
|
+
// Blockquote
|
|
71
|
+
bqBar: chalk.hex("#c18401"),
|
|
72
|
+
bqText: chalk.hex("#696c77").italic,
|
|
73
|
+
// Lists
|
|
74
|
+
bullet0: chalk.hex("#4078f2"), // blue ● depth 0
|
|
75
|
+
bullet1: chalk.hex("#a0a1a7"), // gray ○ depth 1
|
|
76
|
+
bullet2: chalk.hex("#a0a1a7"), // gray ‣ depth 2+
|
|
77
|
+
ordered: chalk.hex("#0184bc"), // teal number
|
|
78
|
+
taskDone: chalk.hex("#50a14f"), // green ☑
|
|
79
|
+
taskTodo: chalk.hex("#a0a1a7"), // gray ☐
|
|
80
|
+
// Table
|
|
81
|
+
tableBorder: chalk.hex("#a0a1a7"),
|
|
82
|
+
tableHead: chalk.hex("#4078f2").bold,
|
|
83
|
+
tableCell: chalk.reset,
|
|
84
|
+
// HR
|
|
85
|
+
hr: chalk.hex("#a0a1a7"),
|
|
86
|
+
// Paragraph text
|
|
87
|
+
fg: chalk.reset,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let c = darkPalette();
|
|
92
|
+
|
|
93
|
+
/** Set the active color palette. Call before renderTokens(). */
|
|
94
|
+
export function setTheme(theme) {
|
|
95
|
+
c = theme === "light" ? lightPalette() : darkPalette();
|
|
96
|
+
}
|
|
45
97
|
|
|
46
98
|
const MARGIN = " "; // 2-space left margin
|
|
47
99
|
|
|
@@ -124,7 +176,7 @@ function inline(tokens) {
|
|
|
124
176
|
if (tok.tokens?.length === 1 && tok.tokens[0].type === "image") {
|
|
125
177
|
const alt = tok.tokens[0].text || tok.tokens[0].alt || "";
|
|
126
178
|
if (alt) {
|
|
127
|
-
const badge = chalk.dim("[") +
|
|
179
|
+
const badge = chalk.dim("[") + c.badge(alt) + chalk.dim("]");
|
|
128
180
|
return `\x1B]8;;${href}\x1B\\${badge}\x1B]8;;\x1B\\`;
|
|
129
181
|
}
|
|
130
182
|
}
|
package/src/theme.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Detect whether the terminal is using a light or dark background.
|
|
5
|
+
*
|
|
6
|
+
* Checks (in order):
|
|
7
|
+
* 1. MDCAT_THEME env var ("light" or "dark")
|
|
8
|
+
* 2. COLORFGBG env var (e.g. "15;0" → dark, "0;15" → light)
|
|
9
|
+
* 3. macOS Appearance via `defaults read`
|
|
10
|
+
* 4. Falls back to "dark"
|
|
11
|
+
*/
|
|
12
|
+
export function detectTheme() {
|
|
13
|
+
// 1. Explicit override
|
|
14
|
+
const env = (process.env.MDCAT_THEME || "").toLowerCase();
|
|
15
|
+
if (env === "light" || env === "dark") return env;
|
|
16
|
+
|
|
17
|
+
// 2. COLORFGBG — "fg;bg" where bg >= 8 usually means light
|
|
18
|
+
const colorfgbg = process.env.COLORFGBG;
|
|
19
|
+
if (colorfgbg) {
|
|
20
|
+
const parts = colorfgbg.split(";");
|
|
21
|
+
const bg = parseInt(parts[parts.length - 1], 10);
|
|
22
|
+
if (!isNaN(bg) && bg >= 8) return "light";
|
|
23
|
+
if (!isNaN(bg) && bg < 8) return "dark";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 3. macOS: check system appearance
|
|
27
|
+
if (process.platform === "darwin") {
|
|
28
|
+
try {
|
|
29
|
+
const result = execSync(
|
|
30
|
+
"defaults read -g AppleInterfaceStyle 2>/dev/null",
|
|
31
|
+
{ encoding: "utf8", timeout: 500 }
|
|
32
|
+
).trim();
|
|
33
|
+
// "Dark" means dark mode; absence or error means light
|
|
34
|
+
return result === "Dark" ? "dark" : "light";
|
|
35
|
+
} catch {
|
|
36
|
+
// Key missing → light mode on macOS
|
|
37
|
+
return "light";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return "dark";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Check CLI args for --light or --dark flags, returns the flag value or null. */
|
|
45
|
+
export function themeFromArgs(args) {
|
|
46
|
+
if (args.includes("--light")) return "light";
|
|
47
|
+
if (args.includes("--dark")) return "dark";
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Remove --light / --dark from an args array. */
|
|
52
|
+
export function stripThemeArgs(args) {
|
|
53
|
+
return args.filter(a => a !== "--light" && a !== "--dark");
|
|
54
|
+
}
|
package/src/tui.js
CHANGED
|
@@ -12,17 +12,36 @@ const ERASE_L = `${ESC}[2K`;
|
|
|
12
12
|
const RESET = `${ESC}[0m`;
|
|
13
13
|
const move = (r, c) => `${ESC}[${r};${c}H`;
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
const
|
|
17
|
-
chromeBg: `${ESC}[48;2;33;37;43m`, // #21252b
|
|
15
|
+
// Color palettes for TUI chrome (24-bit ANSI)
|
|
16
|
+
const DARK = {
|
|
17
|
+
chromeBg: `${ESC}[48;2;33;37;43m`, // #21252b
|
|
18
18
|
badge: `${ESC}[38;2;198;120;221m`, // #c678dd — purple badge
|
|
19
19
|
titleFg: `${ESC}[97m`, // bright white — filename
|
|
20
|
-
dimFg: `${ESC}[38;2;92;99;112m`, // #5c6370
|
|
21
|
-
accentFg: `${ESC}[38;2;97;175;239m`, // #61afef
|
|
22
|
-
matchFg: `${ESC}[38;2;229;192;123m`, // #e5c07b
|
|
23
|
-
greenFg: `${ESC}[38;2;152;195;121m`, // #98c379
|
|
24
|
-
redFg: `${ESC}[38;2;224;108;117m`, // #e06c75
|
|
25
|
-
otherMatchFg:`${ESC}[38;2;92;99;112m`, // #5c6370
|
|
20
|
+
dimFg: `${ESC}[38;2;92;99;112m`, // #5c6370
|
|
21
|
+
accentFg: `${ESC}[38;2;97;175;239m`, // #61afef
|
|
22
|
+
matchFg: `${ESC}[38;2;229;192;123m`, // #e5c07b
|
|
23
|
+
greenFg: `${ESC}[38;2;152;195;121m`, // #98c379
|
|
24
|
+
redFg: `${ESC}[38;2;224;108;117m`, // #e06c75
|
|
25
|
+
otherMatchFg:`${ESC}[38;2;92;99;112m`, // #5c6370
|
|
26
|
+
hlMatch: `${ESC}[48;2;62;68;82m${ESC}[38;2;229;192;123m`,
|
|
27
|
+
hlCurrent: `${ESC}[48;2;229;192;123m${ESC}[38;2;0;0;0m`,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const LIGHT = {
|
|
31
|
+
chromeBg: `${ESC}[48;2;228;228;228m`, // #e4e4e4
|
|
32
|
+
badge: `${ESC}[38;2;166;38;164m`, // #a626a4 — magenta badge
|
|
33
|
+
titleFg: `${ESC}[30m`, // black — filename
|
|
34
|
+
dimFg: `${ESC}[38;2;105;108;119m`, // #696c77
|
|
35
|
+
accentFg: `${ESC}[38;2;64;120;242m`, // #4078f2
|
|
36
|
+
matchFg: `${ESC}[38;2;152;104;1m`, // #986801
|
|
37
|
+
greenFg: `${ESC}[38;2;80;161;79m`, // #50a14f
|
|
38
|
+
redFg: `${ESC}[38;2;228;86;73m`, // #e45649
|
|
39
|
+
otherMatchFg:`${ESC}[38;2;105;108;119m`, // #696c77
|
|
40
|
+
hlMatch: `${ESC}[48;2;209;226;255m${ESC}[38;2;64;120;242m`,
|
|
41
|
+
hlCurrent: `${ESC}[48;2;64;120;242m${ESC}[38;2;255;255;255m`,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
let C = { ...DARK,
|
|
26
45
|
bold: `${ESC}[1m`,
|
|
27
46
|
dim: `${ESC}[2m`,
|
|
28
47
|
italic: `${ESC}[3m`,
|
|
@@ -42,9 +61,9 @@ function plen(s) { return s.replace(/\x1B\[[0-9;]*m/g, "").length; }
|
|
|
42
61
|
|
|
43
62
|
// Highlight all occurrences of `query` within an ANSI-coloured line.
|
|
44
63
|
// Walks the string char-by-char so ANSI escape sequences don't shift offsets.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const HL_OFF
|
|
64
|
+
let HL_MATCH = C.hlMatch;
|
|
65
|
+
let HL_CURRENT = C.hlCurrent;
|
|
66
|
+
const HL_OFF = `${ESC}[49m${ESC}[39m`;
|
|
48
67
|
|
|
49
68
|
function highlightInLine(line, query, isCurrent) {
|
|
50
69
|
if (!query) return line;
|
|
@@ -110,7 +129,14 @@ function copyText(text) {
|
|
|
110
129
|
catch { /* not on macOS or pbcopy unavailable */ }
|
|
111
130
|
}
|
|
112
131
|
|
|
113
|
-
export function launch(title, lines) {
|
|
132
|
+
export function launch(title, lines, theme) {
|
|
133
|
+
// Apply theme to TUI chrome
|
|
134
|
+
if (theme === "light") {
|
|
135
|
+
const pal = LIGHT;
|
|
136
|
+
Object.assign(C, pal);
|
|
137
|
+
HL_MATCH = pal.hlMatch;
|
|
138
|
+
HL_CURRENT = pal.hlCurrent;
|
|
139
|
+
}
|
|
114
140
|
// Viewport state
|
|
115
141
|
let offset = 0;
|
|
116
142
|
|