markdansi 0.1.3 → 0.1.5
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 +23 -7
- package/dist/cli.js +0 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -1
- package/dist/live.d.ts +35 -0
- package/dist/live.js +75 -0
- package/package.json +77 -74
package/README.md
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
# 🎨 Markdansi: Wraps, colors, links—no baggage.
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="./markdansi.png" alt="Markdansi README header" width="1100">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
2
7
|
   
|
|
3
8
|
|
|
4
9
|
Tiny, dependency-light Markdown → ANSI renderer and CLI for modern Node (>=22). Focuses on readable terminal output with sensible wrapping, GFM support (tables, task lists, strikethrough), optional OSC‑8 hyperlinks, and zero built‑in syntax highlighting (pluggable hook). Written in TypeScript, ships ESM.
|
|
@@ -6,14 +11,18 @@ Tiny, dependency-light Markdown → ANSI renderer and CLI for modern Node (>=22)
|
|
|
6
11
|
Published on npm as `markdansi`.
|
|
7
12
|
|
|
8
13
|
## Install
|
|
14
|
+
Grab it from npm; no native deps, so install is instant on Node 22+.
|
|
9
15
|
|
|
10
16
|
```bash
|
|
17
|
+
bun add markdansi
|
|
18
|
+
# or
|
|
11
19
|
pnpm add markdansi
|
|
12
20
|
# or
|
|
13
21
|
npm install markdansi
|
|
14
22
|
```
|
|
15
23
|
|
|
16
24
|
## CLI
|
|
25
|
+
Quick one-shot renderer: pipe Markdown in, ANSI comes out. Flags let you pick width, wrap, colors, links, and table/list styling.
|
|
17
26
|
|
|
18
27
|
```bash
|
|
19
28
|
markdansi [--in FILE] [--out FILE] [--width N] [--no-wrap] [--no-color] [--no-links] [--theme default|dim|bright]
|
|
@@ -27,6 +36,15 @@ markdansi [--in FILE] [--out FILE] [--width N] [--no-wrap] [--no-color] [--no-li
|
|
|
27
36
|
- Lists/quotes: `--list-indent` sets spaces per nesting level (default 2); `--quote-prefix` sets blockquote prefix (default `│ `).
|
|
28
37
|
|
|
29
38
|
## Library
|
|
39
|
+
Use the renderer directly in Node/TS for customizable theming, optional syntax highlighting hooks, and OSC‑8 link control.
|
|
40
|
+
|
|
41
|
+
### ESM / CommonJS
|
|
42
|
+
Markdansi ships ESM (`"type":"module"`). If you’re in CommonJS (or a tool like `tsx` running your script as CJS), prefer dynamic import:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const { render } = await import('markdansi');
|
|
46
|
+
console.log(render('# hello'));
|
|
47
|
+
```
|
|
30
48
|
|
|
31
49
|
```js
|
|
32
50
|
import { render, createRenderer, strip, themes } from 'markdansi';
|
|
@@ -100,12 +118,6 @@ console.log(highlighted('```ts\nconst x: number = 1\n```\n```swift\nlet x = 1\n`
|
|
|
100
118
|
- `codeBox`: draw a box around fenced code (default true); `codeGutter` shows line numbers; `codeWrap` wraps code lines by default.
|
|
101
119
|
- `highlighter(code, lang)`: optional hook to recolor code blocks; must not add/remove newlines.
|
|
102
120
|
|
|
103
|
-
## Status
|
|
104
|
-
|
|
105
|
-
Version: `0.1.2` (released)
|
|
106
|
-
Tests: `pnpm test`
|
|
107
|
-
License: MIT
|
|
108
|
-
|
|
109
121
|
## Notes
|
|
110
122
|
|
|
111
123
|
- Code blocks wrap to the render width by default; disable with `codeWrap=false`. If `lang` is present, a faint `[lang]` label is shown and boxes use unicode borders.
|
|
@@ -113,4 +125,8 @@ License: MIT
|
|
|
113
125
|
- Tables use unicode borders by default, include padding, respect GFM alignment, and truncate long cells with `…` so layouts stay tidy. Turn off truncation with `tableTruncate=false`.
|
|
114
126
|
- Tight vs loose lists follow GFM; task items render `[ ]` / `[x]`.
|
|
115
127
|
|
|
116
|
-
See `docs/spec.md` for full behavior details
|
|
128
|
+
See [`docs/spec.md`](docs/spec.md) for full behavior details.
|
|
129
|
+
|
|
130
|
+
Looking for the Swift port? Check out [Swiftdansi](https://github.com/steipete/Swiftdansi).
|
|
131
|
+
|
|
132
|
+
MIT license.
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { LiveRenderer, LiveRendererOptions } from "./live.js";
|
|
2
|
+
import { createLiveRenderer } from "./live.js";
|
|
1
3
|
import { createRenderer, render as renderMarkdown } from "./render.js";
|
|
2
4
|
import { themes } from "./theme.js";
|
|
3
5
|
import type { RenderOptions, Theme, ThemeName } from "./types.js";
|
|
4
|
-
export { renderMarkdown as render,
|
|
6
|
+
export { createLiveRenderer, createRenderer, renderMarkdown as render, themes };
|
|
5
7
|
export type { RenderOptions, Theme, ThemeName };
|
|
8
|
+
export type { LiveRenderer, LiveRendererOptions };
|
|
6
9
|
/**
|
|
7
10
|
* Render Markdown to plain text (no ANSI, no hyperlinks) while preserving layout/wrapping.
|
|
8
11
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { createLiveRenderer } from "./live.js";
|
|
1
2
|
import { createRenderer, render as renderMarkdown } from "./render.js";
|
|
2
3
|
import { themes } from "./theme.js";
|
|
3
|
-
export { renderMarkdown as render,
|
|
4
|
+
export { createLiveRenderer, createRenderer, renderMarkdown as render, themes };
|
|
4
5
|
/**
|
|
5
6
|
* Render Markdown to plain text (no ANSI, no hyperlinks) while preserving layout/wrapping.
|
|
6
7
|
*/
|
package/dist/live.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type LiveRenderer = {
|
|
2
|
+
render: (input: string) => void;
|
|
3
|
+
finish: () => void;
|
|
4
|
+
};
|
|
5
|
+
export type LiveRendererOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* Function that converts the current full input into a rendered frame.
|
|
8
|
+
* Typically this is Markdansi's `render()` or an app-specific wrapper.
|
|
9
|
+
*/
|
|
10
|
+
renderFrame: (input: string) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Where to write ANSI output (usually `process.stdout.write.bind(process.stdout)`).
|
|
13
|
+
*/
|
|
14
|
+
write: (chunk: string) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Enable terminal "synchronized output" framing (DEC private mode 2026).
|
|
17
|
+
* Most terminals ignore this sequence if unsupported.
|
|
18
|
+
*/
|
|
19
|
+
synchronizedOutput?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Hide cursor during live updates.
|
|
22
|
+
*/
|
|
23
|
+
hideCursor?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Terminal width in columns used for row accounting.
|
|
26
|
+
* If omitted, defaults to 80.
|
|
27
|
+
*/
|
|
28
|
+
width?: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Create a live renderer that repeatedly re-renders the entire buffer and redraws in-place.
|
|
32
|
+
*
|
|
33
|
+
* This is intentionally "terminal plumbing" and renderer-agnostic: you inject `renderFrame()`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createLiveRenderer(options: LiveRendererOptions): LiveRenderer;
|
package/dist/live.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import stringWidth from "string-width";
|
|
2
|
+
import stripAnsi from "strip-ansi";
|
|
3
|
+
const BSU = "\u001b[?2026h";
|
|
4
|
+
const ESU = "\u001b[?2026l";
|
|
5
|
+
const HIDE_CURSOR = "\u001b[?25l";
|
|
6
|
+
const SHOW_CURSOR = "\u001b[?25h";
|
|
7
|
+
const CLEAR_TO_END = "\u001b[0J";
|
|
8
|
+
function cursorUp(lines) {
|
|
9
|
+
if (lines <= 0)
|
|
10
|
+
return "";
|
|
11
|
+
return `\u001b[${lines}A`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a live renderer that repeatedly re-renders the entire buffer and redraws in-place.
|
|
15
|
+
*
|
|
16
|
+
* This is intentionally "terminal plumbing" and renderer-agnostic: you inject `renderFrame()`.
|
|
17
|
+
*/
|
|
18
|
+
export function createLiveRenderer(options) {
|
|
19
|
+
let previousRows = 0;
|
|
20
|
+
let cursorHidden = false;
|
|
21
|
+
const synchronizedOutput = options.synchronizedOutput !== false;
|
|
22
|
+
const hideCursor = options.hideCursor !== false;
|
|
23
|
+
const width = typeof options.width === "number" &&
|
|
24
|
+
Number.isFinite(options.width) &&
|
|
25
|
+
options.width > 0
|
|
26
|
+
? Math.floor(options.width)
|
|
27
|
+
: 80;
|
|
28
|
+
const countRows = (text) => {
|
|
29
|
+
const lines = text.split("\n");
|
|
30
|
+
if (lines.length > 0 && lines.at(-1) === "")
|
|
31
|
+
lines.pop();
|
|
32
|
+
let rows = 0;
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
const visible = stripAnsi(line);
|
|
35
|
+
const w = stringWidth(visible);
|
|
36
|
+
rows += Math.max(1, Math.ceil(Math.max(0, w) / width));
|
|
37
|
+
}
|
|
38
|
+
return rows;
|
|
39
|
+
};
|
|
40
|
+
const render = (input) => {
|
|
41
|
+
const renderedRaw = options.renderFrame(input);
|
|
42
|
+
const rendered = renderedRaw.endsWith("\n")
|
|
43
|
+
? renderedRaw
|
|
44
|
+
: `${renderedRaw}\n`;
|
|
45
|
+
const lines = rendered.split("\n");
|
|
46
|
+
if (lines.length > 0 && lines.at(-1) === "")
|
|
47
|
+
lines.pop();
|
|
48
|
+
const newRows = countRows(rendered);
|
|
49
|
+
let frame = "";
|
|
50
|
+
if (hideCursor && !cursorHidden) {
|
|
51
|
+
frame += HIDE_CURSOR;
|
|
52
|
+
cursorHidden = true;
|
|
53
|
+
}
|
|
54
|
+
if (synchronizedOutput)
|
|
55
|
+
frame += BSU;
|
|
56
|
+
frame += previousRows > 0 ? `${cursorUp(previousRows)}\r` : "\r";
|
|
57
|
+
frame += CLEAR_TO_END;
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
frame += "\r";
|
|
60
|
+
frame += line;
|
|
61
|
+
frame += "\r\n";
|
|
62
|
+
}
|
|
63
|
+
if (synchronizedOutput)
|
|
64
|
+
frame += ESU;
|
|
65
|
+
options.write(frame);
|
|
66
|
+
previousRows = newRows;
|
|
67
|
+
};
|
|
68
|
+
const finish = () => {
|
|
69
|
+
if (hideCursor && cursorHidden) {
|
|
70
|
+
options.write(SHOW_CURSOR);
|
|
71
|
+
cursorHidden = false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return { render, finish };
|
|
75
|
+
}
|
package/package.json
CHANGED
|
@@ -1,75 +1,78 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
2
|
+
"name": "markdansi",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Tiny dependency-light markdown to ANSI converter.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./cli": "./dist/cli.js"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"markdansi": "dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "pnpm lint && pnpm test && pnpm compile",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"lint": "rm -rf dist coverage && biome check .",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:coverage": "vitest run --coverage",
|
|
24
|
+
"types": "tsc -p tsconfig.json --emitDeclarationOnly",
|
|
25
|
+
"compile": "tsc -p tsconfig.json",
|
|
26
|
+
"prepare": "pnpm compile",
|
|
27
|
+
"markdansi": "tsx src/cli.ts"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"markdown",
|
|
31
|
+
"ansi",
|
|
32
|
+
"terminal",
|
|
33
|
+
"cli"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/steipete/Markdansi.git"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/steipete/Markdansi/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/steipete/Markdansi#readme",
|
|
46
|
+
"author": "Peter Steinberger",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"sideEffects": false,
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"README.md",
|
|
52
|
+
"docs/spec.md",
|
|
53
|
+
"package.json",
|
|
54
|
+
"tsconfig.json",
|
|
55
|
+
".biome.json"
|
|
56
|
+
],
|
|
57
|
+
"types": "dist/index.d.ts",
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"chalk": "^5.6.2",
|
|
60
|
+
"mdast-util-from-markdown": "^2.0.2",
|
|
61
|
+
"mdast-util-gfm": "^3.1.0",
|
|
62
|
+
"micromark": "^4.0.2",
|
|
63
|
+
"micromark-extension-gfm": "^3.0.0",
|
|
64
|
+
"micromark-util-combine-extensions": "^2.0.1",
|
|
65
|
+
"string-width": "^8.1.0",
|
|
66
|
+
"strip-ansi": "^7.1.2",
|
|
67
|
+
"supports-hyperlinks": "^4.3.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@biomejs/biome": "^2.3.5",
|
|
71
|
+
"@types/mdast": "^4.0.4",
|
|
72
|
+
"@types/node": "^24.10.1",
|
|
73
|
+
"@vitest/coverage-v8": "^4.0.9",
|
|
74
|
+
"tsx": "^4.20.6",
|
|
75
|
+
"typescript": "^5.9.3",
|
|
76
|
+
"vitest": "^4.0.9"
|
|
77
|
+
}
|
|
78
|
+
}
|