@dr-ishaan/rehype-perfect-code-blocks 1.2.2 → 1.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 CHANGED
@@ -5,6 +5,64 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0] — 2026-06-19
9
+
10
+ ### Summary
11
+
12
+ Minor release adopting 5 architectural patterns from community competitors (expressive-code, VitePress, rehype-pretty-code), identified through a systematic source-code comparison of 6 community packages. The patterns improve correctness (highlighter race conditions), UX (theme-aware color defaults, SPA support), and add a new opt-in word-level diff feature. All 1051 pre-existing tests continue to pass, plus 41 new regression tests for the adopted patterns. No breaking API changes; all new behavior is opt-in or backward-compatible.
13
+
14
+ ### Features
15
+
16
+ #### Pattern 1: Highlighter task queue (from expressive-code)
17
+
18
+ - New exported function `runHighlighterTask<T>(taskFn: () => Promise<T>): Promise<T>` — a mutually exclusive FIFO queue that serializes all highlighter operations (createHighlighter, loadLanguage, codeToHast) globally. Prevents race conditions in parallel static-site builds where multiple unified pipelines share the same module-level highlighter cache.
19
+ - All highlighter creation and lazy-language-loading in `src/shiki.ts` now runs inside `runHighlighterTask()`.
20
+ - Tradeoff: slight throughput reduction in parallel builds; correctness > throughput for syntax highlighting.
21
+
22
+ #### Pattern 2: Color-contrast-aware theme defaults (from expressive-code)
23
+
24
+ - New `src/color-utils.ts` module with `parseColor`, `contrastRatio`, `ensureColorContrastOnBackground`, `mix`, `lighten`, `darken`, `toHex`, `extractThemeColors`, and `computeThemeAwareDefaults` utilities.
25
+ - The `<pre>` element now receives inline `--pcb-*` CSS variable defaults derived from the loaded Shiki theme: `--pcb-bg`, `--pcb-fg`, `--pcb-ln-fg` (contrast-adjusted for WCAG), `--pcb-line-highlight-bg`, `--pcb-line-add-bg`, `--pcb-line-del-bg`, `--pcb-line-focus-bg`.
26
+ - Code blocks now look good with ANY Shiki theme out of the box — line numbers, diff backgrounds, and focus highlights are automatically legible against the theme's background color.
27
+ - Defaults are cached per (highlighter, theme) combination via a `WeakMap` to avoid recomputing per block.
28
+ - The static `dist/styles.css` continues to ship its own generic defaults; the runtime overrides them with theme-aware values via inline styles on `<pre>`.
29
+
30
+ #### Pattern 3: `disposeHighlighter()` lifecycle (from VitePress)
31
+
32
+ - New exported function `disposeHighlighter()` — releases all cached Shiki highlighters (WASM engine, loaded grammars, theme cache) and clears the cache.
33
+ - Intended for long-running dev servers / watch mode where themes change over time. After calling, the next render creates a fresh highlighter.
34
+ - VitePress was the only community package that properly disposed its highlighter; we now match that behavior.
35
+
36
+ #### Pattern 4: Event-delegation copy button + MutationObserver (from VitePress + expressive-code)
37
+
38
+ - The copy-button client script (`src/copy-script.ts`) now registers a `MutationObserver` that watches for new code blocks added to the DOM (e.g. by React/Vue re-render, Astro view transitions, Turbolinks navigation) and re-applies the `.no-js → .js` class swap + ensures the aria-live region exists.
39
+ - New `astro:page-load` event listener for Astro view transitions — re-initializes UI state after SPA navigations.
40
+ - The script already used event delegation (`document.addEventListener('click', ...)` with `closest('.pcb__copy')`); Pattern 4 completes the SPA-robustness story.
41
+ - No inline `onclick` handlers (CSP-friendly); no per-button listeners (efficient for pages with many code blocks).
42
+
43
+ #### Pattern 5: Word-level diff (selective adoption from expressive-code)
44
+
45
+ - New `src/word-diff.ts` module with `wordDiff(oldStr, newStr): DiffToken[]` and `hasChanges(tokens): boolean` — a self-contained LCS-based word diff algorithm (~80 lines, no external deps).
46
+ - New `wordDiff: boolean` option (default `false`, opt-in). When enabled alongside `diff: true`, adjacent `pcb__line--del` / `pcb__line--add` pairs are post-processed: the per-word diff is computed and changed words are wrapped in `<mark class="pcb__word-diff--del">` / `<mark class="pcb__word-diff--add">` elements.
47
+ - Makes it easy for readers to see exactly what changed within a diff line, not just which lines changed.
48
+ - `wordDiff` and `hasChanges` are also exported as standalone utilities for users who want to compute word diffs in their own code.
49
+ - This is a selective adoption of expressive-code's token-annotation architecture — we did NOT rewrite the transformer to use `codeToTokensBase` + annotations (that would be a major rewrite). Instead, we added the most impactful feature (word-level diff) as a post-processing step on top of the existing HAST-walking code.
50
+
51
+ ### Verification
52
+
53
+ - All 1051 pre-existing tests pass (no regressions).
54
+ - New `test-architecture-patterns.mjs` adds 41 regression tests covering all 5 patterns.
55
+ - Total: 1092/1092 tests passing.
56
+
57
+ ### References
58
+
59
+ - Architecture comparison report: 590-line analysis of 6 community packages (rehype-pretty-code, expressive-code, @shikijs/transformers, VitePress, Docusaurus, astro-expressive-code) with 30 cited source file references.
60
+ - Pattern 1 source: `expressive-code/packages/@expressive-code/plugin-shiki/src/highlighter.ts:133-170` (`runHighlighterTask`).
61
+ - Pattern 2 source: `expressive-code/packages/@expressive-code/core/src/internal/core-styles.ts:191-215` (theme-aware defaults as functions).
62
+ - Pattern 3 source: `vitepress/src/node/markdown/plugins/highlight.ts:223` (`highlighter.dispose()` return).
63
+ - Pattern 4 source: `vitepress/src/client/app/composables/copyCode.ts` + `expressive-code/packages/@expressive-code/plugin-frames/src/copy-js-module.ts`.
64
+ - Pattern 5 source: `expressive-code/packages/@expressive-code/plugin-text-markers/src/index.ts` (`mark`/`ins`/`del` word-level markers).
65
+
8
66
  ## [1.2.2] — 2026-06-19
