@dunkinfrunkin/mdcat 0.1.14 → 0.1.16
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 +3 -1
- package/package.json +1 -1
- package/src/cli.js +40 -10
- package/src/concat.js +16 -0
package/README.md
CHANGED
|
@@ -33,7 +33,9 @@ npx @dunkinfrunkin/mdcat README.md
|
|
|
33
33
|
|
|
34
34
|
```sh
|
|
35
35
|
mdcat README.md # open a file
|
|
36
|
+
mdcat file1.md file2.md file3.md # view multiple files
|
|
36
37
|
mdcat --web README.md # render and open in browser
|
|
38
|
+
mdcat -p README.md # plain text output (no TUI, no ANSI)
|
|
37
39
|
mdcat -n README.md # show line numbers
|
|
38
40
|
mdcat --light README.md # force light theme
|
|
39
41
|
mdcat --dark README.md # force dark theme
|
|
@@ -108,7 +110,7 @@ npm install
|
|
|
108
110
|
npm test
|
|
109
111
|
```
|
|
110
112
|
|
|
111
|
-
All PRs must pass `npm test` (
|
|
113
|
+
All PRs must pass `npm test` (97 tests).
|
|
112
114
|
|
|
113
115
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
|
|
114
116
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { renderTokens, setTheme } from "./render.js";
|
|
|
8
8
|
import { launch } from "./tui.js";
|
|
9
9
|
import { toDocx } from "./docx.js";
|
|
10
10
|
import { detectTheme, themeFromArgs, stripThemeArgs } from "./theme.js";
|
|
11
|
+
import { concatFiles } from "./concat.js";
|
|
11
12
|
|
|
12
13
|
marked.use({ gfm: true });
|
|
13
14
|
|
|
@@ -27,9 +28,10 @@ if (args[0] === "--help" || args[0] === "-h") {
|
|
|
27
28
|
console.log(`\n${CAT} ${bold("mdcat")} ${dim(`v${pkg.version}`)}`);
|
|
28
29
|
console.log(`${dim(" markdown pager for your terminal")}\n`);
|
|
29
30
|
console.log(`${bold("Usage:")}`);
|
|
30
|
-
console.log(` mdcat ${dim("<file.md>")}`);
|
|
31
|
+
console.log(` mdcat ${dim("<file.md> [file2.md ...]")}`);
|
|
31
32
|
console.log(` mdcat ${dim("--web <file.md>")} ${dim("# open in browser")}`);
|
|
32
33
|
console.log(` mdcat ${dim("--doc <file.md>")} ${dim("# export to .docx")}`);
|
|
34
|
+
console.log(` mdcat ${dim("-p, --plain")} ${dim("# plain output (no TUI, no ANSI)")}`);
|
|
33
35
|
console.log(` mdcat ${dim("--light")} ${dim("# force light theme")}`);
|
|
34
36
|
console.log(` mdcat ${dim("--dark")} ${dim("# force dark theme")}`);
|
|
35
37
|
console.log(` mdcat ${dim("-n, --number")} ${dim("# show line numbers")}`);
|
|
@@ -50,6 +52,10 @@ const activeTheme = themeFromArgs(args) ?? detectTheme();
|
|
|
50
52
|
args = stripThemeArgs(args);
|
|
51
53
|
setTheme(activeTheme);
|
|
52
54
|
|
|
55
|
+
// Plain mode flag
|
|
56
|
+
const plainMode = args.includes("-p") || args.includes("--plain");
|
|
57
|
+
args = args.filter(a => a !== "-p" && a !== "--plain");
|
|
58
|
+
|
|
53
59
|
// Line numbers flag
|
|
54
60
|
const showLineNumbers = args.includes("-n") || args.includes("--number");
|
|
55
61
|
args = args.filter(a => a !== "-n" && a !== "--number");
|
|
@@ -61,6 +67,22 @@ if (args[0] === "--version" || args[0] === "-v") {
|
|
|
61
67
|
|
|
62
68
|
const MAX_COLS = 100;
|
|
63
69
|
|
|
70
|
+
/** Strip ANSI SGR sequences and OSC 8 hyperlinks for plain text output. */
|
|
71
|
+
function stripAnsi(s) {
|
|
72
|
+
return s
|
|
73
|
+
.replace(/\x1B\]8;;.*?\x1B\\/gs, "")
|
|
74
|
+
.replace(/\x1B\[[0-9;]*m/g, "");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function runPlain(content) {
|
|
78
|
+
const cols = Math.min(process.stdout.columns || 80, MAX_COLS);
|
|
79
|
+
const tokens = marked.lexer(content);
|
|
80
|
+
const lines = renderTokens(tokens, cols);
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
console.log(stripAnsi(line));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
64
86
|
function escapeHtml(s) {
|
|
65
87
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
66
88
|
}
|
|
@@ -158,22 +180,30 @@ if (!process.stdin.isTTY && fileArgs.length === 0) {
|
|
|
158
180
|
process.stdin.on("end", () => {
|
|
159
181
|
if (docMode) exportDocx("stdin", input);
|
|
160
182
|
else if (webMode) openInBrowser("stdin", input);
|
|
183
|
+
else if (plainMode) runPlain(input);
|
|
161
184
|
else runTUI("stdin", input);
|
|
162
185
|
});
|
|
163
186
|
} else if (fileArgs.length === 0) {
|
|
164
|
-
console.error("Usage: mdcat <file.md>");
|
|
187
|
+
console.error("Usage: mdcat <file.md> [file2.md ...]");
|
|
165
188
|
process.exit(1);
|
|
166
189
|
} else {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
190
|
+
const parts = [];
|
|
191
|
+
for (const arg of fileArgs) {
|
|
192
|
+
const filePath = resolve(arg);
|
|
193
|
+
let text;
|
|
194
|
+
try {
|
|
195
|
+
text = readFileSync(filePath, "utf8");
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.error(`mdcat: ${arg}: ${err.code === "ENOENT" ? "No such file" : err.message}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
parts.push({ name: basename(filePath), content: text });
|
|
174
201
|
}
|
|
175
|
-
|
|
202
|
+
|
|
203
|
+
const { title, content } = concatFiles(parts);
|
|
204
|
+
|
|
176
205
|
if (docMode) exportDocx(title, content);
|
|
177
206
|
else if (webMode) openInBrowser(title, content);
|
|
207
|
+
else if (plainMode) runPlain(content);
|
|
178
208
|
else runTUI(title, content);
|
|
179
209
|
}
|
package/src/concat.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concatenate multiple file entries into a single markdown document.
|
|
3
|
+
* Each entry is { name, content }. When there is more than one file,
|
|
4
|
+
* a `## filename` heading and `---` separator are inserted between files.
|
|
5
|
+
* Returns { title, content }.
|
|
6
|
+
*/
|
|
7
|
+
export function concatFiles(parts) {
|
|
8
|
+
if (parts.length === 0) return { title: "", content: "" };
|
|
9
|
+
if (parts.length === 1) return { title: parts[0].name, content: parts[0].content };
|
|
10
|
+
return {
|
|
11
|
+
title: `${parts.length} files`,
|
|
12
|
+
content: parts
|
|
13
|
+
.map(p => `## ${p.name}\n\n${p.content}`)
|
|
14
|
+
.join("\n\n---\n\n"),
|
|
15
|
+
};
|
|
16
|
+
}
|