@dr-ishaan/rehype-perfect-code-blocks 2.2.0 → 2.3.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/CHANGELOG.md +68 -0
- package/dist/classes.d.ts +58 -0
- package/dist/classes.d.ts.map +1 -0
- package/dist/classes.js +62 -0
- package/dist/classes.js.map +1 -0
- package/dist/diagrams.d.ts +41 -0
- package/dist/diagrams.d.ts.map +1 -0
- package/dist/diagrams.js +114 -0
- package/dist/diagrams.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/shiki.d.ts.map +1 -1
- package/dist/shiki.js +80 -0
- package/dist/shiki.js.map +1 -1
- package/dist/styles.css +92 -0
- package/dist/transformer.d.ts.map +1 -1
- package/dist/transformer.js +12 -1
- package/dist/transformer.js.map +1 -1
- package/dist/types.d.ts +25 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/classes.ts +64 -0
- package/src/diagrams.ts +125 -0
- package/src/index.ts +8 -0
- package/src/mermaid.d.ts +11 -0
- package/src/shiki.ts +82 -0
- package/src/styles.css +92 -0
- package/src/transformer.ts +12 -1
- package/src/types.ts +30 -1
package/src/diagrams.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diagram & table rendering (v2.3.0).
|
|
3
|
+
*
|
|
4
|
+
* - Mermaid: render ```mermaid blocks as SVG diagrams
|
|
5
|
+
* - CSV/TSV: render ```csv / ```tsv blocks as HTML tables
|
|
6
|
+
*
|
|
7
|
+
* Both are build-time (server-side) — no client JS needed.
|
|
8
|
+
* `mermaid` is an optional peer dependency.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Element, ElementContent } from 'hast';
|
|
12
|
+
|
|
13
|
+
/** Languages that trigger Mermaid rendering. */
|
|
14
|
+
export const MERMAID_LANGS = new Set(['mermaid', 'mmd']);
|
|
15
|
+
|
|
16
|
+
/** Languages that trigger CSV/TSV table rendering. */
|
|
17
|
+
export const CSV_LANGS = new Set(['csv']);
|
|
18
|
+
export const TSV_LANGS = new Set(['tsv']);
|
|
19
|
+
|
|
20
|
+
export function isMermaidLanguage(lang: string): boolean {
|
|
21
|
+
return MERMAID_LANGS.has(lang.toLowerCase());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isCsvLanguage(lang: string): boolean {
|
|
25
|
+
return CSV_LANGS.has(lang.toLowerCase()) || TSV_LANGS.has(lang.toLowerCase());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Render a Mermaid diagram to SVG.
|
|
30
|
+
* Falls back to a `<pre>` with the source if mermaid is not installed.
|
|
31
|
+
*/
|
|
32
|
+
export async function renderMermaid(source: string): Promise<{ svg: string | null; isError: boolean }> {
|
|
33
|
+
try {
|
|
34
|
+
const mermaid = await import('mermaid');
|
|
35
|
+
// mermaid v10+ uses mermaid.default.render()
|
|
36
|
+
const m = (mermaid as unknown as { default?: { render: (id: string, text: string) => Promise<string> } }).default ?? mermaid;
|
|
37
|
+
if (typeof m.render === 'function') {
|
|
38
|
+
const id = 'pcb-mermaid-' + Math.random().toString(36).slice(2, 10);
|
|
39
|
+
const svg = await m.render(id, source.trim());
|
|
40
|
+
return { svg, isError: false };
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// mermaid not installed or rendering failed
|
|
44
|
+
}
|
|
45
|
+
return { svg: null, isError: true };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parse CSV/TSV text into rows of cells.
|
|
50
|
+
*/
|
|
51
|
+
export function parseCsv(text: string, delimiter: ',' | '\t' = ','): string[][] {
|
|
52
|
+
const rows: string[][] = [];
|
|
53
|
+
for (const line of text.split('\n')) {
|
|
54
|
+
if (line.trim() === '') continue;
|
|
55
|
+
rows.push(line.split(delimiter).map((cell) => cell.trim()));
|
|
56
|
+
}
|
|
57
|
+
return rows;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build a HAST element tree for a CSV/TSV table.
|
|
62
|
+
* First row is the header (in `<thead>`), remaining rows in `<tbody>`.
|
|
63
|
+
*/
|
|
64
|
+
export function buildCsvTable(text: string, delimiter: ',' | '\t' = ','): Element {
|
|
65
|
+
const rows = parseCsv(text, delimiter);
|
|
66
|
+
const children: ElementContent[] = [];
|
|
67
|
+
|
|
68
|
+
if (rows.length > 0) {
|
|
69
|
+
// Header
|
|
70
|
+
const headerCells: ElementContent[] = rows[0].map((cell) => ({
|
|
71
|
+
type: 'element',
|
|
72
|
+
tagName: 'th',
|
|
73
|
+
properties: { className: ['pcb__th'] },
|
|
74
|
+
children: [{ type: 'text', value: cell }],
|
|
75
|
+
}));
|
|
76
|
+
children.push({
|
|
77
|
+
type: 'element',
|
|
78
|
+
tagName: 'thead',
|
|
79
|
+
properties: {},
|
|
80
|
+
children: [{
|
|
81
|
+
type: 'element',
|
|
82
|
+
tagName: 'tr',
|
|
83
|
+
properties: { className: ['pcb__tr'] },
|
|
84
|
+
children: headerCells,
|
|
85
|
+
}],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Body
|
|
89
|
+
const bodyRows: ElementContent[] = rows.slice(1).map((row) => ({
|
|
90
|
+
type: 'element',
|
|
91
|
+
tagName: 'tr',
|
|
92
|
+
properties: { className: ['pcb__tr'] },
|
|
93
|
+
children: row.map((cell) => ({
|
|
94
|
+
type: 'element',
|
|
95
|
+
tagName: 'td',
|
|
96
|
+
properties: { className: ['pcb__td'] },
|
|
97
|
+
children: [{ type: 'text', value: cell }],
|
|
98
|
+
})),
|
|
99
|
+
}));
|
|
100
|
+
children.push({
|
|
101
|
+
type: 'element',
|
|
102
|
+
tagName: 'tbody',
|
|
103
|
+
properties: {},
|
|
104
|
+
children: bodyRows,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
type: 'element',
|
|
110
|
+
tagName: 'table',
|
|
111
|
+
properties: { className: ['pcb__table'] },
|
|
112
|
+
children,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** ASCII art language defaults — disable ligatures for alignment. */
|
|
117
|
+
export const DEFAULT_ASCII_ART_LANGS = ['text', 'plaintext', 'txt', 'ascii', 'plain'];
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check if a language should be treated as ASCII art (ligatures disabled).
|
|
121
|
+
*/
|
|
122
|
+
export function isAsciiArtLang(lang: string, asciiArtLangs: string[]): boolean {
|
|
123
|
+
const set = new Set(asciiArtLangs.map((l) => l.toLowerCase()));
|
|
124
|
+
return set.has(lang.toLowerCase());
|
|
125
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,8 @@ import type { DesignTokens } from './tokens.js';
|
|
|
29
29
|
import { resolveMathOptions, isMathLanguage, renderMath } from './math.js';
|
|
30
30
|
import type { MathOptions, ResolvedMathOptions } from './math.js';
|
|
31
31
|
import { runDevWarnings, warnUnknownLanguage } from './dev-warnings.js';
|
|
32
|
+
import { isMermaidLanguage, isCsvLanguage, buildCsvTable, parseCsv, renderMermaid } from './diagrams.js';
|
|
33
|
+
import { CLASSES } from './classes.js';
|
|
32
34
|
import type { PerfectCodeOptions } from './types.js';
|
|
33
35
|
|
|
34
36
|
export { remarkPreserveCodeMeta };
|
|
@@ -37,6 +39,8 @@ export { wordDiff, hasChanges };
|
|
|
37
39
|
export { generateTokenStyles, applyScopeToCss, generateDarkModeSelector, generateLightModeSelector };
|
|
38
40
|
export { resolveMathOptions, isMathLanguage, renderMath };
|
|
39
41
|
export { runDevWarnings, warnUnknownLanguage };
|
|
42
|
+
export { isMermaidLanguage, isCsvLanguage, buildCsvTable, parseCsv, renderMermaid };
|
|
43
|
+
export { CLASSES };
|
|
40
44
|
export type { DiffToken, DesignTokens, MathOptions, ResolvedMathOptions };
|
|
41
45
|
|
|
42
46
|
export const rehypePerfectCodeBlocks: Plugin<[PerfectCodeOptions?], Root> =
|
|
@@ -178,6 +182,10 @@ function resolveDefaults(opts: PerfectCodeOptions): Required<PerfectCodeOptions>
|
|
|
178
182
|
diffMode: opts.diffMode ?? 'unified',
|
|
179
183
|
annotations: opts.annotations ?? false,
|
|
180
184
|
attribution: opts.attribution ?? false,
|
|
185
|
+
// v2.3.0: P2
|
|
186
|
+
mermaid: opts.mermaid ?? false,
|
|
187
|
+
csvTables: opts.csvTables ?? false,
|
|
188
|
+
asciiArtLangs: opts.asciiArtLangs ?? ['text', 'plaintext', 'txt', 'ascii', 'plain'],
|
|
181
189
|
inline: opts.inline ?? false,
|
|
182
190
|
};
|
|
183
191
|
}
|
package/src/mermaid.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Minimal type declaration for mermaid (optional peer dependency). */
|
|
2
|
+
declare module 'mermaid' {
|
|
3
|
+
export interface MermaidConfig { [key: string]: unknown; }
|
|
4
|
+
export function render(id: string, text: string): Promise<string>;
|
|
5
|
+
export function initialize(config?: MermaidConfig): void;
|
|
6
|
+
const _default: {
|
|
7
|
+
render: typeof render;
|
|
8
|
+
initialize: typeof initialize;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
|
11
|
+
}
|
package/src/shiki.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { visit } from 'unist-util-visit';
|
|
|
19
19
|
import type { PerfectCodeOptions } from './types.js';
|
|
20
20
|
import { computeThemeAwareDefaults } from './color-utils.js';
|
|
21
21
|
import { isMathLanguage, renderMath, resolveMathOptions, MATH_LANGS } from './math.js';
|
|
22
|
+
import { isMermaidLanguage, renderMermaid, isCsvLanguage, buildCsvTable } from './diagrams.js';
|
|
22
23
|
import {
|
|
23
24
|
transformerNotationDiff,
|
|
24
25
|
transformerNotationFocus,
|
|
@@ -490,6 +491,87 @@ export async function runShikiOnRawBlocks(
|
|
|
490
491
|
targets.splice(0, targets.length, ...codeTargets);
|
|
491
492
|
}
|
|
492
493
|
|
|
494
|
+
// v2.3.0: Handle Mermaid diagram blocks — render as SVG instead of Shiki.
|
|
495
|
+
if ((opts as { mermaid?: boolean }).mermaid) {
|
|
496
|
+
const mermaidTargets: Element[] = [];
|
|
497
|
+
const remainingTargets: Element[] = [];
|
|
498
|
+
for (const pre of targets) {
|
|
499
|
+
const code = pre.children.find(
|
|
500
|
+
(c): c is Element => c.type === 'element' && c.tagName === 'code'
|
|
501
|
+
);
|
|
502
|
+
if (!code) { remainingTargets.push(pre); continue; }
|
|
503
|
+
const cls = (code.properties?.className as string[] | undefined) ?? [];
|
|
504
|
+
const langClass = cls.find((c) => c.startsWith('language-'));
|
|
505
|
+
const lang = langClass ? langClass.replace('language-', '') : '';
|
|
506
|
+
if (isMermaidLanguage(lang)) {
|
|
507
|
+
mermaidTargets.push(pre);
|
|
508
|
+
} else {
|
|
509
|
+
remainingTargets.push(pre);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
for (const pre of mermaidTargets) {
|
|
514
|
+
const code = pre.children.find(
|
|
515
|
+
(c): c is Element => c.type === 'element' && c.tagName === 'code'
|
|
516
|
+
);
|
|
517
|
+
if (!code) continue;
|
|
518
|
+
const text = extractText(code).replace(/\r\n?/g, '\n').trim();
|
|
519
|
+
const { svg, isError } = await renderMermaid(text);
|
|
520
|
+
const mermaidDiv: Element = {
|
|
521
|
+
type: 'element',
|
|
522
|
+
tagName: 'div',
|
|
523
|
+
properties: { className: ['pcb__mermaid', isError ? 'pcb__mermaid--error' : 'pcb__mermaid--rendered'] },
|
|
524
|
+
children: svg
|
|
525
|
+
? [{ type: 'text', value: svg }]
|
|
526
|
+
: [{ type: 'element', tagName: 'pre', properties: {}, children: [{ type: 'text', value: text }] }],
|
|
527
|
+
};
|
|
528
|
+
Object.assign(pre, mermaidDiv);
|
|
529
|
+
}
|
|
530
|
+
targets.splice(0, targets.length, ...remainingTargets);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// v2.3.0: Handle CSV/TSV table blocks — render as HTML table instead of Shiki.
|
|
534
|
+
if ((opts as { csvTables?: boolean }).csvTables) {
|
|
535
|
+
const csvTargets: Element[] = [];
|
|
536
|
+
const remainingTargets: Element[] = [];
|
|
537
|
+
for (const pre of targets) {
|
|
538
|
+
const code = pre.children.find(
|
|
539
|
+
(c): c is Element => c.type === 'element' && c.tagName === 'code'
|
|
540
|
+
);
|
|
541
|
+
if (!code) { remainingTargets.push(pre); continue; }
|
|
542
|
+
const cls = (code.properties?.className as string[] | undefined) ?? [];
|
|
543
|
+
const langClass = cls.find((c) => c.startsWith('language-'));
|
|
544
|
+
const lang = langClass ? langClass.replace('language-', '') : '';
|
|
545
|
+
if (isCsvLanguage(lang)) {
|
|
546
|
+
csvTargets.push(pre);
|
|
547
|
+
} else {
|
|
548
|
+
remainingTargets.push(pre);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
for (const pre of csvTargets) {
|
|
553
|
+
const code = pre.children.find(
|
|
554
|
+
(c): c is Element => c.type === 'element' && c.tagName === 'code'
|
|
555
|
+
);
|
|
556
|
+
if (!code) continue;
|
|
557
|
+
const text = extractText(code).replace(/\r\n?/g, '\n').trim();
|
|
558
|
+
const cls = (code.properties?.className as string[] | undefined) ?? [];
|
|
559
|
+
const langClass = cls.find((c) => c.startsWith('language-'));
|
|
560
|
+
const lang = langClass ? langClass.replace('language-', '') : 'csv';
|
|
561
|
+
const delimiter = lang.toLowerCase() === 'tsv' ? '\t' : ',';
|
|
562
|
+
const tableEl = buildCsvTable(text, delimiter);
|
|
563
|
+
// Replace the <pre> with the table wrapped in a div
|
|
564
|
+
const tableDiv: Element = {
|
|
565
|
+
type: 'element',
|
|
566
|
+
tagName: 'div',
|
|
567
|
+
properties: { className: ['pcb__csv-table'] },
|
|
568
|
+
children: [tableEl],
|
|
569
|
+
};
|
|
570
|
+
Object.assign(pre, tableDiv);
|
|
571
|
+
}
|
|
572
|
+
targets.splice(0, targets.length, ...remainingTargets);
|
|
573
|
+
}
|
|
574
|
+
|
|
493
575
|
if (targets.length === 0) return;
|
|
494
576
|
|
|
495
577
|
// Build theme keys — supports single (string), dual ({light,dark}), and
|
package/src/styles.css
CHANGED
|
@@ -722,3 +722,95 @@ html.no-js .pcb__copy {
|
|
|
722
722
|
border-top: 1px solid var(--pcb-border);
|
|
723
723
|
font-style: italic;
|
|
724
724
|
}
|
|
725
|
+
|
|
726
|
+
/* ============================================================
|
|
727
|
+
v2.3.0: P2 — Retro preset, Mermaid, CSV tables, ASCII art, A11y
|
|
728
|
+
============================================================ */
|
|
729
|
+
|
|
730
|
+
/* ---------- Retro CRT preset ---------- */
|
|
731
|
+
:where(.pcb--retro) {
|
|
732
|
+
--pcb-bg: #000000;
|
|
733
|
+
--pcb-fg: #00ff41;
|
|
734
|
+
--pcb-text-muted: #008f11;
|
|
735
|
+
--pcb-border: #00ff41;
|
|
736
|
+
--pcb-bg-header: #001100;
|
|
737
|
+
--pcb-bg-gutter: #000000;
|
|
738
|
+
--pcb-radius: 0px;
|
|
739
|
+
--pcb-font-mono: 'Courier New', 'Lucida Console', monospace;
|
|
740
|
+
--pcb-bar-font: 'Courier New', monospace;
|
|
741
|
+
text-shadow: 0 0 2px currentColor, 0 0 4px rgba(0, 255, 65, 0.3);
|
|
742
|
+
}
|
|
743
|
+
:where(.pcb--retro) .pcb__body {
|
|
744
|
+
position: relative;
|
|
745
|
+
}
|
|
746
|
+
:where(.pcb--retro) .pcb__body::after {
|
|
747
|
+
content: '';
|
|
748
|
+
position: absolute;
|
|
749
|
+
inset: 0;
|
|
750
|
+
background: repeating-linear-gradient(
|
|
751
|
+
0deg,
|
|
752
|
+
rgba(0, 0, 0, 0) 0px,
|
|
753
|
+
rgba(0, 0, 0, 0) 2px,
|
|
754
|
+
rgba(0, 0, 0, 0.08) 2px,
|
|
755
|
+
rgba(0, 0, 0, 0.08) 4px
|
|
756
|
+
);
|
|
757
|
+
pointer-events: none;
|
|
758
|
+
}
|
|
759
|
+
:where(.pcb--retro) .pcb__dots span) {
|
|
760
|
+
background: #00ff41;
|
|
761
|
+
opacity: 0.6;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/* ---------- Mermaid diagram container ---------- */
|
|
765
|
+
:where(.pcb__mermaid) {
|
|
766
|
+
padding: 1rem;
|
|
767
|
+
background: var(--pcb-bg);
|
|
768
|
+
overflow-x: auto;
|
|
769
|
+
text-align: center;
|
|
770
|
+
}
|
|
771
|
+
:where(.pcb__mermaid svg) {
|
|
772
|
+
max-width: 100%;
|
|
773
|
+
height: auto;
|
|
774
|
+
}
|
|
775
|
+
:where(.pcb__mermaid--error) {
|
|
776
|
+
color: var(--pcb-text-muted);
|
|
777
|
+
font-family: var(--pcb-font-mono);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/* ---------- CSV/TSV table ---------- */
|
|
781
|
+
:where(.pcb__csv-table) {
|
|
782
|
+
padding: 0;
|
|
783
|
+
background: var(--pcb-bg);
|
|
784
|
+
overflow-x: auto;
|
|
785
|
+
}
|
|
786
|
+
:where(.pcb__table) {
|
|
787
|
+
width: 100%;
|
|
788
|
+
border-collapse: collapse;
|
|
789
|
+
font-family: var(--pcb-font-mono);
|
|
790
|
+
font-size: var(--pcb-font-size);
|
|
791
|
+
}
|
|
792
|
+
:where(.pcb__th) {
|
|
793
|
+
padding: 0.5rem 1rem;
|
|
794
|
+
text-align: left;
|
|
795
|
+
border-bottom: 2px solid var(--pcb-border);
|
|
796
|
+
color: var(--pcb-text-bar);
|
|
797
|
+
font-weight: 600;
|
|
798
|
+
}
|
|
799
|
+
:where(.pcb__td) {
|
|
800
|
+
padding: 0.4rem 1rem;
|
|
801
|
+
border-bottom: 1px solid var(--pcb-border);
|
|
802
|
+
color: var(--pcb-text);
|
|
803
|
+
}
|
|
804
|
+
:where(.pcb__tr:nth-child(even) .pcb__td) {
|
|
805
|
+
background: rgba(127, 127, 127, 0.05);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/* ---------- ASCII art: disable ligatures ---------- */
|
|
809
|
+
:where(.pcb[data-language="text"] .pcb__code),
|
|
810
|
+
:where(.pcb[data-language="plaintext"] .pcb__code),
|
|
811
|
+
:where(.pcb[data-language="txt"] .pcb__code),
|
|
812
|
+
:where(.pcb[data-language="ascii"] .pcb__code),
|
|
813
|
+
:where(.pcb[data-language="plain"] .pcb__code) {
|
|
814
|
+
font-variant-ligatures: none;
|
|
815
|
+
font-feature-settings: "liga" 0, "calt" 0;
|
|
816
|
+
}
|
package/src/transformer.ts
CHANGED
|
@@ -253,6 +253,10 @@ export function rehypePerfectCodeBlocks(userOptions: PerfectCodeOptions = {}) {
|
|
|
253
253
|
diffMode: 'unified' as const,
|
|
254
254
|
annotations: false,
|
|
255
255
|
attribution: false,
|
|
256
|
+
// v2.3.0: P2
|
|
257
|
+
mermaid: false,
|
|
258
|
+
csvTables: false,
|
|
259
|
+
asciiArtLangs: ['text', 'plaintext', 'txt', 'ascii', 'plain'] as string[],
|
|
256
260
|
inline: false,
|
|
257
261
|
...rest,
|
|
258
262
|
};
|
|
@@ -999,7 +1003,7 @@ function toLineSpans(
|
|
|
999
1003
|
const lineChildren: ElementContent[] = [];
|
|
1000
1004
|
if (resolved.lineNumbers) {
|
|
1001
1005
|
lineChildren.push(
|
|
1002
|
-
h('span', { className: ['pcb__ln'], ariaHidden: true }, [hText(String(lineNumber))])
|
|
1006
|
+
h('span', { className: ['pcb__ln'], ariaHidden: true, 'aria-label': `Line ${lineNumber}` }, [hText(String(lineNumber))])
|
|
1003
1007
|
);
|
|
1004
1008
|
}
|
|
1005
1009
|
lineChildren.push(
|
|
@@ -1017,6 +1021,13 @@ function toLineSpans(
|
|
|
1017
1021
|
if (annotationText !== null) {
|
|
1018
1022
|
lineProps['dataAnn'] = annotationText;
|
|
1019
1023
|
}
|
|
1024
|
+
// v2.3.0: Add aria-label for diff lines (accessibility)
|
|
1025
|
+
if (classes.has('pcb__line--add')) {
|
|
1026
|
+
lineProps['aria-label'] = 'Added line';
|
|
1027
|
+
} else if (classes.has('pcb__line--del')) {
|
|
1028
|
+
lineProps['aria-label'] = 'Removed line';
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1020
1031
|
return h('span', lineProps, lineChildren);
|
|
1021
1032
|
});
|
|
1022
1033
|
}
|
package/src/types.ts
CHANGED
|
@@ -343,7 +343,7 @@ export interface PerfectCodeOptions {
|
|
|
343
343
|
|
|
344
344
|
/* ---------- Styling ---------- */
|
|
345
345
|
/** Visual preset. Default: 'default' */
|
|
346
|
-
preset?: 'default' | 'terminal' | 'minimal';
|
|
346
|
+
preset?: 'default' | 'terminal' | 'minimal' | 'retro';
|
|
347
347
|
/** Inject the bundled CSS automatically. Set false to ship your own. Default: true */
|
|
348
348
|
injectStyles?: boolean;
|
|
349
349
|
/** Manual theme override. Default: 'auto' (prefers-color-scheme) */
|
|
@@ -609,6 +609,35 @@ export interface PerfectCodeOptions {
|
|
|
609
609
|
*/
|
|
610
610
|
attribution?: boolean;
|
|
611
611
|
|
|
612
|
+
/* ---------- v2.3.0: P2 — Diagrams, Tables, ASCII, Frames, A11y ---------- */
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Mermaid diagram rendering. When a fenced code block has language `mermaid`,
|
|
616
|
+
* render it as an SVG diagram instead of syntax-highlighted code.
|
|
617
|
+
*
|
|
618
|
+
* `mermaid` must be installed: `npm install mermaid`
|
|
619
|
+
*
|
|
620
|
+
* Default: `false` (mermaid blocks render as plain code)
|
|
621
|
+
*/
|
|
622
|
+
mermaid?: boolean;
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* CSV/TSV table rendering. When a fenced code block has language `csv` or
|
|
626
|
+
* `tsv`, render it as a styled HTML table instead of code.
|
|
627
|
+
*
|
|
628
|
+
* Default: `false` (CSV/TSV blocks render as plain code)
|
|
629
|
+
*/
|
|
630
|
+
csvTables?: boolean;
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* ASCII art preservation. For languages in the list, disable ligatures,
|
|
634
|
+
* preserve trailing whitespace, and set `font-variant-ligatures: none` to
|
|
635
|
+
* ensure ASCII art alignment is maintained.
|
|
636
|
+
*
|
|
637
|
+
* Default: `['text', 'plaintext', 'txt', 'ascii', 'plain']`
|
|
638
|
+
*/
|
|
639
|
+
asciiArtLangs?: string[];
|
|
640
|
+
|
|
612
641
|
/* ---------- Inline code (legacy cosmetic option) ---------- */
|
|
613
642
|
/** Also style inline `code` cosmetically (no tokenization). Default: false */
|
|
614
643
|
inline?: boolean;
|