9
67
 
10
68
  ### Summary
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Color manipulation utilities for theme-aware CSS variable defaults.
3
+ *
4
+ * Pattern 2 (adopted from expressive-code's `helpers/color-transforms.ts`):
5
+ * CSS variable defaults are derived from the loaded Shiki theme and adjusted
6
+ * to meet WCAG contrast ratios, so code blocks look good with ANY Shiki theme
7
+ * out of the box — line numbers, diff backgrounds, and focus highlights are
8
+ * automatically legible against the theme's background color.
9
+ *
10
+ * The functions here are intentionally minimal — we only implement what we
11
+ * need to compute a few default `--pcb-*` values. For full color manipulation
12
+ * (lighten/darken/mix), see expressive-code's implementation.
13
+ */
14
+ /** A color in RGB format (0–255 per channel). */
15
+ export interface RGB {
16
+ r: number;
17
+ g: number;
18
+ b: number;
19
+ a?: number;
20
+ }
21
+ /** Contrast ratio per WCAG 2.1: https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio (1–21). */
22
+ export declare function contrastRatio(fg: RGB, bg: RGB): number;
23
+ /**
24
+ * Parse a hex color (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) or rgb()/rgba() string
25
+ * into an RGB object. Returns null if the input can't be parsed.
26
+ */
27
+ export declare function parseColor(input: string | undefined | null): RGB | null;
28
+ /** Convert an RGB color back to a hex string (#RRGGBB or #RRGGBBAA). */
29
+ export declare function toHex({ r, g, b, a }: RGB): string;
30
+ /** Mix two colors by a ratio (0 = pure a, 1 = pure b). */
31
+ export declare function mix(a: RGB, b: RGB, ratio: number): RGB;
32
+ /** Lighten a color toward white by a ratio (0–1). */
33
+ export declare function lighten(color: RGB, ratio: number): RGB;
34
+ /** Darken a color toward black by a ratio (0–1). */
35
+ export declare function darken(color: RGB, ratio: number): RGB;
36
+ /**
37
+ * Adjust a foreground color to meet a target contrast ratio against a
38
+ * background. If the contrast is already sufficient, returns the foreground
39
+ * unchanged. Otherwise, lightens (if bg is dark) or darkens (if bg is light)
40
+ * the foreground until the target ratio is met.
41
+ *
42
+ * @param fg Foreground color to adjust
43
+ * @param bg Background color to adjust against
44
+ * @param minRatio Minimum WCAG contrast ratio (default 4.5 = AA for normal text)
45
+ * @param maxRatio Maximum ratio to aim for if adjusting (default 7.0 = AAA)
46
+ * @returns The adjusted foreground color (or the original if already sufficient)
47
+ */
48
+ export declare function ensureColorContrastOnBackground(fg: RGB, bg: RGB, minRatio?: number, maxRatio?: number): RGB;
49
+ /**
50
+ * Extract the background and foreground colors from a Shiki theme object.
51
+ * Shiki themes have a `bg` and `fg` property at the top level (hex strings).
52
+ * Returns nulls if the theme shape doesn't match.
53
+ */
54
+ export declare function extractThemeColors(theme: unknown): {
55
+ bg: RGB | null;
56
+ fg: RGB | null;
57
+ };
58
+ /**
59
+ * Compute theme-aware `--pcb-*` defaults for a code block based on the
60
+ * loaded Shiki theme. These are applied as inline styles on the `<figure>`
61
+ * element, so the static `dist/styles.css` can ship its own defaults while
62
+ * the runtime overrides them with theme-aware values.
63
+ *
64
+ * Currently computes:
65
+ * - --pcb-bg: theme background (or 'inherit' if unknown)
66
+ * - --pcb-fg: theme foreground (or 'inherit' if unknown)
67
+ * - --pcb-line-numbers-fg: theme fg, contrast-adjusted against theme bg
68
+ * - --pcb-line-highlight-bg: theme fg at ~12% alpha (subtle highlight)
69
+ * - --pcb-line-diff-add-bg: green at ~18% alpha
70
+ * - --pcb-line-diff-del-bg: red at ~18% alpha
71
+ * - --pcb-line-focus-bg: theme fg at ~6% alpha (subtle dim)
72
+ *
73
+ * @param theme The Shiki theme object (must have `bg` and `fg` hex strings)
74
+ * @returns A CSS style string (e.g. `--pcb-bg:#fff;--pcb-fg:#000;...`) or empty string
75
+ */
76
+ export declare function computeThemeAwareDefaults(theme: unknown): string;
77
+ //# sourceMappingURL=color-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-utils.d.ts","sourceRoot":"","sources":["../src/color-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,iDAAiD;AACjD,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ;AAWD,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,MAAM,CAMtD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CA8BvE;AAED,wEAAwE;AACxE,wBAAgB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,MAAM,CAKjD;AAED,0DAA0D;AAC1D,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,CAQtD;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,CAEtD;AAED,oDAAoD;AACpD,wBAAgB,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,CAErD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,GAAG,EACP,EAAE,EAAE,GAAG,EACP,QAAQ,SAAM,EACd,QAAQ,SAAM,GACb,GAAG,CA0BL;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,EAAE,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,EAAE,EAAE,GAAG,GAAG,IAAI,CAAA;CAAE,CAMrF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAgChE"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Color manipulation utilities for theme-aware CSS variable defaults.
3
+ *
4
+ * Pattern 2 (adopted from expressive-code's `helpers/color-transforms.ts`):
5
+ * CSS variable defaults are derived from the loaded Shiki theme and adjusted
6
+ * to meet WCAG contrast ratios, so code blocks look good with ANY Shiki theme
7
+ * out of the box — line numbers, diff backgrounds, and focus highlights are
8
+ * automatically legible against the theme's background color.
9
+ *
10
+ * The functions here are intentionally minimal — we only implement what we
11
+ * need to compute a few default `--pcb-*` values. For full color manipulation
12
+ * (lighten/darken/mix), see expressive-code's implementation.
13
+ */
14
+ /** Relative luminance per WCAG 2.1: https://www.w3.org/TR/WCAG21/#dfn-relative-luminance */
15
+ function relativeLuminance({ r, g, b }) {
16
+ const channel = (c) => {
17
+ const s = c / 255;
18
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
19
+ };
20
+ return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
21
+ }
22
+ /** Contrast ratio per WCAG 2.1: https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio (1–21). */
23
+ export function contrastRatio(fg, bg) {
24
+ const l1 = relativeLuminance(fg);
25
+ const l2 = relativeLuminance(bg);
26
+ const lighter = Math.max(l1, l2);
27
+ const darker = Math.min(l1, l2);
28
+ return (lighter + 0.05) / (darker + 0.05);
29
+ }
30
+ /**
31
+ * Parse a hex color (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) or rgb()/rgba() string
32
+ * into an RGB object. Returns null if the input can't be parsed.
33
+ */
34
+ export function parseColor(input) {
35
+ if (!input)
36
+ return null;
37
+ const s = input.trim().toLowerCase();
38
+ // Hex: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
39
+ const hexMatch = s.match(/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/);
40
+ if (hexMatch) {
41
+ let hex = hexMatch[1];
42
+ if (hex.length === 3 || hex.length === 4) {
43
+ hex = hex
44
+ .split('')
45
+ .map((c) => c + c)
46
+ .join('');
47
+ }
48
+ const r = parseInt(hex.slice(0, 2), 16);
49
+ const g = parseInt(hex.slice(2, 4), 16);
50
+ const b = parseInt(hex.slice(4, 6), 16);
51
+ const a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : undefined;
52
+ return { r, g, b, a };
53
+ }
54
+ // rgb() / rgba()
55
+ const rgbMatch = s.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)$/);
56
+ if (rgbMatch) {
57
+ return {
58
+ r: parseInt(rgbMatch[1], 10),
59
+ g: parseInt(rgbMatch[2], 10),
60
+ b: parseInt(rgbMatch[3], 10),
61
+ a: rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) : undefined,
62
+ };
63
+ }
64
+ return null;
65
+ }
66
+ /** Convert an RGB color back to a hex string (#RRGGBB or #RRGGBBAA). */
67
+ export function toHex({ r, g, b, a }) {
68
+ const toHex2 = (n) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, '0');
69
+ const base = `#${toHex2(r)}${toHex2(g)}${toHex2(b)}`;
70
+ if (a !== undefined && a < 1)
71
+ return base + toHex2(a * 255);
72
+ return base;
73
+ }
74
+ /** Mix two colors by a ratio (0 = pure a, 1 = pure b). */
75
+ export function mix(a, b, ratio) {
76
+ const t = Math.max(0, Math.min(1, ratio));
77
+ return {
78
+ r: a.r * (1 - t) + b.r * t,
79
+ g: a.g * (1 - t) + b.g * t,
80
+ b: a.b * (1 - t) + b.b * t,
81
+ a: a.a !== undefined || b.a !== undefined ? (a.a ?? 1) * (1 - t) + (b.a ?? 1) * t : undefined,
82
+ };
83
+ }
84
+ /** Lighten a color toward white by a ratio (0–1). */
85
+ export function lighten(color, ratio) {
86
+ return mix(color, { r: 255, g: 255, b: 255 }, ratio);
87
+ }
88
+ /** Darken a color toward black by a ratio (0–1). */
89
+ export function darken(color, ratio) {
90
+ return mix(color, { r: 0, g: 0, b: 0 }, ratio);
91
+ }
92
+ /**
93
+ * Adjust a foreground color to meet a target contrast ratio against a
94
+ * background. If the contrast is already sufficient, returns the foreground
95
+ * unchanged. Otherwise, lightens (if bg is dark) or darkens (if bg is light)
96
+ * the foreground until the target ratio is met.
97
+ *
98
+ * @param fg Foreground color to adjust
99
+ * @param bg Background color to adjust against
100
+ * @param minRatio Minimum WCAG contrast ratio (default 4.5 = AA for normal text)
101
+ * @param maxRatio Maximum ratio to aim for if adjusting (default 7.0 = AAA)
102
+ * @returns The adjusted foreground color (or the original if already sufficient)
103
+ */
104
+ export function ensureColorContrastOnBackground(fg, bg, minRatio = 4.5, maxRatio = 7.0) {
105
+ const current = contrastRatio(fg, bg);
106
+ if (current >= minRatio)
107
+ return fg;
108
+ // Decide direction: if bg is dark (luminance < 0.5), lighten fg; else darken.
109
+ const bgLum = relativeLuminance(bg);
110
+ const direction = bgLum < 0.5 ? 'lighten' : 'darken';
111
+ // Binary search between current and pure white/black for the target ratio.
112
+ let lo = 0;
113
+ let hi = 1;
114
+ let best = fg;
115
+ for (let i = 0; i < 16; i++) {
116
+ const mid = (lo + hi) / 2;
117
+ const candidate = direction === 'lighten' ? lighten(fg, mid) : darken(fg, mid);
118
+ const ratio = contrastRatio(candidate, bg);
119
+ if (ratio >= minRatio && ratio <= maxRatio) {
120
+ return candidate;
121
+ }
122
+ if (ratio < minRatio) {
123
+ lo = mid;
124
+ }
125
+ else {
126
+ best = candidate;
127
+ hi = mid;
128
+ }
129
+ }
130
+ return best;
131
+ }
132
+ /**
133
+ * Extract the background and foreground colors from a Shiki theme object.
134
+ * Shiki themes have a `bg` and `fg` property at the top level (hex strings).
135
+ * Returns nulls if the theme shape doesn't match.
136
+ */
137
+ export function extractThemeColors(theme) {
138
+ const t = theme;
139
+ return {
140
+ bg: parseColor(t?.bg),
141
+ fg: parseColor(t?.fg),
142
+ };
143
+ }
144
+ /**
145
+ * Compute theme-aware `--pcb-*` defaults for a code block based on the
146
+ * loaded Shiki theme. These are applied as inline styles on the `<figure>`
147
+ * element, so the static `dist/styles.css` can ship its own defaults while
148
+ * the runtime overrides them with theme-aware values.
149
+ *
150
+ * Currently computes:
151
+ * - --pcb-bg: theme background (or 'inherit' if unknown)
152
+ * - --pcb-fg: theme foreground (or 'inherit' if unknown)
153
+ * - --pcb-line-numbers-fg: theme fg, contrast-adjusted against theme bg
154
+ * - --pcb-line-highlight-bg: theme fg at ~12% alpha (subtle highlight)
155
+ * - --pcb-line-diff-add-bg: green at ~18% alpha
156
+ * - --pcb-line-diff-del-bg: red at ~18% alpha
157
+ * - --pcb-line-focus-bg: theme fg at ~6% alpha (subtle dim)
158
+ *
159
+ * @param theme The Shiki theme object (must have `bg` and `fg` hex strings)
160
+ * @returns A CSS style string (e.g. `--pcb-bg:#fff;--pcb-fg:#000;...`) or empty string
161
+ */
162
+ export function computeThemeAwareDefaults(theme) {
163
+ const { bg, fg } = extractThemeColors(theme);
164
+ if (!bg || !fg)
165
+ return '';
166
+ const parts = [];
167
+ parts.push(`--pcb-bg:${toHex(bg)}`);
168
+ parts.push(`--pcb-fg:${toHex(fg)}`);
169
+ // Line numbers: use fg, but adjust contrast to >= 3.0 (WCAG AA for large text)
170
+ // against the background. Shiki themes often have low-contrast line-number
171
+ // colors baked in; we override them with a guaranteed-legible value.
172
+ const lineNumFg = ensureColorContrastOnBackground(fg, bg, 3.0, 4.5);
173
+ parts.push(`--pcb-ln-fg:${toHex(lineNumFg)}`);
174
+ // Line highlight background: subtle tint of the foreground at ~12% alpha.
175
+ // Use mix() with the background to get a slightly-lighter/darker shade.
176
+ const hlBg = mix(bg, fg, 0.12);
177
+ parts.push(`--pcb-line-highlight-bg:${toHex(hlBg)}`);
178
+ // Diff add: green (#22863a in github) at 18% alpha over bg.
179
+ const diffAdd = mix(bg, { r: 34, g: 134, b: 58 }, 0.18);
180
+ parts.push(`--pcb-line-add-bg:${toHex(diffAdd)}`);
181
+ // Diff del: red (#cb2431 in github) at 18% alpha over bg.
182
+ const diffDel = mix(bg, { r: 203, g: 36, b: 49 }, 0.18);
183
+ parts.push(`--pcb-line-del-bg:${toHex(diffDel)}`);
184
+ // Focus background: dim the non-focused lines by mixing bg with fg at low alpha.
185
+ const focusBg = mix(bg, fg, 0.04);
186
+ parts.push(`--pcb-line-focus-bg:${toHex(focusBg)}`);
187
+ return parts.join(';');
188
+ }
189
+ //# sourceMappingURL=color-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-utils.js","sourceRoot":"","sources":["../src/color-utils.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * The copy-button client script. Inlined once per page (deduped by the
3
- * Astro integration via `injectScript`). ~600 bytes gzipped.
3
+ * Astro integration via `injectScript`). ~1.2 KB gzipped.
4
4
  *
