@dr-ishaan/rehype-perfect-code-blocks 1.2.2 → 1.3.2
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 +98 -0
- package/LICENSE +0 -0
- package/README.md +225 -13
- package/dist/astro.d.ts +0 -0
- package/dist/astro.d.ts.map +1 -1
- package/dist/astro.js +9 -2
- package/dist/astro.js.map +1 -1
- package/dist/color-utils.d.ts +77 -0
- package/dist/color-utils.d.ts.map +1 -0
- package/dist/color-utils.js +189 -0
- package/dist/color-utils.js.map +1 -0
- package/dist/copy-script.d.ts +10 -3
- package/dist/copy-script.d.ts.map +1 -1
- package/dist/copy-script.js +108 -16
- package/dist/copy-script.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/meta.d.ts +0 -0
- package/dist/meta.d.ts.map +0 -0
- package/dist/meta.js +0 -0
- package/dist/meta.js.map +0 -0
- package/dist/remark.d.ts +0 -0
- package/dist/remark.d.ts.map +0 -0
- package/dist/remark.js +0 -0
- package/dist/remark.js.map +0 -0
- package/dist/shiki.d.ts +20 -0
- package/dist/shiki.d.ts.map +1 -1
- package/dist/shiki.js +116 -4
- package/dist/shiki.js.map +1 -1
- package/dist/styles.css +0 -0
- package/dist/transformer.d.ts +0 -0
- package/dist/transformer.d.ts.map +1 -1
- package/dist/transformer.js +108 -1
- package/dist/transformer.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -0
- package/dist/types.js.map +0 -0
- package/dist/word-diff.d.ts +47 -0
- package/dist/word-diff.d.ts.map +1 -0
- package/dist/word-diff.js +138 -0
- package/dist/word-diff.js.map +1 -0
- package/package.json +2 -2
- package/src/astro.ts +9 -2
- package/src/color-utils.ts +214 -0
- package/src/copy-script.ts +108 -16
- package/src/index.ts +7 -1
- package/src/meta.ts +0 -0
- package/src/remark.ts +0 -0
- package/src/shiki.ts +157 -10
- package/src/styles.css +0 -0
- package/src/transformer.ts +109 -1
- package/src/types.ts +12 -0
- package/src/vite-raw.d.ts +0 -0
- package/src/word-diff.ts +143 -0
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
File without changes
|
package/dist/types.js.map
CHANGED
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Word-level diff utility for Pattern 5 (selective adoption from expressive-code).
|
|
3
|
+
*
|
|
4
|
+
* Computes a per-word diff between two lines of code using a simple LCS-based
|
|
5
|
+
* algorithm. Used to highlight the specific words that changed within `+`/`-`
|
|
6
|
+
* diff lines, so readers can see exactly what was added/removed rather than
|
|
7
|
+
* just which lines changed.
|
|
8
|
+
*
|
|
9
|
+
* Algorithm: split each line into tokens (words + whitespace + punctuation),
|
|
10
|
+
* compute the LCS (Longest Common Subsequence) between the two token arrays,
|
|
11
|
+
* then walk both arrays emitting add/remove/equal markers.
|
|
12
|
+
*
|
|
13
|
+
* No external dependencies — this is ~80 lines of self-contained code.
|
|
14
|
+
*/
|
|
15
|
+
/** A diff token: the text content + whether it was added, removed, or unchanged. */
|
|
16
|
+
export interface DiffToken {
|
|
17
|
+
text: string;
|
|
18
|
+
type: 'add' | 'del' | 'equal';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Compute a word-level diff between two strings.
|
|
22
|
+
* Returns an array of DiffToken entries; concatenating all `.text` values
|
|
23
|
+
* reconstructs the union of both inputs. The `.type` field indicates whether
|
|
24
|
+
* each token was added, removed, or unchanged relative to the other string.
|
|
25
|
+
*
|
|
26
|
+
* @param oldStr The "before" line (typically the `-` line, without the prefix)
|
|
27
|
+
* @param newStr The "after" line (typically the `+` line, without the prefix)
|
|
28
|
+
* @returns Array of diff tokens
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* wordDiff('const x = 1', 'const y = 2')
|
|
32
|
+
* // → [
|
|
33
|
+
* // { text: 'const ', type: 'equal' },
|
|
34
|
+
* // { text: 'x', type: 'del' },
|
|
35
|
+
* // { text: 'y', type: 'add' },
|
|
36
|
+
* // { text: ' = ', type: 'equal' },
|
|
37
|
+
* // { text: '1', type: 'del' },
|
|
38
|
+
* // { text: '2', type: 'add' },
|
|
39
|
+
* // ]
|
|
40
|
+
*/
|
|
41
|
+
export declare function wordDiff(oldStr: string, newStr: string): DiffToken[];
|
|
42
|
+
/**
|
|
43
|
+
* Check if a diff result has any changes (i.e., at least one add or del token).
|
|
44
|
+
* Used to skip wrapping when the lines are identical.
|
|
45
|
+
*/
|
|
46
|
+
export declare function hasChanges(tokens: DiffToken[]): boolean;
|
|
47
|
+
//# sourceMappingURL=word-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"word-diff.d.ts","sourceRoot":"","sources":["../src/word-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,oFAAoF;AACpF,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;CAC/B;AA2DD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAmCpE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAEvD"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Word-level diff utility for Pattern 5 (selective adoption from expressive-code).
|
|
3
|
+
*
|
|
4
|
+
* Computes a per-word diff between two lines of code using a simple LCS-based
|
|
5
|
+
* algorithm. Used to highlight the specific words that changed within `+`/`-`
|
|
6
|
+
* diff lines, so readers can see exactly what was added/removed rather than
|
|
7
|
+
* just which lines changed.
|
|
8
|
+
*
|
|
9
|
+
* Algorithm: split each line into tokens (words + whitespace + punctuation),
|
|
10
|
+
* compute the LCS (Longest Common Subsequence) between the two token arrays,
|
|
11
|
+
* then walk both arrays emitting add/remove/equal markers.
|
|
12
|
+
*
|
|
13
|
+
* No external dependencies — this is ~80 lines of self-contained code.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Split a code line into tokens for diffing. Each token is either:
|
|
17
|
+
* - a run of whitespace
|
|
18
|
+
* - a run of word characters (alphanumeric + underscore)
|
|
19
|
+
* - a single punctuation character
|
|
20
|
+
*
|
|
21
|
+
* This produces reasonable word-level diffs for most code without being
|
|
22
|
+
* overly granular (character-level) or too coarse (line-level).
|
|
23
|
+
*/
|
|
24
|
+
function tokenize(line) {
|
|
25
|
+
const tokens = [];
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < line.length) {
|
|
28
|
+
const ch = line[i];
|
|
29
|
+
// Whitespace run
|
|
30
|
+
if (/\s/.test(ch)) {
|
|
31
|
+
let j = i + 1;
|
|
32
|
+
while (j < line.length && /\s/.test(line[j]))
|
|
33
|
+
j++;
|
|
34
|
+
tokens.push(line.slice(i, j));
|
|
35
|
+
i = j;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// Word character run (alphanumeric + underscore + dot for method chains)
|
|
39
|
+
if (/[\w.]/.test(ch)) {
|
|
40
|
+
let j = i + 1;
|
|
41
|
+
while (j < line.length && /[\w.]/.test(line[j]))
|
|
42
|
+
j++;
|
|
43
|
+
tokens.push(line.slice(i, j));
|
|
44
|
+
i = j;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// Single punctuation character
|
|
48
|
+
tokens.push(ch);
|
|
49
|
+
i++;
|
|
50
|
+
}
|
|
51
|
+
return tokens;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Compute the LCS table between two token arrays.
|
|
55
|
+
* Returns a 2D array where table[i][j] = length of LCS of a[0..i) and b[0..j).
|
|
56
|
+
*/
|
|
57
|
+
function lcsTable(a, b) {
|
|
58
|
+
const m = a.length;
|
|
59
|
+
const n = b.length;
|
|
60
|
+
const table = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
61
|
+
for (let i = 1; i <= m; i++) {
|
|
62
|
+
for (let j = 1; j <= n; j++) {
|
|
63
|
+
if (a[i - 1] === b[j - 1]) {
|
|
64
|
+
table[i][j] = table[i - 1][j - 1] + 1;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
table[i][j] = Math.max(table[i - 1][j], table[i][j - 1]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return table;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compute a word-level diff between two strings.
|
|
75
|
+
* Returns an array of DiffToken entries; concatenating all `.text` values
|
|
76
|
+
* reconstructs the union of both inputs. The `.type` field indicates whether
|
|
77
|
+
* each token was added, removed, or unchanged relative to the other string.
|
|
78
|
+
*
|
|
79
|
+
* @param oldStr The "before" line (typically the `-` line, without the prefix)
|
|
80
|
+
* @param newStr The "after" line (typically the `+` line, without the prefix)
|
|
81
|
+
* @returns Array of diff tokens
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* wordDiff('const x = 1', 'const y = 2')
|
|
85
|
+
* // → [
|
|
86
|
+
* // { text: 'const ', type: 'equal' },
|
|
87
|
+
* // { text: 'x', type: 'del' },
|
|
88
|
+
* // { text: 'y', type: 'add' },
|
|
89
|
+
* // { text: ' = ', type: 'equal' },
|
|
90
|
+
* // { text: '1', type: 'del' },
|
|
91
|
+
* // { text: '2', type: 'add' },
|
|
92
|
+
* // ]
|
|
93
|
+
*/
|
|
94
|
+
export function wordDiff(oldStr, newStr) {
|
|
95
|
+
const a = tokenize(oldStr);
|
|
96
|
+
const b = tokenize(newStr);
|
|
97
|
+
const table = lcsTable(a, b);
|
|
98
|
+
// Backtrack through the LCS table to emit the diff.
|
|
99
|
+
const result = [];
|
|
100
|
+
let i = a.length;
|
|
101
|
+
let j = b.length;
|
|
102
|
+
while (i > 0 || j > 0) {
|
|
103
|
+
if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) {
|
|
104
|
+
result.push({ text: a[i - 1], type: 'equal' });
|
|
105
|
+
i--;
|
|
106
|
+
j--;
|
|
107
|
+
}
|
|
108
|
+
else if (j > 0 && (i === 0 || table[i][j - 1] >= table[i - 1][j])) {
|
|
109
|
+
result.push({ text: b[j - 1], type: 'add' });
|
|
110
|
+
j--;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
result.push({ text: a[i - 1], type: 'del' });
|
|
114
|
+
i--;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
result.reverse();
|
|
118
|
+
// Merge consecutive tokens of the same type to reduce output size.
|
|
119
|
+
const merged = [];
|
|
120
|
+
for (const token of result) {
|
|
121
|
+
const last = merged[merged.length - 1];
|
|
122
|
+
if (last && last.type === token.type) {
|
|
123
|
+
last.text += token.text;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
merged.push({ ...token });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return merged;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if a diff result has any changes (i.e., at least one add or del token).
|
|
133
|
+
* Used to skip wrapping when the lines are identical.
|
|
134
|
+
*/
|
|
135
|
+
export function hasChanges(tokens) {
|
|
136
|
+
return tokens.some((t) => t.type !== 'equal');
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=word-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"word-diff.js","sourceRoot":"","sources":["../src/word-diff.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dr-ishaan/rehype-perfect-code-blocks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Beautiful, configurable code blocks for Astro / MDX / any rehype pipeline. Built on Shiki, inspired by rehype-pretty-code, VitePress, Docusaurus, and Expressive Code.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "tsc -p tsconfig.json && cp src/styles.css dist/styles.css",
|
|
53
53
|
"dev": "tsc -p tsconfig.json --watch",
|
|
54
|
-
"test": "node test-meta-parser.mjs && node test-dom-structure.mjs && node test-options.mjs && node test-notations.mjs && node test-security.mjs && node test-integration.mjs && node test-regression.mjs && node test-css.mjs && node test-edge-cases.mjs && node stress-tests.mjs && node new-feature-tests.mjs && node test-issue-12.mjs && node test-issue-11.mjs",
|
|
54
|
+
"test": "node test-meta-parser.mjs && node test-dom-structure.mjs && node test-options.mjs && node test-notations.mjs && node test-security.mjs && node test-integration.mjs && node test-regression.mjs && node test-css.mjs && node test-edge-cases.mjs && node stress-tests.mjs && node new-feature-tests.mjs && node test-issue-12.mjs && node test-issue-11.mjs && node test-architecture-patterns.mjs && node test-copy-button-fix.mjs",
|
|
55
55
|
"prepublishOnly": "npm run build && npm test",
|
|
56
56
|
"prepack": "npm run build"
|
|
57
57
|
},
|
package/src/astro.ts
CHANGED
|
@@ -130,13 +130,20 @@ export default function perfectCode(
|
|
|
130
130
|
|
|
131
131
|
// Copy-button script
|
|
132
132
|
if (options.copyButton !== false) {
|
|
133
|
-
|
|
134
|
-
//
|
|
133
|
+
// Graceful degradation: .no-js class MUST be added BEFORE the copy
|
|
134
|
+
// script runs, so the copy script's swapNoJs() can detect and remove
|
|
135
|
+
// it. If we add .no-js AFTER the copy script, swapNoJs() is a no-op
|
|
136
|
+
// (the class isn't there yet), and the MutationObserver (which only
|
|
137
|
+
// watches childList by default) won't catch the attribute change —
|
|
138
|
+
// leaving .no-js on <html> permanently and hiding the copy button
|
|
139
|
+
// via the `html.no-js .pcb__copy { display: none !important; }` rule.
|
|
140
|
+
// See issue: copy button not working in Astro build output.
|
|
135
141
|
if (options.hideCopyWithoutJs !== false) {
|
|
136
142
|
injections.push(
|
|
137
143
|
`<script${nonceAttr}>document.documentElement.classList.add('no-js');</script>`
|
|
138
144
|
);
|
|
139
145
|
}
|
|
146
|
+
injections.push(`<script${nonceAttr}>${COPY_SCRIPT}</script>`);
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
// Manual theme override
|
|
@@ -0,0 +1,214 @@
|
|
|
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
|
+
|
|
15
|
+
/** A color in RGB format (0–255 per channel). */
|
|
16
|
+
export interface RGB {
|
|
17
|
+
r: number;
|
|
18
|
+
g: number;
|
|
19
|
+
b: number;
|
|
20
|
+
a?: number; // 0–1, optional alpha
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Relative luminance per WCAG 2.1: https://www.w3.org/TR/WCAG21/#dfn-relative-luminance */
|
|
24
|
+
function relativeLuminance({ r, g, b }: RGB): number {
|
|
25
|
+
const channel = (c: number): number => {
|
|
26
|
+
const s = c / 255;
|
|
27
|
+
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
28
|
+
};
|
|
29
|
+
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Contrast ratio per WCAG 2.1: https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio (1–21). */
|
|
33
|
+
export function contrastRatio(fg: RGB, bg: RGB): number {
|
|
34
|
+
const l1 = relativeLuminance(fg);
|
|
35
|
+
const l2 = relativeLuminance(bg);
|
|
36
|
+
const lighter = Math.max(l1, l2);
|
|
37
|
+
const darker = Math.min(l1, l2);
|
|
38
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse a hex color (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) or rgb()/rgba() string
|
|
43
|
+
* into an RGB object. Returns null if the input can't be parsed.
|
|
44
|
+
*/
|
|
45
|
+
export function parseColor(input: string | undefined | null): RGB | null {
|
|
46
|
+
if (!input) return null;
|
|
47
|
+
const s = input.trim().toLowerCase();
|
|
48
|
+
// Hex: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
|
|
49
|
+
const hexMatch = s.match(/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/);
|
|
50
|
+
if (hexMatch) {
|
|
51
|
+
let hex = hexMatch[1];
|
|
52
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
53
|
+
hex = hex
|
|
54
|
+
.split('')
|
|
55
|
+
.map((c) => c + c)
|
|
56
|
+
.join('');
|
|
57
|
+
}
|
|
58
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
59
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
60
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
61
|
+
const a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : undefined;
|
|
62
|
+
return { r, g, b, a };
|
|
63
|
+
}
|
|
64
|
+
// rgb() / rgba()
|
|
65
|
+
const rgbMatch = s.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)$/);
|
|
66
|
+
if (rgbMatch) {
|
|
67
|
+
return {
|
|
68
|
+
r: parseInt(rgbMatch[1], 10),
|
|
69
|
+
g: parseInt(rgbMatch[2], 10),
|
|
70
|
+
b: parseInt(rgbMatch[3], 10),
|
|
71
|
+
a: rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) : undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Convert an RGB color back to a hex string (#RRGGBB or #RRGGBBAA). */
|
|
78
|
+
export function toHex({ r, g, b, a }: RGB): string {
|
|
79
|
+
const toHex2 = (n: number): string => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, '0');
|
|
80
|
+
const base = `#${toHex2(r)}${toHex2(g)}${toHex2(b)}`;
|
|
81
|
+
if (a !== undefined && a < 1) return base + toHex2(a * 255);
|
|
82
|
+
return base;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Mix two colors by a ratio (0 = pure a, 1 = pure b). */
|
|
86
|
+
export function mix(a: RGB, b: RGB, ratio: number): RGB {
|
|
87
|
+
const t = Math.max(0, Math.min(1, ratio));
|
|
88
|
+
return {
|
|
89
|
+
r: a.r * (1 - t) + b.r * t,
|
|
90
|
+
g: a.g * (1 - t) + b.g * t,
|
|
91
|
+
b: a.b * (1 - t) + b.b * t,
|
|
92
|
+
a: a.a !== undefined || b.a !== undefined ? (a.a ?? 1) * (1 - t) + (b.a ?? 1) * t : undefined,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Lighten a color toward white by a ratio (0–1). */
|
|
97
|
+
export function lighten(color: RGB, ratio: number): RGB {
|
|
98
|
+
return mix(color, { r: 255, g: 255, b: 255 }, ratio);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Darken a color toward black by a ratio (0–1). */
|
|
102
|
+
export function darken(color: RGB, ratio: number): RGB {
|
|
103
|
+
return mix(color, { r: 0, g: 0, b: 0 }, ratio);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Adjust a foreground color to meet a target contrast ratio against a
|
|
108
|
+
* background. If the contrast is already sufficient, returns the foreground
|
|
109
|
+
* unchanged. Otherwise, lightens (if bg is dark) or darkens (if bg is light)
|
|
110
|
+
* the foreground until the target ratio is met.
|
|
111
|
+
*
|
|
112
|
+
* @param fg Foreground color to adjust
|
|
113
|
+
* @param bg Background color to adjust against
|
|
114
|
+
* @param minRatio Minimum WCAG contrast ratio (default 4.5 = AA for normal text)
|
|
115
|
+
* @param maxRatio Maximum ratio to aim for if adjusting (default 7.0 = AAA)
|
|
116
|
+
* @returns The adjusted foreground color (or the original if already sufficient)
|
|
117
|
+
*/
|
|
118
|
+
export function ensureColorContrastOnBackground(
|
|
119
|
+
fg: RGB,
|
|
120
|
+
bg: RGB,
|
|
121
|
+
minRatio = 4.5,
|
|
122
|
+
maxRatio = 7.0
|
|
123
|
+
): RGB {
|
|
124
|
+
const current = contrastRatio(fg, bg);
|
|
125
|
+
if (current >= minRatio) return fg;
|
|
126
|
+
|
|
127
|
+
// Decide direction: if bg is dark (luminance < 0.5), lighten fg; else darken.
|
|
128
|
+
const bgLum = relativeLuminance(bg);
|
|
129
|
+
const direction = bgLum < 0.5 ? 'lighten' : 'darken';
|
|
130
|
+
// Binary search between current and pure white/black for the target ratio.
|
|
131
|
+
let lo = 0;
|
|
132
|
+
let hi = 1;
|
|
133
|
+
let best = fg;
|
|
134
|
+
for (let i = 0; i < 16; i++) {
|
|
135
|
+
const mid = (lo + hi) / 2;
|
|
136
|
+
const candidate = direction === 'lighten' ? lighten(fg, mid) : darken(fg, mid);
|
|
137
|
+
const ratio = contrastRatio(candidate, bg);
|
|
138
|
+
if (ratio >= minRatio && ratio <= maxRatio) {
|
|
139
|
+
return candidate;
|
|
140
|
+
}
|
|
141
|
+
if (ratio < minRatio) {
|
|
142
|
+
lo = mid;
|
|
143
|
+
} else {
|
|
144
|
+
best = candidate;
|
|
145
|
+
hi = mid;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return best;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Extract the background and foreground colors from a Shiki theme object.
|
|
153
|
+
* Shiki themes have a `bg` and `fg` property at the top level (hex strings).
|
|
154
|
+
* Returns nulls if the theme shape doesn't match.
|
|
155
|
+
*/
|
|
156
|
+
export function extractThemeColors(theme: unknown): { bg: RGB | null; fg: RGB | null } {
|
|
157
|
+
const t = theme as { bg?: string; fg?: string; name?: string };
|
|
158
|
+
return {
|
|
159
|
+
bg: parseColor(t?.bg),
|
|
160
|
+
fg: parseColor(t?.fg),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Compute theme-aware `--pcb-*` defaults for a code block based on the
|
|
166
|
+
* loaded Shiki theme. These are applied as inline styles on the `<figure>`
|
|
167
|
+
* element, so the static `dist/styles.css` can ship its own defaults while
|
|
168
|
+
* the runtime overrides them with theme-aware values.
|
|
169
|
+
*
|
|
170
|
+
* Currently computes:
|
|
171
|
+
* - --pcb-bg: theme background (or 'inherit' if unknown)
|
|
172
|
+
* - --pcb-fg: theme foreground (or 'inherit' if unknown)
|
|
173
|
+
* - --pcb-line-numbers-fg: theme fg, contrast-adjusted against theme bg
|
|
174
|
+
* - --pcb-line-highlight-bg: theme fg at ~12% alpha (subtle highlight)
|
|
175
|
+
* - --pcb-line-diff-add-bg: green at ~18% alpha
|
|
176
|
+
* - --pcb-line-diff-del-bg: red at ~18% alpha
|
|
177
|
+
* - --pcb-line-focus-bg: theme fg at ~6% alpha (subtle dim)
|
|
178
|
+
*
|
|
179
|
+
* @param theme The Shiki theme object (must have `bg` and `fg` hex strings)
|
|
180
|
+
* @returns A CSS style string (e.g. `--pcb-bg:#fff;--pcb-fg:#000;...`) or empty string
|
|
181
|
+
*/
|
|
182
|
+
export function computeThemeAwareDefaults(theme: unknown): string {
|
|
183
|
+
const { bg, fg } = extractThemeColors(theme);
|
|
184
|
+
if (!bg || !fg) return '';
|
|
185
|
+
|
|
186
|
+
const parts: string[] = [];
|
|
187
|
+
parts.push(`--pcb-bg:${toHex(bg)}`);
|
|
188
|
+
parts.push(`--pcb-fg:${toHex(fg)}`);
|
|
189
|
+
|
|
190
|
+
// Line numbers: use fg, but adjust contrast to >= 3.0 (WCAG AA for large text)
|
|
191
|
+
// against the background. Shiki themes often have low-contrast line-number
|
|
192
|
+
// colors baked in; we override them with a guaranteed-legible value.
|
|
193
|
+
const lineNumFg = ensureColorContrastOnBackground(fg, bg, 3.0, 4.5);
|
|
194
|
+
parts.push(`--pcb-ln-fg:${toHex(lineNumFg)}`);
|
|
195
|
+
|
|
196
|
+
// Line highlight background: subtle tint of the foreground at ~12% alpha.
|
|
197
|
+
// Use mix() with the background to get a slightly-lighter/darker shade.
|
|
198
|
+
const hlBg = mix(bg, fg, 0.12);
|
|
199
|
+
parts.push(`--pcb-line-highlight-bg:${toHex(hlBg)}`);
|
|
200
|
+
|
|
201
|
+
// Diff add: green (#22863a in github) at 18% alpha over bg.
|
|
202
|
+
const diffAdd = mix(bg, { r: 34, g: 134, b: 58 }, 0.18);
|
|
203
|
+
parts.push(`--pcb-line-add-bg:${toHex(diffAdd)}`);
|
|
204
|
+
|
|
205
|
+
// Diff del: red (#cb2431 in github) at 18% alpha over bg.
|
|
206
|
+
const diffDel = mix(bg, { r: 203, g: 36, b: 49 }, 0.18);
|
|
207
|
+
parts.push(`--pcb-line-del-bg:${toHex(diffDel)}`);
|
|
208
|
+
|
|
209
|
+
// Focus background: dim the non-focused lines by mixing bg with fg at low alpha.
|
|
210
|
+
const focusBg = mix(bg, fg, 0.04);
|
|
211
|
+
parts.push(`--pcb-line-focus-bg:${toHex(focusBg)}`);
|
|
212
|
+
|
|
213
|
+
return parts.join(';');
|
|
214
|
+
}
|
package/src/copy-script.ts
CHANGED
|
@@ -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`). ~
|
|
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
|
-
|
|
24
|
-
document.documentElement.classList.
|
|
25
|
-
|
|
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 =
|
|
30
|
-
|
|
31
|
-
liveRegion
|
|
32
|
-
liveRegion
|
|
33
|
-
liveRegion
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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 () {
|
|
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,5 +143,77 @@ export const COPY_SCRIPT = `
|
|
|
123
143
|
document.body.removeChild(ta);
|
|
124
144
|
}
|
|
125
145
|
});
|
|
146
|
+
|
|
147
|
+
// Pattern 4: MutationObserver for SPA support + .no-js race fix.
|
|
148
|
+
// Watches for:
|
|
149
|
+
// - New code blocks added to the DOM (React/Vue re-render, Astro view
|
|
150
|
+
// transitions, Turbolinks) → re-apply .no-js → .js swap + ensure
|
|
151
|
+
// aria-live region.
|
|
152
|
+
// - Attribute changes on <html> (specifically the class attribute) →
|
|
153
|
+
// catches the case where a later script adds .no-js AFTER this script
|
|
154
|
+
// ran (a race condition that previously left copy buttons hidden).
|
|
155
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
156
|
+
var pendingObserve = false;
|
|
157
|
+
var observer = new MutationObserver(function (mutations) {
|
|
158
|
+
// Batch checks with microtask to avoid layout thrash.
|
|
159
|
+
if (pendingObserve) return;
|
|
160
|
+
pendingObserve = true;
|
|
161
|
+
Promise.resolve().then(function () {
|
|
162
|
+
pendingObserve = false;
|
|
163
|
+
var needsSwap = false;
|
|
164
|
+
for (var i = 0; i < mutations.length; i++) {
|
|
165
|
+
var m = mutations[i];
|
|
166
|
+
// childList: new nodes added (SPA navigation, view transitions)
|
|
167
|
+
if (m.type === 'childList' && m.addedNodes && m.addedNodes.length) {
|
|
168
|
+
needsSwap = true;
|
|
169
|
+
}
|
|
170
|
+
// attributes: class changed on <html> (the .no-js race fix)
|
|
171
|
+
if (m.type === 'attributes' && m.attributeName === 'class') {
|
|
172
|
+
needsSwap = true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (needsSwap) {
|
|
176
|
+
swapNoJs();
|
|
177
|
+
ensureLiveRegion();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
// Observe documentElement for BOTH childList (new nodes) AND attribute
|
|
182
|
+
// changes (class attribute — catches .no-js being added by a later script).
|
|
183
|
+
observer.observe(document.documentElement, {
|
|
184
|
+
childList: true,
|
|
185
|
+
subtree: true,
|
|
186
|
+
attributes: true,
|
|
187
|
+
attributeFilter: ['class'],
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Defensive: re-run swapNoJs() on DOMContentLoaded and window.load.
|
|
192
|
+
// Belt + suspenders for the .no-js race — if a framework (Astro, Next.js,
|
|
193
|
+
// etc.) adds .no-js in a way the MutationObserver doesn't catch (e.g.,
|
|
194
|
+
// before the observer is set up, or in a different document context),
|
|
195
|
+
// these event handlers will catch it.
|
|
196
|
+
if (typeof document.addEventListener === 'function') {
|
|
197
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
198
|
+
swapNoJs();
|
|
199
|
+
ensureLiveRegion();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
if (typeof window.addEventListener === 'function') {
|
|
203
|
+
window.addEventListener('load', function () {
|
|
204
|
+
swapNoJs();
|
|
205
|
+
ensureLiveRegion();
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Pattern 4: astro:page-load event listener for Astro view transitions.
|
|
210
|
+
// Astro emits this event after a view transition completes; the new page's
|
|
211
|
+
// DOM may have replaced the old, so re-apply the .no-js → .js swap.
|
|
212
|
+
if (typeof document.addEventListener === 'function') {
|
|
213
|
+
document.addEventListener('astro:page-load', function () {
|
|
214
|
+
swapNoJs();
|
|
215
|
+
ensureLiveRegion();
|
|
216
|
+
});
|
|
217
|
+
}
|
|
126
218
|
})();
|
|
127
219
|
`;
|