ink-markdown-es 1.0.2 → 1.2.0
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 +47 -26
- package/dist/highlight.d.ts +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +287 -40
- package/dist/types.d.ts +16 -2
- package/dist/utils.d.ts +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -21,9 +21,6 @@ bun add ink-markdown-es # bun
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
```tsx
|
|
24
|
-
import Markdown from "ink-markdown-es";
|
|
25
|
-
import { render } from "ink";
|
|
26
|
-
|
|
27
24
|
const text = `# Hello World
|
|
28
25
|
|
|
29
26
|
This is a show case.
|
|
@@ -34,12 +31,27 @@ It's very fast!
|
|
|
34
31
|
- Support custom renderers
|
|
35
32
|
- **Bold text** and *italic text*
|
|
36
33
|
- Inline \`code\` support
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
- **Syntax highlighting** for code blocks powered by highlight.js
|
|
35
|
+
|
|
36
|
+
### Code Block with Syntax Highlighting
|
|
37
|
+
|
|
38
|
+
\`\`\`typescript
|
|
39
|
+
interface User {
|
|
40
|
+
id: number;
|
|
41
|
+
name: string;
|
|
42
|
+
email: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const user: User = {
|
|
46
|
+
id: 1,
|
|
47
|
+
name: "Alice",
|
|
48
|
+
email: "alice@example.com"
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
async function fetchUser(id: number): Promise<User> {
|
|
52
|
+
const response = await fetch(\`/api/users/\${id}\`);
|
|
53
|
+
return response.json();
|
|
54
|
+
}
|
|
43
55
|
\`\`\`
|
|
44
56
|
|
|
45
57
|
> This is a blockquote
|
|
@@ -59,25 +71,33 @@ Check out [this link](https://example.com) for more info.
|
|
|
59
71
|
| Bob | 30 |
|
|
60
72
|
`;
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
const TestApp = () => {
|
|
75
|
+
useInput(() => {});
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Markdown
|
|
79
|
+
showSharp
|
|
80
|
+
theme="dracula"
|
|
81
|
+
renderers={{
|
|
82
|
+
h1: (text) => (
|
|
83
|
+
<Box padding={1} borderStyle="round" borderDimColor>
|
|
84
|
+
<Text bold color="greenBright">
|
|
85
|
+
{text}
|
|
86
|
+
</Text>
|
|
87
|
+
</Box>
|
|
88
|
+
),
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{text}
|
|
92
|
+
</Markdown>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
render(<TestApp />);
|
|
97
|
+
|
|
78
98
|
```
|
|
79
99
|
|
|
80
|
-
<img width="
|
|
100
|
+
<img width="1904" height="964" alt="image" src="https://github.com/user-attachments/assets/bed0c942-6d08-4f24-a42a-4999bdf1fc85" />
|
|
81
101
|
|
|
82
102
|
## Props
|
|
83
103
|
|
|
@@ -86,6 +106,7 @@ render(
|
|
|
86
106
|
- `styles` (BlockStyles, optional): Custom styles for markdown blocks.
|
|
87
107
|
- `renderers` (BlockRenderers, optional): Custom renderers for markdown blocks.
|
|
88
108
|
- `showSharp` (boolean, optional): Whether to show sharp signs for headings. Default is `false`.
|
|
109
|
+
- `theme` (string, optional): The theme for syntax highlighting. Default is `github-dark`. Check out [shiki](https://shiki.style/themes) for more themes.
|
|
89
110
|
|
|
90
111
|
## Contributing
|
|
91
112
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
/** biome-ignore-all lint/suspicious/noArrayIndexKey: <empty> */
|
|
1
2
|
import { type Token, type Tokens } from 'marked';
|
|
2
3
|
import type { MarkdownProps } from './types';
|
|
3
|
-
declare function MarkdownComponent({ children, id, styles, renderers, showSharp, }: MarkdownProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
declare function MarkdownComponent({ children, id, styles, renderers, showSharp, highlight, theme, }: MarkdownProps): import("react/jsx-runtime").JSX.Element;
|
|
4
5
|
declare const Markdown: import("react").MemoExoticComponent<typeof MarkdownComponent>;
|
|
5
6
|
export default Markdown;
|
|
6
7
|
export type { Token, Tokens };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { memo, useId, useMemo } from "react";
|
|
3
|
-
import { marked } from "marked";
|
|
4
2
|
import { Box, Text } from "ink";
|
|
3
|
+
import { marked } from "marked";
|
|
4
|
+
import { memo, useEffect, useId, useMemo, useState } from "react";
|
|
5
|
+
import { codeToTokens } from "shiki";
|
|
5
6
|
const DEFAULT_STYLES = {
|
|
6
7
|
h1: {
|
|
7
8
|
bold: true,
|
|
@@ -120,6 +121,119 @@ const BOX_STYLE_KEYS = [
|
|
|
120
121
|
'borderLeftDimColor',
|
|
121
122
|
'borderRightDimColor'
|
|
122
123
|
];
|
|
124
|
+
const TERMINAL_COLORS = {
|
|
125
|
+
comment: 'gray',
|
|
126
|
+
'comment.block': 'gray',
|
|
127
|
+
'comment.line': 'gray',
|
|
128
|
+
keyword: 'magenta',
|
|
129
|
+
'keyword.control': 'magenta',
|
|
130
|
+
'keyword.other': 'magenta',
|
|
131
|
+
string: 'green',
|
|
132
|
+
'string.quoted': 'green',
|
|
133
|
+
'string.regexp': 'red',
|
|
134
|
+
'entity.name.function': 'blue',
|
|
135
|
+
'support.function': 'blueBright',
|
|
136
|
+
'meta.function-call': 'blueBright',
|
|
137
|
+
variable: 'white',
|
|
138
|
+
'variable.parameter': 'yellowBright',
|
|
139
|
+
'variable.other': 'white',
|
|
140
|
+
'entity.name.type': 'cyan',
|
|
141
|
+
'entity.name.class': 'yellowBright',
|
|
142
|
+
'support.type': 'cyanBright',
|
|
143
|
+
'support.class': 'yellowBright',
|
|
144
|
+
'constant.numeric': 'yellow',
|
|
145
|
+
'constant.language': 'yellow',
|
|
146
|
+
operator: 'cyan',
|
|
147
|
+
punctuation: 'white',
|
|
148
|
+
'variable.other.property': 'blue',
|
|
149
|
+
'entity.other.attribute-name': 'blue',
|
|
150
|
+
'entity.name.tag': 'red',
|
|
151
|
+
'meta.tag': 'red',
|
|
152
|
+
'constant.language.boolean': 'yellow',
|
|
153
|
+
'constant.language.null': 'yellow',
|
|
154
|
+
meta: 'gray',
|
|
155
|
+
storage: 'magenta',
|
|
156
|
+
'storage.type': 'magenta'
|
|
157
|
+
};
|
|
158
|
+
function hexToTerminalColor(hex) {
|
|
159
|
+
const colorMap = {
|
|
160
|
+
'#6a737d': 'gray',
|
|
161
|
+
'#8b949e': 'gray',
|
|
162
|
+
'#6e7681': 'gray',
|
|
163
|
+
'#d73a49': 'magenta',
|
|
164
|
+
'#f97583': 'magenta',
|
|
165
|
+
'#ff7b72': 'red',
|
|
166
|
+
'#032f62': 'green',
|
|
167
|
+
'#22863a': 'green',
|
|
168
|
+
'#7ee787': 'green',
|
|
169
|
+
'#005cc5': 'blue',
|
|
170
|
+
'#0366d6': 'blue',
|
|
171
|
+
'#79c0ff': 'cyan',
|
|
172
|
+
'#58a6ff': 'blueBright',
|
|
173
|
+
'#a5d6ff': 'cyan',
|
|
174
|
+
'#e36209': 'yellow',
|
|
175
|
+
'#d29922': 'yellow',
|
|
176
|
+
'#ffa657': 'yellow',
|
|
177
|
+
'#0550ae': 'cyan'
|
|
178
|
+
};
|
|
179
|
+
const normalized = hex.toLowerCase();
|
|
180
|
+
if (colorMap[normalized]) return colorMap[normalized];
|
|
181
|
+
const r = Number.parseInt(normalized.slice(1, 3), 16);
|
|
182
|
+
const g = Number.parseInt(normalized.slice(3, 5), 16);
|
|
183
|
+
const b = Number.parseInt(normalized.slice(5, 7), 16);
|
|
184
|
+
const brightness = (299 * r + 587 * g + 114 * b) / 1000;
|
|
185
|
+
if (brightness < 80) return 'gray';
|
|
186
|
+
if (r > g && r > b) return 'red';
|
|
187
|
+
if (g > r && g > b) return 'green';
|
|
188
|
+
if (b > r && b > g) return 'blue';
|
|
189
|
+
if (r > 150 && g > 150) return 'yellow';
|
|
190
|
+
if (g > 150 && b > 150) return 'cyan';
|
|
191
|
+
if (r > 150 && b > 150) return 'magenta';
|
|
192
|
+
}
|
|
193
|
+
function tokenScopeToColor(scopes) {
|
|
194
|
+
for (const scope of scopes){
|
|
195
|
+
if (TERMINAL_COLORS[scope]) return TERMINAL_COLORS[scope];
|
|
196
|
+
const parts = scope.split('.');
|
|
197
|
+
for(let i = parts.length; i > 0; i--){
|
|
198
|
+
const key = parts.slice(0, i).join('.');
|
|
199
|
+
if (TERMINAL_COLORS[key]) return TERMINAL_COLORS[key];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function highlightCodeAsync(code, language, theme = 'github-dark') {
|
|
204
|
+
try {
|
|
205
|
+
const result = await codeToTokens(code, {
|
|
206
|
+
lang: language || 'text',
|
|
207
|
+
theme: theme
|
|
208
|
+
});
|
|
209
|
+
const nodes = [];
|
|
210
|
+
let index = 0;
|
|
211
|
+
for(let lineIndex = 0; lineIndex < result.tokens.length; lineIndex++){
|
|
212
|
+
const line = result.tokens[lineIndex];
|
|
213
|
+
if (line) {
|
|
214
|
+
for (const token of line){
|
|
215
|
+
let color;
|
|
216
|
+
if (token.color) color = hexToTerminalColor(token.color);
|
|
217
|
+
if (!color && token.explanation) {
|
|
218
|
+
const scopes = token.explanation.map((e)=>e.scopes?.[0]?.scopeName || '');
|
|
219
|
+
color = tokenScopeToColor(scopes);
|
|
220
|
+
}
|
|
221
|
+
nodes.push(/*#__PURE__*/ jsx(Text, {
|
|
222
|
+
color: color,
|
|
223
|
+
children: token.content
|
|
224
|
+
}, `token-${index}`));
|
|
225
|
+
index++;
|
|
226
|
+
}
|
|
227
|
+
if (lineIndex < result.tokens.length - 1) nodes.push(/*#__PURE__*/ jsx(Text, {
|
|
228
|
+
children: '\n'
|
|
229
|
+
}, `nl-${lineIndex}`));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return nodes;
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
123
237
|
function extractTextProps(style) {
|
|
124
238
|
if (!style) return {};
|
|
125
239
|
const result = {};
|
|
@@ -141,6 +255,51 @@ function mergeStyles(defaultStyle, userStyle) {
|
|
|
141
255
|
...userStyle
|
|
142
256
|
};
|
|
143
257
|
}
|
|
258
|
+
function CodeBlock({ code, language, style, theme }) {
|
|
259
|
+
const [highlightedCode, setHighlightedCode] = useState(null);
|
|
260
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
261
|
+
useEffect(()=>{
|
|
262
|
+
let cancelled = false;
|
|
263
|
+
async function highlight() {
|
|
264
|
+
try {
|
|
265
|
+
const result = await highlightCodeAsync(code, language, theme);
|
|
266
|
+
if (!cancelled) {
|
|
267
|
+
setHighlightedCode(result);
|
|
268
|
+
setIsLoading(false);
|
|
269
|
+
}
|
|
270
|
+
} catch {
|
|
271
|
+
if (!cancelled) {
|
|
272
|
+
setHighlightedCode(null);
|
|
273
|
+
setIsLoading(false);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
highlight();
|
|
278
|
+
return ()=>{
|
|
279
|
+
cancelled = true;
|
|
280
|
+
};
|
|
281
|
+
}, [
|
|
282
|
+
code,
|
|
283
|
+
language,
|
|
284
|
+
theme
|
|
285
|
+
]);
|
|
286
|
+
if (isLoading || !highlightedCode) return /*#__PURE__*/ jsx(Box, {
|
|
287
|
+
...extractBoxProps(style),
|
|
288
|
+
children: /*#__PURE__*/ jsx(Text, {
|
|
289
|
+
...extractTextProps(style),
|
|
290
|
+
children: code
|
|
291
|
+
})
|
|
292
|
+
});
|
|
293
|
+
return /*#__PURE__*/ jsx(Box, {
|
|
294
|
+
paddingX: 2,
|
|
295
|
+
paddingY: 1,
|
|
296
|
+
...extractBoxProps(style),
|
|
297
|
+
children: /*#__PURE__*/ jsx(Text, {
|
|
298
|
+
...extractTextProps(style),
|
|
299
|
+
children: highlightedCode
|
|
300
|
+
})
|
|
301
|
+
});
|
|
302
|
+
}
|
|
144
303
|
function renderInlineTokens(tokens, styles, renderers) {
|
|
145
304
|
if (!tokens || 0 === tokens.length) return null;
|
|
146
305
|
return tokens.map((token, index)=>{
|
|
@@ -248,7 +407,7 @@ function renderInlineTokens(tokens, styles, renderers) {
|
|
|
248
407
|
}
|
|
249
408
|
});
|
|
250
409
|
}
|
|
251
|
-
function renderBlockToken(token, styles, renderers, showSharp) {
|
|
410
|
+
function renderBlockToken(token, styles, renderers, showSharp, highlight, theme = 'github-dark') {
|
|
252
411
|
switch(token.type){
|
|
253
412
|
case 'heading':
|
|
254
413
|
{
|
|
@@ -289,6 +448,12 @@ function renderBlockToken(token, styles, renderers, showSharp) {
|
|
|
289
448
|
const codeToken = token;
|
|
290
449
|
const codeStyle = mergeStyles(DEFAULT_STYLES.code, styles.code);
|
|
291
450
|
if (renderers.code) return renderers.code(codeToken.text, codeToken.lang, codeToken);
|
|
451
|
+
if (highlight) return /*#__PURE__*/ jsx(CodeBlock, {
|
|
452
|
+
code: codeToken.text,
|
|
453
|
+
language: codeToken.lang,
|
|
454
|
+
style: codeStyle,
|
|
455
|
+
theme: theme
|
|
456
|
+
});
|
|
292
457
|
return /*#__PURE__*/ jsx(Box, {
|
|
293
458
|
...extractBoxProps(codeStyle),
|
|
294
459
|
children: /*#__PURE__*/ jsx(Text, {
|
|
@@ -302,17 +467,15 @@ function renderBlockToken(token, styles, renderers, showSharp) {
|
|
|
302
467
|
const blockquoteToken = token;
|
|
303
468
|
const blockquoteStyle = mergeStyles(DEFAULT_STYLES.blockquote, styles.blockquote);
|
|
304
469
|
const content = blockquoteToken.tokens.map((t, i)=>/*#__PURE__*/ jsx(Box, {
|
|
305
|
-
children: renderBlockToken(t, styles, renderers)
|
|
470
|
+
children: renderBlockToken(t, styles, renderers, showSharp, highlight, theme)
|
|
306
471
|
}, `bq-${i}`));
|
|
307
|
-
if (renderers.blockquote) return renderers.blockquote(
|
|
308
|
-
children: content
|
|
309
|
-
}), blockquoteToken);
|
|
472
|
+
if (renderers.blockquote) return renderers.blockquote(content, blockquoteToken);
|
|
310
473
|
return /*#__PURE__*/ jsx(Box, {
|
|
311
474
|
...extractBoxProps(blockquoteStyle),
|
|
312
475
|
children: /*#__PURE__*/ jsx(Box, {
|
|
313
476
|
flexDirection: "column",
|
|
314
477
|
children: blockquoteToken.tokens.map((t, i)=>{
|
|
315
|
-
const rendered = renderBlockToken(t, styles, renderers);
|
|
478
|
+
const rendered = renderBlockToken(t, styles, renderers, showSharp, highlight, theme);
|
|
316
479
|
if (rendered) return /*#__PURE__*/ jsx(Text, {
|
|
317
480
|
...extractTextProps(blockquoteStyle),
|
|
318
481
|
children: 'paragraph' === t.type ? renderInlineTokens(t.tokens, styles, renderers) : t.text || ''
|
|
@@ -374,40 +537,119 @@ function renderBlockToken(token, styles, renderers, showSharp) {
|
|
|
374
537
|
const tableToken = token;
|
|
375
538
|
const tableStyle = mergeStyles(DEFAULT_STYLES.table, styles.table);
|
|
376
539
|
const cellStyle = mergeStyles(DEFAULT_STYLES.tableCell, styles.tableCell);
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
540
|
+
const getCellText = (tokens)=>tokens.map((t)=>{
|
|
541
|
+
if ('text' in t) return t.text;
|
|
542
|
+
return '';
|
|
543
|
+
}).join('');
|
|
544
|
+
const columnWidths = tableToken.header.map((cell, colIndex)=>{
|
|
545
|
+
const headerWidth = getCellText(cell.tokens).length;
|
|
546
|
+
const bodyWidth = Math.max(...tableToken.rows.map((row)=>{
|
|
547
|
+
const cellTokens = row[colIndex]?.tokens;
|
|
548
|
+
return cellTokens ? getCellText(cellTokens).length : 0;
|
|
549
|
+
}), 0);
|
|
550
|
+
return Math.max(headerWidth, bodyWidth, 3);
|
|
551
|
+
});
|
|
552
|
+
const topBorder = /*#__PURE__*/ jsxs(Text, {
|
|
553
|
+
dimColor: true,
|
|
554
|
+
children: [
|
|
555
|
+
"┌─",
|
|
556
|
+
columnWidths.map((w)=>'─'.repeat(w)).join('─┬─'),
|
|
557
|
+
"─┐"
|
|
558
|
+
]
|
|
559
|
+
});
|
|
560
|
+
const headerRow = /*#__PURE__*/ jsxs(Text, {
|
|
561
|
+
children: [
|
|
562
|
+
/*#__PURE__*/ jsx(Text, {
|
|
563
|
+
dimColor: true,
|
|
564
|
+
children: "│ "
|
|
565
|
+
}),
|
|
566
|
+
tableToken.header.map((cell, i)=>{
|
|
567
|
+
const content = renderInlineTokens(cell.tokens, styles, renderers);
|
|
568
|
+
const cellText = getCellText(cell.tokens);
|
|
569
|
+
return /*#__PURE__*/ jsxs(Text, {
|
|
570
|
+
children: [
|
|
571
|
+
/*#__PURE__*/ jsxs(Text, {
|
|
572
|
+
bold: true,
|
|
573
|
+
...extractTextProps(cellStyle),
|
|
574
|
+
children: [
|
|
575
|
+
content,
|
|
576
|
+
' '.repeat(Math.max(0, (columnWidths[i] || 3) - cellText.length))
|
|
577
|
+
]
|
|
578
|
+
}),
|
|
579
|
+
/*#__PURE__*/ jsxs(Text, {
|
|
580
|
+
dimColor: true,
|
|
581
|
+
children: [
|
|
582
|
+
" │",
|
|
583
|
+
i < tableToken.header.length - 1 ? ' ' : ''
|
|
584
|
+
]
|
|
585
|
+
})
|
|
586
|
+
]
|
|
587
|
+
}, `th-${i}`);
|
|
383
588
|
})
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
589
|
+
]
|
|
590
|
+
});
|
|
591
|
+
const headerSeparator = /*#__PURE__*/ jsxs(Text, {
|
|
592
|
+
dimColor: true,
|
|
593
|
+
children: [
|
|
594
|
+
"├─",
|
|
595
|
+
columnWidths.map((w)=>'─'.repeat(w)).join('─┼─'),
|
|
596
|
+
"─┤"
|
|
597
|
+
]
|
|
388
598
|
});
|
|
389
|
-
const bodyRows = tableToken.rows.map((row, rowIndex)=>/*#__PURE__*/
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
children:
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
599
|
+
const bodyRows = tableToken.rows.map((row, rowIndex)=>/*#__PURE__*/ jsxs(Text, {
|
|
600
|
+
children: [
|
|
601
|
+
/*#__PURE__*/ jsx(Text, {
|
|
602
|
+
dimColor: true,
|
|
603
|
+
children: "│ "
|
|
604
|
+
}),
|
|
605
|
+
row.map((cell, cellIndex)=>{
|
|
606
|
+
const content = renderInlineTokens(cell.tokens, styles, renderers);
|
|
607
|
+
const cellText = getCellText(cell.tokens);
|
|
608
|
+
const colWidth = columnWidths[cellIndex] || 3;
|
|
609
|
+
return /*#__PURE__*/ jsxs(Text, {
|
|
610
|
+
children: [
|
|
611
|
+
/*#__PURE__*/ jsxs(Text, {
|
|
612
|
+
...extractTextProps(cellStyle),
|
|
613
|
+
children: [
|
|
614
|
+
content,
|
|
615
|
+
' '.repeat(Math.max(0, colWidth - cellText.length))
|
|
616
|
+
]
|
|
617
|
+
}),
|
|
618
|
+
/*#__PURE__*/ jsxs(Text, {
|
|
619
|
+
dimColor: true,
|
|
620
|
+
children: [
|
|
621
|
+
" │",
|
|
622
|
+
cellIndex < row.length - 1 ? ' ' : ''
|
|
623
|
+
]
|
|
624
|
+
})
|
|
625
|
+
]
|
|
626
|
+
}, `td-${rowIndex}-${cellIndex}`);
|
|
627
|
+
})
|
|
628
|
+
]
|
|
398
629
|
}, `tr-${rowIndex}`));
|
|
399
|
-
const
|
|
400
|
-
|
|
630
|
+
const bottomBorder = /*#__PURE__*/ jsxs(Text, {
|
|
631
|
+
dimColor: true,
|
|
632
|
+
children: [
|
|
633
|
+
"└─",
|
|
634
|
+
columnWidths.map((w)=>'─'.repeat(w)).join('─┴─'),
|
|
635
|
+
"─┘"
|
|
636
|
+
]
|
|
401
637
|
});
|
|
402
|
-
|
|
403
|
-
return /*#__PURE__*/ jsxs(Box, {
|
|
404
|
-
flexDirection: "column",
|
|
405
|
-
...extractBoxProps(tableStyle),
|
|
638
|
+
const tableContent = /*#__PURE__*/ jsxs(Fragment, {
|
|
406
639
|
children: [
|
|
407
|
-
|
|
408
|
-
|
|
640
|
+
topBorder,
|
|
641
|
+
headerRow,
|
|
642
|
+
headerSeparator,
|
|
643
|
+
bodyRows,
|
|
644
|
+
bottomBorder
|
|
409
645
|
]
|
|
410
646
|
});
|
|
647
|
+
if (renderers.table) return renderers.table(headerRow, bodyRows, tableToken);
|
|
648
|
+
return /*#__PURE__*/ jsx(Box, {
|
|
649
|
+
flexDirection: "column",
|
|
650
|
+
...extractBoxProps(tableStyle),
|
|
651
|
+
children: tableContent
|
|
652
|
+
});
|
|
411
653
|
}
|
|
412
654
|
case 'space':
|
|
413
655
|
return /*#__PURE__*/ jsx(Text, {
|
|
@@ -428,16 +670,19 @@ function renderBlockToken(token, styles, renderers, showSharp) {
|
|
|
428
670
|
return null;
|
|
429
671
|
}
|
|
430
672
|
}
|
|
431
|
-
const src_MemoizedBlock = /*#__PURE__*/ memo(function({ token, styles, renderers, showSharp }) {
|
|
673
|
+
const src_MemoizedBlock = /*#__PURE__*/ memo(function({ token, styles, renderers, showSharp, highlight, theme }) {
|
|
432
674
|
return /*#__PURE__*/ jsx(Fragment, {
|
|
433
|
-
children: renderBlockToken(token, styles, renderers, showSharp)
|
|
675
|
+
children: renderBlockToken(token, styles, renderers, showSharp, highlight, theme)
|
|
434
676
|
});
|
|
435
677
|
}, (prevProps, nextProps)=>prevProps.token === nextProps.token);
|
|
436
678
|
src_MemoizedBlock.displayName = 'MemoizedBlock';
|
|
437
|
-
function MarkdownComponent({ children, id, styles = {}, renderers = {}, showSharp = false }) {
|
|
679
|
+
function MarkdownComponent({ children, id, styles = {}, renderers = {}, showSharp = false, highlight = true, theme = 'github-dark' }) {
|
|
438
680
|
const generatedId = useId();
|
|
439
681
|
const key = id || generatedId;
|
|
440
|
-
const tokens = useMemo(()=>marked.lexer(children
|
|
682
|
+
const tokens = useMemo(()=>marked.lexer(children, {
|
|
683
|
+
silent: true,
|
|
684
|
+
gfm: true
|
|
685
|
+
}), [
|
|
441
686
|
children
|
|
442
687
|
]);
|
|
443
688
|
return /*#__PURE__*/ jsx(Box, {
|
|
@@ -446,7 +691,9 @@ function MarkdownComponent({ children, id, styles = {}, renderers = {}, showShar
|
|
|
446
691
|
token: token,
|
|
447
692
|
styles: styles,
|
|
448
693
|
renderers: renderers,
|
|
449
|
-
showSharp: showSharp
|
|
694
|
+
showSharp: showSharp,
|
|
695
|
+
highlight: highlight,
|
|
696
|
+
theme: theme
|
|
450
697
|
}, `${key}-block-${index}`))
|
|
451
698
|
});
|
|
452
699
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Box, Text } from 'ink';
|
|
2
2
|
import type { Token, Tokens } from 'marked';
|
|
3
|
-
import type {
|
|
3
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
4
4
|
export type TextStyleProps = Pick<ComponentProps<typeof Text>, 'color' | 'backgroundColor' | 'dimColor' | 'bold' | 'italic' | 'underline' | 'strikethrough' | 'inverse' | 'wrap'>;
|
|
5
5
|
export type BoxStyleProps = ComponentProps<typeof Box>;
|
|
6
6
|
export type HeadingStyleProps = TextStyleProps & BoxStyleProps & {
|
|
@@ -62,10 +62,24 @@ export type MarkdownProps = {
|
|
|
62
62
|
styles?: BlockStyles;
|
|
63
63
|
renderers?: BlockRenderers;
|
|
64
64
|
showSharp?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Enable syntax highlighting for code blocks
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
highlight?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Shiki theme name for syntax highlighting
|
|
72
|
+
* @default 'github-dark'
|
|
73
|
+
* @example 'github-dark', 'github-light', 'nord', 'dracula', etc.
|
|
74
|
+
* @see https://shiki.style/themes
|
|
75
|
+
*/
|
|
76
|
+
theme?: string;
|
|
65
77
|
};
|
|
66
78
|
export type MemoizedBlockProps = {
|
|
67
79
|
token: Token;
|
|
68
80
|
styles: BlockStyles;
|
|
69
81
|
renderers: BlockRenderers;
|
|
70
82
|
showSharp: boolean;
|
|
83
|
+
highlight: boolean;
|
|
84
|
+
theme: string;
|
|
71
85
|
};
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BoxStyleProps, TextStyleProps } from './types';
|
|
2
2
|
export declare function extractTextProps(style: (TextStyleProps & BoxStyleProps) | undefined): TextStyleProps;
|
|
3
3
|
export declare function extractBoxProps(style: (TextStyleProps & BoxStyleProps) | undefined): BoxStyleProps;
|
|
4
4
|
export declare function mergeStyles<T>(defaultStyle: T | undefined, userStyle: T | undefined): T;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ink-markdown-es",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A modern performance markdown renderer for ink",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"dedent": "^1.7.1",
|
|
54
|
-
"marked": "^16.4.2"
|
|
54
|
+
"marked": "^16.4.2",
|
|
55
|
+
"shiki": "^3.22.0"
|
|
55
56
|
}
|
|
56
57
|
}
|