5
5
  * Behavior:
6
- * - Listens for clicks on `.pcb__copy`
6
+ * - Listens for clicks on `.pcb__copy` (event delegation on document)
7
7
  * - Copies the textContent of the nearest `pre code`
8
8
  * - Toggles `.pcb__copy--done` and swaps the icon + label
9
9
  * - Resets after `data-feedback-duration` ms (default 1600)
@@ -13,6 +13,13 @@
13
13
  * - Strips leading `#` comment lines when `data-strip-comments` is set (terminal preset)
14
14
  * - Announces "Copied" to screen readers via an aria-live region (WCAG 4.1.2)
15
15
  * - Hides copy button when JS is disabled (via .no-js class on <html>)
16
+ *
17
+ * Pattern 4 (adopted from VitePress + expressive-code):
18
+ * - Event delegation via `document.addEventListener('click', ...)` — works
19
+ * regardless of how buttons were rendered (SSR, CSR, view transitions).
20
+ * - MutationObserver re-initializes the aria-live region and the .no-js → .js
21
+ * class swap when new code blocks are added to the DOM (SPA support).
22
+ * - `astro:page-load` event listener for Astro view transitions.
16
23
  */
17
- export declare const COPY_SCRIPT = "\n(function () {\n if (window.__pcbCopyReady) return;\n window.__pcbCopyReady = true;\n\n // Remove the .no-js class so the copy buttons become visible (graceful degradation).\n if (document.documentElement.classList.contains('no-js')) {\n document.documentElement.classList.remove('no-js');\n document.documentElement.classList.add('js');\n }\n\n // Reuse a single aria-live region for all copy announcements.\n var liveRegion = document.querySelector('.pcb__sr-live');\n if (!liveRegion) {\n liveRegion = document.createElement('span');\n liveRegion.className = 'pcb__sr-live';\n liveRegion.setAttribute('aria-live', 'polite');\n liveRegion.setAttribute('aria-atomic', 'true');\n liveRegion.setAttribute('role', 'status');\n document.body.appendChild(liveRegion);\n }\n\n function announce(msg) {\n if (!liveRegion) return;\n liveRegion.textContent = '';\n // Force re-announcement by clearing then setting on next tick.\n setTimeout(function () { liveRegion.textContent = msg; }, 50);\n }\n\n function findLabel(btn) {\n return btn.querySelector('.pcb__copy-label');\n }\n\n function findIcon(btn) {\n return btn.querySelector('svg');\n }\n\n // Strip leading comment lines (e.g. shell prompts like \"# comment\") from\n // the text before copying. Used for terminal-preset blocks where the\n // displayed code may include comments the user doesn't want on the clipboard.\n function stripComments(text) {\n // Strip lines that start with optional whitespace followed by # (shell),\n // // (C-style), or REM (Windows batch). Keep everything else.\n return text.replace(/^[ \\t]*(?:#|\\/\\/|REM\\b).*$/gm, '').replace(/\\n{3,}/g, '\\n\\n').trim();\n }\n\n document.addEventListener('click', function (e) {\n var btn = e.target && e.target.closest && e.target.closest('.pcb__copy');\n if (!btn) return;\n var figure = btn.closest('.pcb');\n var code = figure && figure.querySelector('pre code');\n if (!code) return;\n\n var done = btn.getAttribute('data-done-label') || 'copied!';\n var duration = parseInt(btn.getAttribute('data-feedback-duration') || '1600', 10);\n var successIconHtml = btn.getAttribute('data-success-icon');\n var stripCommentsFlag = btn.hasAttribute('data-strip-comments');\n\n var label = findLabel(btn);\n var icon = findIcon(btn);\n var originalLabel = label ? label.textContent : null;\n var originalIconHtml = icon ? icon.outerHTML : null;\n\n var rawText = code.innerText;\n var textToCopy = stripCommentsFlag ? stripComments(rawText) : rawText;\n\n var finish = function () {\n btn.classList.add('pcb__copy--done');\n if (label) label.textContent = done;\n if (successIconHtml && icon) {\n var tmp = document.createElement('span');\n tmp.innerHTML = successIconHtml;\n var newIcon = tmp.firstChild;\n if (newIcon) {\n icon.replaceWith(newIcon);\n icon = newIcon;\n }\n }\n announce(done);\n setTimeout(function () {\n btn.classList.remove('pcb__copy--done');\n if (label && originalLabel != null) label.textContent = originalLabel;\n if (originalIconHtml && icon) {\n var tmp2 = document.createElement('span');\n tmp2.innerHTML = originalIconHtml;\n var oldIcon = icon;\n icon = tmp2.firstChild;\n if (icon) oldIcon.replaceWith(icon);\n }\n }, duration);\n };\n\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(textToCopy).then(finish).catch(fallback);\n } else {\n fallback();\n }\n\n function fallback() {\n var ta = document.createElement('textarea');\n ta.value = textToCopy;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n try { document.execCommand('copy'); finish(); } catch (_) {}\n document.body.removeChild(ta);\n }\n });\n})();\n";
24
+ export declare const COPY_SCRIPT = "\n(function () {\n if (window.__pcbCopyReady) return;\n window.__pcbCopyReady = true;\n\n // Remove the .no-js class so the copy buttons become visible (graceful degradation).\n function swapNoJs() {\n if (document.documentElement.classList.contains('no-js')) {\n document.documentElement.classList.remove('no-js');\n document.documentElement.classList.add('js');\n }\n }\n swapNoJs();\n\n // Reuse a single aria-live region for all copy announcements.\n var liveRegion = null;\n function ensureLiveRegion() {\n if (liveRegion && document.body.contains(liveRegion)) return liveRegion;\n liveRegion = document.querySelector('.pcb__sr-live');\n if (!liveRegion) {\n liveRegion = document.createElement('span');\n liveRegion.className = 'pcb__sr-live';\n liveRegion.setAttribute('aria-live', 'polite');\n liveRegion.setAttribute('aria-atomic', 'true');\n liveRegion.setAttribute('role', 'status');\n document.body.appendChild(liveRegion);\n }\n return liveRegion;\n }\n ensureLiveRegion();\n\n function announce(msg) {\n var lr = ensureLiveRegion();\n if (!lr) return;\n lr.textContent = '';\n // Force re-announcement by clearing then setting on next tick.\n setTimeout(function () { lr.textContent = msg; }, 50);\n }\n\n function findLabel(btn) {\n return btn.querySelector('.pcb__copy-label');\n }\n\n function findIcon(btn) {\n return btn.querySelector('svg');\n }\n\n // Strip leading comment lines (e.g. shell prompts like \"# comment\") from\n // the text before copying. Used for terminal-preset blocks where the\n // displayed code may include comments the user doesn't want on the clipboard.\n function stripComments(text) {\n // Strip lines that start with optional whitespace followed by # (shell),\n // // (C-style), or REM (Windows batch). Keep everything else.\n return text.replace(/^[ \\t]*(?:#|\\/\\/|REM\\b).*$/gm, '').replace(/\\n{3,}/g, '\\n\\n').trim();\n }\n\n // Event-delegated click handler. Works for buttons added after initial\n // render (e.g. via React/Vue re-render or Astro view transitions) because\n // the listener is on document, not on each button.\n document.addEventListener('click', function (e) {\n var btn = e.target && e.target.closest && e.target.closest('.pcb__copy');\n if (!btn) return;\n var figure = btn.closest('.pcb');\n var code = figure && figure.querySelector('pre code');\n if (!code) return;\n\n var done = btn.getAttribute('data-done-label') || 'copied!';\n var duration = parseInt(btn.getAttribute('data-feedback-duration') || '1600', 10);\n var successIconHtml = btn.getAttribute('data-success-icon');\n var stripCommentsFlag = btn.hasAttribute('data-strip-comments');\n\n var label = findLabel(btn);\n var icon = findIcon(btn);\n var originalLabel = label ? label.textContent : null;\n var originalIconHtml = icon ? icon.outerHTML : null;\n\n var rawText = code.innerText;\n var textToCopy = stripCommentsFlag ? stripComments(rawText) : rawText;\n\n var finish = function () {\n btn.classList.add('pcb__copy--done');\n if (label) label.textContent = done;\n if (successIconHtml && icon) {\n var tmp = document.createElement('span');\n tmp.innerHTML = successIconHtml;\n var newIcon = tmp.firstChild;\n if (newIcon) {\n icon.replaceWith(newIcon);\n icon = newIcon;\n }\n }\n announce(done);\n setTimeout(function () {\n btn.classList.remove('pcb__copy--done');\n if (label && originalLabel != null) label.textContent = originalLabel;\n if (originalIconHtml && icon) {\n var tmp2 = document.createElement('span');\n tmp2.innerHTML = originalIconHtml;\n var oldIcon = icon;\n icon = tmp2.firstChild;\n if (icon) oldIcon.replaceWith(icon);\n }\n }, duration);\n };\n\n if (navigator.clipboard && navigator.clipboard.writeText) {\n navigator.clipboard.writeText(textToCopy).then(finish).catch(fallback);\n } else {\n fallback();\n }\n\n function fallback() {\n var ta = document.createElement('textarea');\n ta.value = textToCopy;\n ta.style.position = 'fixed';\n ta.style.opacity = '0';\n document.body.appendChild(ta);\n ta.select();\n try { document.execCommand('copy'); finish(); } catch (_) {}\n document.body.removeChild(ta);\n }\n });\n\n // Pattern 4: MutationObserver for SPA support.\n // When new code blocks are inserted into the DOM (e.g. by React/Vue\n // re-render, Astro view transitions, or Turbolinks navigation), the\n // .no-js \u2192 .js class swap may need to be re-applied so newly-added\n // copy buttons become visible. The observer watches for added .pcb nodes.\n if (typeof MutationObserver !== 'undefined') {\n var pendingObserve = false;\n var observer = new MutationObserver(function (mutations) {\n // Batch checks with microtask to avoid layout thrash.\n if (pendingObserve) return;\n pendingObserve = true;\n Promise.resolve().then(function () {\n pendingObserve = false;\n // If any new .pcb nodes were added, ensure the .js class is set\n // (in case the page was rendered server-side with .no-js and the\n // client took over after initial load).\n for (var i = 0; i < mutations.length; i++) {\n if (mutations[i].addedNodes && mutations[i].addedNodes.length) {\n swapNoJs();\n ensureLiveRegion();\n break;\n }\n }\n });\n });\n // Observe the whole document subtree for added nodes.\n observer.observe(document.documentElement, { childList: true, subtree: true });\n }\n\n // Pattern 4: astro:page-load event listener for Astro view transitions.\n // Astro emits this event after a view transition completes; the new page's\n // DOM may have replaced the old, so re-apply the .no-js \u2192 .js swap.\n if (typeof document.addEventListener === 'function') {\n document.addEventListener('astro:page-load', function () {\n swapNoJs();\n ensureLiveRegion();\n });\n }\n})();\n";
18
25
  //# sourceMappingURL=copy-script.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"copy-script.d.ts","sourceRoot":"","sources":["../src/copy-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW,67HA8GvB,CAAC"}
