No description
- TypeScript 93.2%
- HTML 5.5%
- JavaScript 1.3%
| example | ||
| src | ||
| test | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.js | ||
canvark
Parse markdown text into a flat list of canvas-drawable primitives.
parseMarkdown(md) → Drawable { parts, width, height }, then draw the parts in order with draw(ctx, drawable). No DOM required to parse (text measurement falls back to an estimate in Node); images are drawn only if you supply preloaded elements.
Inspired by the architecture of canvas-markdown (lexer → layout parts → render), rewritten as a clean, dependency-light TypeScript library.
Install / build
npm install
npm run build # emits dist/ (ESM + .d.ts)
npm test # node --test suite (builds first)
Usage
import { parseMarkdown, draw, renderToCanvas } from 'canvark';
const md = `# Hello
Some **bold** text with a \`code span\` and a [link](https://example.com).
\`\`\`js
console.log("hi");
\`\`\`
`;
const drawable = parseMarkdown(md, { width: 600 });
// drawable = { parts: DrawPart[], width: 600, height: 172 }
// Manual draw:
const ctx = canvas.getContext('2d')!;
draw(ctx, drawable);
// Or one-shot (sizes the canvas for you, handles devicePixelRatio):
renderToCanvas(canvas, md, { width: 600, padding: 16 });
Drawable parts
Each part is plain data — you can inspect, transform (offset/scale), animate, or draw them yourself:
| type | fields |
|---|---|
text |
x, y, text, font, fillStyle, textBaseline, width, height |
rect |
x, y, width, height, radius, fillStyle, strokeStyle, lineWidth |
line |
x, y, x2, y2, strokeStyle, lineWidth |
ellipse |
x, y, radius, fillStyle |
image |
x, y, src, width, height (drawn only if draw gets it preloaded) |
Options
parseMarkdown(md, {
width: 800, // content width, text wraps beyond it
theme: { // any partial theme override
fontSize: 18,
fontFamily: 'Inter, sans-serif',
colors: { link: '#0969da' },
},
measureText: (text, font) => number, // custom measurement
});
draw(ctx, drawable, {
images: new Map([['https://…/i.png', htmlImageElement]]),
});
Supported markdown
- Headings (
#…######) - Paragraphs with word wrap
- Inline: bold, italic,
strikethrough,code span, [links], images,<br> - Fenced code blocks (background + border, monospace)
- Blockquotes (left bar, muted text, nested blocks)
- Ordered & unordered lists (nested)
- Horizontal rules
- Tables (simple, monospace, bordered)
How it works
marked.lexer(md)tokenizes the markdown.- A recursive layout pass walks block → inline tokens, maintaining a text cursor and emitting positioned parts (widths come from
measureText). draw()replays the parts onto a 2D context.
src/
index.ts public API + renderToCanvas
parse.ts lexer → Drawable (the layout engine)
draw.ts Drawable → canvas ctx
measure.ts text measurement (canvas or headless estimate)
theme.ts default visual theme
types.ts DrawPart / Drawable / Theme types