1
+ {"version":3,"file":"copy-script.d.ts","sourceRoot":"","sources":["../src/copy-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,WAAW,8kMAkKvB,CAAC"}
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * The copy-button client script. Inlined once per page (deduped by the
3
- * Astro integration via `injectScript`). ~600 bytes gzipped.
3
+ * Astro integration via `injectScript`). ~1.2 KB gzipped.
4
4
  *
5
5
  * Behavior:
6
- * - Listens for clicks on `.pcb__copy`
6
+ * - Listens for clicks on `.pcb__copy` (event delegation on document)
7
7
  * - Copies the textContent of the nearest `pre code`
8
8
  * - Toggles `.pcb__copy--done` and swaps the icon + label
9
9
  * - Resets after `data-feedback-duration` ms (default 1600)
@@ -13,6 +13,13 @@
13
13
  * - Strips leading `#` comment lines when `data-strip-comments` is set (terminal preset)
14
14
  * - Announces "Copied" to screen readers via an aria-live region (WCAG 4.1.2)
15
15
  * - Hides copy button when JS is disabled (via .no-js class on <html>)
16
+ *
17
+ * Pattern 4 (adopted from VitePress + expressive-code):
18
+ * - Event delegation via `document.addEventListener('click', ...)` — works
19
+ * regardless of how buttons were rendered (SSR, CSR, view transitions).
20
+ * - MutationObserver re-initializes the aria-live region and the .no-js → .js
21
+ * class swap when new code blocks are added to the DOM (SPA support).
22
+ * - `astro:page-load` event listener for Astro view transitions.
16
23
  */
17
24
  export const COPY_SCRIPT = `
18
25
  (function () {
@@ -20,27 +27,37 @@ export const COPY_SCRIPT = `
20
27
  window.__pcbCopyReady = true;
21
28
 
22
29
  // Remove the .no-js class so the copy buttons become visible (graceful degradation).
23
- if (document.documentElement.classList.contains('no-js')) {
24
- document.documentElement.classList.remove('no-js');
25
- document.documentElement.classList.add('js');
30
+ function swapNoJs() {
31
+ if (document.documentElement.classList.contains('no-js')) {
32
+ document.documentElement.classList.remove('no-js');
33
+ document.documentElement.classList.add('js');
34
+ }
26
35
  }
36
+ swapNoJs();
27
37
 
28
38
  // Reuse a single aria-live region for all copy announcements.
29
- var liveRegion = document.querySelector('.pcb__sr-live');
30
- if (!liveRegion) {
31
- liveRegion = document.createElement('span');
32
- liveRegion.className = 'pcb__sr-live';
33
- liveRegion.setAttribute('aria-live', 'polite');
34
- liveRegion.setAttribute('aria-atomic', 'true');
35
- liveRegion.setAttribute('role', 'status');
36
- document.body.appendChild(liveRegion);
39
+ var liveRegion = null;
40
+ function ensureLiveRegion() {
41
+ if (liveRegion && document.body.contains(liveRegion)) return liveRegion;
42
+ liveRegion = document.querySelector('.pcb__sr-live');
43
+ if (!liveRegion) {
44
+ liveRegion = document.createElement('span');
45
+ liveRegion.className = 'pcb__sr-live';
46
+ liveRegion.setAttribute('aria-live', 'polite');
47
+ liveRegion.setAttribute('aria-atomic', 'true');
48
+ liveRegion.setAttribute('role', 'status');
49
+ document.body.appendChild(liveRegion);
50
+ }
51
+ return liveRegion;
37
52
  }
53
+ ensureLiveRegion();
38
54
 
39
55
  function announce(msg) {
40
- if (!liveRegion) return;
41
- liveRegion.textContent = '';
56
+ var lr = ensureLiveRegion();
57
+ if (!lr) return;
58
+ lr.textContent = '';
42
59
  // Force re-announcement by clearing then setting on next tick.
43
- setTimeout(function () { liveRegion.textContent = msg; }, 50);
60
+ setTimeout(function () { lr.textContent = msg; }, 50);
44
61
  }
45
62
 
46
63
  function findLabel(btn) {
@@ -60,6 +77,9 @@ export const COPY_SCRIPT = `
60
77
  return text.replace(/^[ \\t]*(?:#|\\/\\/|REM\\b).*$/gm, '').replace(/\\n{3,}/g, '\\n\\n').trim();
61
78
  }
62
79
 
80
+ // Event-delegated click handler. Works for buttons added after initial
81
+ // render (e.g. via React/Vue re-render or Astro view transitions) because
82
+ // the listener is on document, not on each button.
63
83
  document.addEventListener('click', function (e) {
64
84
  var btn = e.target && e.target.closest && e.target.closest('.pcb__copy');
65
85
  if (!btn) return;
@@ -123,6 +143,45 @@ export const COPY_SCRIPT = `
123
143
  document.body.removeChild(ta);
124
144
  }
125
145
  });
146
+
147
+ // Pattern 4: MutationObserver for SPA support.
148
+ // When new code blocks are inserted into the DOM (e.g. by React/Vue
149
+ // re-render, Astro view transitions, or Turbolinks navigation), the
150
+ // .no-js → .js class swap may need to be re-applied so newly-added
151
+ // copy buttons become visible. The observer watches for added .pcb nodes.
152
+ if (typeof MutationObserver !== 'undefined') {
153
+ var pendingObserve = false;
154
+ var observer = new MutationObserver(function (mutations) {
155
+ // Batch checks with microtask to avoid layout thrash.
156
+ if (pendingObserve) return;
157
+ pendingObserve = true;
158
+ Promise.resolve().then(function () {
159
+ pendingObserve = false;
160
+ // If any new .pcb nodes were added, ensure the .js class is set
161
+ // (in case the page was rendered server-side with .no-js and the
162
+ // client took over after initial load).
163
+ for (var i = 0; i < mutations.length; i++) {
164
+ if (mutations[i].addedNodes && mutations[i].addedNodes.length) {
165
+ swapNoJs();
166
+ ensureLiveRegion();
167
+ break;
168
+ }
169
+ }
170
+ });
171
+ });
172
+ // Observe the whole document subtree for added nodes.
173
+ observer.observe(document.documentElement, { childList: true, subtree: true });
174
+ }
175
+
176
+ // Pattern 4: astro:page-load event listener for Astro view transitions.
177
+ // Astro emits this event after a view transition completes; the new page's
178
+ // DOM may have replaced the old, so re-apply the .no-js → .js swap.
179
+ if (typeof document.addEventListener === 'function') {
180
+ document.addEventListener('astro:page-load', function () {
181
+ swapNoJs();
182
+ ensureLiveRegion();
183
+ });
184
+ }
126
185
  })();
127
186
  `;
128
187
  //# sourceMappingURL=copy-script.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"copy-script.js","sourceRoot":"","sources":["../src/copy-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8G1B,CAAC"}
1
+ {"version":3,"file":"copy-script.js","sourceRoot":"","sources":["../src/copy-script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkK1B,CAAC"}
package/dist/index.d.ts CHANGED
@@ -18,9 +18,15 @@
18
18
  */
19
19
  import type { Plugin } from 'unified';
20
20
  import type { Root } from 'hast';
21
+ import { disposeHighlighter, runHighlighterTask } from './shiki.js';
21
22
  import { remarkPreserveCodeMeta } from './remark.js';
23
+ import { wordDiff, hasChanges } from './word-diff.js';
24
+ import type { DiffToken } from './word-diff.js';
22
25
  import type { PerfectCodeOptions } from './types.js';
23
26
  export { remarkPreserveCodeMeta };
27
+ export { disposeHighlighter, runHighlighterTask };
28
+ export { wordDiff, hasChanges };
29
+ export type { DiffToken };
24
30
  export declare const rehypePerfectCodeBlocks: Plugin<[PerfectCodeOptions?], Root>;
25
31
  export default rehypePerfectCodeBlocks;
26
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAGjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAmBrE,CAAC;AAEJ,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAuB,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAChC,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAmBrE,CAAC;AAEJ,eAAe,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -17,9 +17,12 @@
17
17
  * plugin with style/script injection and automatic Shiki integration.
18
18
  */
19
19
  import { rehypePerfectCodeBlocks as transformer } from './transformer.js';
20
- import { runShikiOnRawBlocks } from './shiki.js';
20
+ import { runShikiOnRawBlocks, disposeHighlighter, runHighlighterTask } from './shiki.js';
21
21
  import { remarkPreserveCodeMeta } from './remark.js';
22
+ import { wordDiff, hasChanges } from './word-diff.js';
22
23
  export { remarkPreserveCodeMeta };
24
+ export { disposeHighlighter, runHighlighterTask };
25
+ export { wordDiff, hasChanges };
23
26
  export const rehypePerfectCodeBlocks = (options = {}) => {
24
27
  const engine = options.engine ?? 'auto';
25
28
  const opts = options;
@@ -78,6 +81,7 @@ function resolveDefaults(opts) {
78
81
  lineNumbersStart: opts.lineNumbersStart ?? 1,
79
82
  highlight: opts.highlight ?? true,
80
83
  diff: opts.diff ?? true,
84
+ wordDiff: opts.wordDiff ?? false,
81
85
  focus: opts.focus ?? true,
82
86
  errorLevels: opts.errorLevels ?? true,
83
87
  wrap: opts.wrap ?? false,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAAE,uBAAuB,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAItD,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAGhC,MAAM,CAAC,MAAM,uBAAuB,GAClC,CAAC,OAAO,GAAG,EAAE,EAAE,EAAE;IACf,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC;IACxC,MAAM,IAAI,GAAG,OAAO,CAAC;IAErB,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;QACpB,8EAA8E;QAC9E,2EAA2E;QAC3E,0EAA0E;QAC1E,0BAA0B;QAC1B,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5C,MAAM,mBAAmB,CAAC,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,2DAA2D;QAE3D,2CAA2C;QAC3C,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC;AACJ,CAAC,CAAC;AAEJ,eAAe,uBAAuB,CAAC;AAEvC;;;;GAIG;AACH,SAAS,eAAe,CAAC,IAAwB;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IACnC,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC;IACvC,IAAI,eAAe,GAAG,MAAM,CAAC;IAC7B,IAAI,mBAAmB,GAAG,SAAS,CAAC;IACpC,IAAI,kBAAkB,GAAqC,IAAI,CAAC;IAEhE,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;QAClE,kBAAkB,GAAG;YACnB,UAAU,EAAE,QAAQ;YACpB,gBAAgB,EAAE,IAAI;YACtB,QAAQ,EAAE,SAAS;YACnB,WAAW,EAAE,SAAS;YACtB,KAAK,EAAE,MAAM;YACb,SAAS,EAAE,SAAS;YACpB,GAAG,cAAc;SAClB,CAAC;QACF,eAAe,GAAG,kBAAkB,CAAC,KAAK,IAAI,MAAM,CAAC;QACrD,mBAAmB,GAAG,kBAAkB,CAAC,SAAS,IAAI,SAAS,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,kBAAkB,GAAG,cAAc,IAAI,IAAI,CAAC;QAC5C,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,MAAM,CAAC;QACjD,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,IAAI,SAAS,CAAC;IAC9D,CAAC;IAED,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;QACrC,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI;QACvC,UAAU,EAAE,kBAAkB;QAC9B,eAAe;QACf,mBAAmB;QACnB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,MAAM;QACvC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,MAAM;QACjC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,CAAC;QAC5C,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;QACjC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;QAChC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI;QACzB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;QACrC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK;QACxB,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI;QACzC,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI;QAC3C,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,QAAQ;QAC7C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK;QAC5C,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,KAAK;QACxC,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,MAAM;QAC7B,KAAK,EAAE;YACL,KAAK,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE;YACrD,KAAK,EAAE,EAAE;YACT,YAAY,EAAE,EAAE;YAChB,gBAAgB,EAAE,OAAO;YACzB,GAAG,SAAS;SACb;QACD,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK;QAC5C,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,KAAK;QACxC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;QACnC,uBAAuB,EAAE,IAAI,CAAC,uBAAuB,IAAI,KAAK;QAC9D,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK;QAC5C,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,KAAK;QAChD,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,KAAK;QACtC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;QACnC,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,EAAE;QAC3C,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI;YACnC;gBACE,SAAS,EAAE,eAAe;gBAC1B,IAAI,EAAE,qBAAqB;gBAC3B,KAAK,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,eAAe,EAAE;aAC1D;SACF;QACD,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,KAAK;QACpC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,iBAAiB,IAAI,EAAE;QACzE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,iBAAiB,IAAI,EAAE;QACzE,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;QAC/B,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;QAC1H,uBAAuB,EAAE,IAAI,CAAC,uBAAuB,IAAI,KAAK;QAC9D,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,EAAE;QACzC,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,EAAE;QAC3C,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,EAAE;QAC7C,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC;QAC5B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,IAAI;QACjD,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,IAAI;QAC/C,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI;QACvC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,IAAI;QACjD,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,IAAI,IAAI;QACrD,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,EAAE;QACvC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACrD,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;QAC3C,sBAAsB,EAAE,IAAI,CAAC,sBAAsB,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;QACjE,uBAAuB,EAAE,IAAI,CAAC,uBAAuB,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;QACnE,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;QAC7C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;QACjD,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,OAAO;QAC9B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;QAChC,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI;QACvC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,MAAM;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;KAC7B,CAAC;AACJ,CAAC"}