@dr-ishaan/remake-blocks 1.9.0 → 2.0.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 +153 -0
- package/dist/accordion.js +57 -5
- package/dist/css-generator.d.ts.map +1 -1
- package/dist/css-generator.js +21 -1
- package/dist/css-generator.js.map +1 -1
- package/dist/dx.d.ts +139 -0
- package/dist/dx.d.ts.map +1 -0
- package/dist/dx.js +230 -0
- package/dist/dx.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/remark-remake-blocks.d.ts.map +1 -1
- package/dist/remark-remake-blocks.js +130 -16
- package/dist/remark-remake-blocks.js.map +1 -1
- package/dist/types.d.ts +126 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -3
package/dist/dx.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v1.10.0: Developer Experience utilities.
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* - CLASSES: frozen object of CSS class name constants for programmatic use
|
|
6
|
+
* - warn(message, ...args): dev-mode warning emitter (gated on devWarnings option)
|
|
7
|
+
* - validateCustomCallouts(configs, strict): validates customCallouts at init time
|
|
8
|
+
* - suggestSimilarType(type, knownTypes): Levenshtein-based "did you mean" suggester
|
|
9
|
+
*
|
|
10
|
+
* @module dx
|
|
11
|
+
*/
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// CLASSES — exported CSS class name constants
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* v1.10.0+: Frozen object of CSS class names used by the plugin. Use these
|
|
17
|
+
* in CSS-in-JS, tests, or any code that needs to reference callout classes
|
|
18
|
+
* programmatically without hardcoding strings.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { CLASSES } from '@dr-ishaan/remake-blocks';
|
|
23
|
+
*
|
|
24
|
+
* document.querySelector(`.${CLASSES.CALLOUT_NOTE}`) // '.callout-note'
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const CLASSES = Object.freeze({
|
|
28
|
+
// Container classes
|
|
29
|
+
CALLOUT: "callout",
|
|
30
|
+
CALLOUT_NOTE: "callout-note",
|
|
31
|
+
CALLOUT_TIP: "callout-tip",
|
|
32
|
+
CALLOUT_IMPORTANT: "callout-important",
|
|
33
|
+
CALLOUT_WARNING: "callout-warning",
|
|
34
|
+
CALLOUT_CAUTION: "callout-caution",
|
|
35
|
+
CALLOUT_ABSTRACT: "callout-abstract",
|
|
36
|
+
CALLOUT_INFO: "callout-info",
|
|
37
|
+
CALLOUT_SUCCESS: "callout-success",
|
|
38
|
+
CALLOUT_QUESTION: "callout-question",
|
|
39
|
+
CALLOUT_FAILURE: "callout-failure",
|
|
40
|
+
CALLOUT_DANGER: "callout-danger",
|
|
41
|
+
CALLOUT_QUOTE: "callout-quote",
|
|
42
|
+
CALLOUT_BUG: "callout-bug",
|
|
43
|
+
CALLOUT_EXAMPLE: "callout-example",
|
|
44
|
+
CALLOUT_TODO: "callout-todo",
|
|
45
|
+
CALLOUT_SUMMARY: "callout-summary",
|
|
46
|
+
CALLOUT_TLDR: "callout-tldr",
|
|
47
|
+
CALLOUT_HINT: "callout-hint",
|
|
48
|
+
CALLOUT_CHECK: "callout-check",
|
|
49
|
+
CALLOUT_DONE: "callout-done",
|
|
50
|
+
CALLOUT_HELP: "callout-help",
|
|
51
|
+
CALLOUT_FAQ: "callout-faq",
|
|
52
|
+
CALLOUT_ATTENTION: "callout-attention",
|
|
53
|
+
CALLOUT_FAIL: "callout-fail",
|
|
54
|
+
CALLOUT_MISSING: "callout-missing",
|
|
55
|
+
CALLOUT_ERROR: "callout-error",
|
|
56
|
+
CALLOUT_CITE: "callout-cite",
|
|
57
|
+
// v1.6.0 types
|
|
58
|
+
CALLOUT_DEFINITION: "callout-definition",
|
|
59
|
+
CALLOUT_ASIDE: "callout-aside",
|
|
60
|
+
CALLOUT_CORRECTION: "callout-correction",
|
|
61
|
+
CALLOUT_UPDATE: "callout-update",
|
|
62
|
+
CALLOUT_FIGURE: "callout-figure",
|
|
63
|
+
CALLOUT_FURTHER_READING: "callout-further-reading",
|
|
64
|
+
CALLOUT_PREREQUISITE: "callout-prerequisite",
|
|
65
|
+
CALLOUT_EXERCISE: "callout-exercise",
|
|
66
|
+
CALLOUT_SIDENOTE: "callout-sidenote",
|
|
67
|
+
CALLOUT_TIMELINE: "callout-timeline",
|
|
68
|
+
CALLOUT_ANNOUNCEMENT: "callout-announcement",
|
|
69
|
+
CALLOUT_BIBLIOGRAPHY: "callout-bibliography",
|
|
70
|
+
CALLOUT_DRAFT: "callout-draft",
|
|
71
|
+
CALLOUT_TRANSLATION: "callout-translation",
|
|
72
|
+
CALLOUT_DISCUSSION: "callout-discussion",
|
|
73
|
+
CALLOUT_RETRO: "callout-retro",
|
|
74
|
+
// Sub-element classes
|
|
75
|
+
CALLOUT_TITLE: "callout-title",
|
|
76
|
+
CALLOUT_TITLE_TEXT: "callout-title-text",
|
|
77
|
+
CALLOUT_ICON: "callout-icon",
|
|
78
|
+
CALLOUT_BODY: "callout-body",
|
|
79
|
+
CALLOUT_COLLAPSIBLE: "collapsible",
|
|
80
|
+
// Disclosure / accordion
|
|
81
|
+
DISCLOSURE: "disclosure",
|
|
82
|
+
DISCLOSURE_TITLE: "disclosure-title",
|
|
83
|
+
DISCLOSURE_BODY: "disclosure-body",
|
|
84
|
+
DISCLOSURE_ACCORDION: "disclosure-accordion",
|
|
85
|
+
DISCLOSURE_TREE: "disclosure-tree",
|
|
86
|
+
// Pull quote / epigraph
|
|
87
|
+
PULL_QUOTE: "pull-quote",
|
|
88
|
+
PULL_QUOTE_TEXT: "pull-quote-text",
|
|
89
|
+
PULL_QUOTE_ATTRIBUTION: "pull-quote-attribution",
|
|
90
|
+
EPIGRAPH: "epigraph",
|
|
91
|
+
EPIGRAPH_TEXT: "epigraph-text",
|
|
92
|
+
EPIGRAPH_ATTRIBUTION: "epigraph-attribution",
|
|
93
|
+
// Blockquote enhancement
|
|
94
|
+
BLOCKQUOTE_ENHANCED: "blockquote-enhanced",
|
|
95
|
+
});
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// warn — dev-mode warning emitter
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* v1.10.0+: Emit a developer-mode warning via `console.warn`.
|
|
101
|
+
*
|
|
102
|
+
* The warning is prefixed with `[remake-blocks]` for easy identification
|
|
103
|
+
* and filtering. No-op when `devWarnings` is false OR when running in
|
|
104
|
+
* production (`process.env.NODE_ENV === 'production'`).
|
|
105
|
+
*
|
|
106
|
+
* @param enabled — whether devWarnings is enabled (from plugin options)
|
|
107
|
+
* @param message — warning message (without prefix)
|
|
108
|
+
* @param args — additional context args
|
|
109
|
+
*/
|
|
110
|
+
export function warn(enabled, message, ...args) {
|
|
111
|
+
if (!enabled)
|
|
112
|
+
return;
|
|
113
|
+
if (typeof process !== "undefined" && process.env && process.env.NODE_ENV === "production" && enabled !== true) {
|
|
114
|
+
// Auto-disable in production unless explicitly enabled
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.warn(`[remake-blocks] ${message}`, ...args);
|
|
118
|
+
}
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// suggestSimilarType — Levenshtein-based "did you mean" suggester
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
/**
|
|
123
|
+
* Compute Levenshtein edit distance between two strings.
|
|
124
|
+
* Used by `suggestSimilarType` to find the closest match.
|
|
125
|
+
*/
|
|
126
|
+
function levenshtein(a, b) {
|
|
127
|
+
const m = a.length;
|
|
128
|
+
const n = b.length;
|
|
129
|
+
if (m === 0)
|
|
130
|
+
return n;
|
|
131
|
+
if (n === 0)
|
|
132
|
+
return m;
|
|
133
|
+
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
134
|
+
for (let i = 0; i <= m; i++)
|
|
135
|
+
dp[i][0] = i;
|
|
136
|
+
for (let j = 0; j <= n; j++)
|
|
137
|
+
dp[0][j] = j;
|
|
138
|
+
for (let i = 1; i <= m; i++) {
|
|
139
|
+
for (let j = 1; j <= n; j++) {
|
|
140
|
+
const cost = a[i - 1].toLowerCase() === b[j - 1].toLowerCase() ? 0 : 1;
|
|
141
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, // deletion
|
|
142
|
+
dp[i][j - 1] + 1, // insertion
|
|
143
|
+
dp[i - 1][j - 1] + cost);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return dp[m][n];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Find the closest matching type from a list of known types.
|
|
150
|
+
* Returns the best match if its edit distance is ≤ 3 (reasonable threshold
|
|
151
|
+
* for typo detection), or null if no close match exists.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* suggestSimilarType('TYPO', ['note', 'tip', 'warning']) // → 'tip'
|
|
156
|
+
* suggestSimilarType('XYZ', ['note', 'tip']) // → null
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function suggestSimilarType(input, knownTypes) {
|
|
160
|
+
let bestMatch = null;
|
|
161
|
+
let bestDistance = Infinity;
|
|
162
|
+
for (const candidate of knownTypes) {
|
|
163
|
+
const dist = levenshtein(input, candidate);
|
|
164
|
+
if (dist < bestDistance) {
|
|
165
|
+
bestDistance = dist;
|
|
166
|
+
bestMatch = candidate;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Only suggest if the edit distance is reasonable (≤ 3 edits, or ≤ half
|
|
170
|
+
// the input length for longer type names like 'further-reading').
|
|
171
|
+
const threshold = Math.max(3, Math.floor(input.length / 2));
|
|
172
|
+
return bestDistance <= threshold ? bestMatch : null;
|
|
173
|
+
}
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// validateCustomCallouts — strict config validation
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
const REQUIRED_CALLOUT_FIELDS = ["type", "icon", "className", "defaultTitle", "color", "backgroundColor"];
|
|
178
|
+
/**
|
|
179
|
+
* v1.10.0+: Validate `customCallouts` config entries.
|
|
180
|
+
*
|
|
181
|
+
* When `strict` is true, throws a descriptive Error for the first config
|
|
182
|
+
* entry that's missing a required field. When `strict` is false, returns
|
|
183
|
+
* an array of validation errors without throwing (caller decides what to do).
|
|
184
|
+
*
|
|
185
|
+
* Required fields per CalloutConfig:
|
|
186
|
+
* - type (non-empty string)
|
|
187
|
+
* - icon (string, may be empty for icon-less callouts)
|
|
188
|
+
* - className (non-empty string, used for CSS targeting)
|
|
189
|
+
* - defaultTitle (non-empty string)
|
|
190
|
+
* - color (valid CSS color)
|
|
191
|
+
* - backgroundColor (valid CSS color)
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* validateCustomCallouts([
|
|
196
|
+
* { type: 'x', icon: '🔥', className: 'callout-x', defaultTitle: 'X', color: '#f00', backgroundColor: '#fee' }
|
|
197
|
+
* ], true); // OK — no throw
|
|
198
|
+
*
|
|
199
|
+
* validateCustomCallouts([
|
|
200
|
+
* { type: 'x', icon: '🔥' /* missing className, defaultTitle, color, backgroundColor *\/ }
|
|
201
|
+
* ], true); // throws: "Custom callout config[0] ('x') is missing required fields: className, defaultTitle, color, backgroundColor"
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export function validateCustomCallouts(configs, strict) {
|
|
205
|
+
const errors = [];
|
|
206
|
+
for (let i = 0; i < configs.length; i++) {
|
|
207
|
+
const c = configs[i];
|
|
208
|
+
if (!c || typeof c !== "object") {
|
|
209
|
+
errors.push(`Custom callout config[${i}] is not an object`);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const missing = [];
|
|
213
|
+
for (const field of REQUIRED_CALLOUT_FIELDS) {
|
|
214
|
+
const val = c[field];
|
|
215
|
+
if (val === undefined || val === null || (typeof val === "string" && val.trim() === "" && field !== "icon")) {
|
|
216
|
+
missing.push(field);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (missing.length > 0) {
|
|
220
|
+
const name = typeof c.type === "string" ? `'${c.type}'` : `at index ${i}`;
|
|
221
|
+
const msg = `Custom callout config[${i}] (${name}) is missing required fields: ${missing.join(", ")}`;
|
|
222
|
+
errors.push(msg);
|
|
223
|
+
if (strict) {
|
|
224
|
+
throw new Error(`[remake-blocks] ${msg}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return errors;
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=dx.js.map
|
package/dist/dx.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dx.js","sourceRoot":"","sources":["../src/dx.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
export { remarkRemakeBlocks, remarkCalloutBlocks, BUILTIN_CALLOUTS } from "./remark-remake-blocks.js";
|
|
10
10
|
export { escapeHtml, escapeAttribute, sanitizeColor } from "./remark-remake-blocks.js";
|
|
11
11
|
export { generateCss, generateMinimalThemeCss } from "./css-generator.js";
|
|
12
|
+
export { CLASSES, validateCustomCallouts, suggestSimilarType } from "./dx.js";
|
|
13
|
+
export type { CalloutClassName } from "./dx.js";
|
|
12
14
|
export { default } from "./astro.js";
|
|
13
|
-
export type { RemakeBlocksOptions, AstroRemakeBlocksOptions, CalloutConfig, CalloutType, BuiltinCalloutType, ParsedCallout, CustomCalloutType, CalloutConfigMap, } from "./types.js";
|
|
15
|
+
export type { RemakeBlocksOptions, AstroRemakeBlocksOptions, CalloutConfig, CalloutType, BuiltinCalloutType, LucideIconName, ParsedCallout, CustomCalloutType, CalloutConfigMap, } from "./types.js";
|
|
14
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAEtG,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAIvF,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAG1E,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGrC,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAEtG,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAIvF,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAG1E,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC9E,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGrC,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,8 @@ export { escapeHtml, escapeAttribute, sanitizeColor } from "./remark-remake-bloc
|
|
|
13
13
|
// v1.7.0+: CSS generator (for advanced usage outside Astro)
|
|
14
14
|
// v1.9.0+: also exports generateMinimalThemeCss for tree-shaken theme CSS
|
|
15
15
|
export { generateCss, generateMinimalThemeCss } from "./css-generator.js";
|
|
16
|
+
// v1.10.0+: DX utilities — CSS class constants, validation, "did you mean" suggester
|
|
17
|
+
export { CLASSES, validateCustomCallouts, suggestSimilarType } from "./dx.js";
|
|
16
18
|
// Astro integration (default export, re-exported for backward compatibility)
|
|
17
19
|
export { default } from "./astro.js";
|
|
18
20
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAqB;AACrB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACtG,kEAAkE;AAClE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEvF,4DAA4D;AAC5D,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE1E,6EAA6E;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAqB;AACrB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACtG,kEAAkE;AAClE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEvF,4DAA4D;AAC5D,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE1E,qFAAqF;AACrF,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAG9E,6EAA6E;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remark-remake-blocks.d.ts","sourceRoot":"","sources":["../src/remark-remake-blocks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAqC,MAAM,OAAO,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EAId,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"remark-remake-blocks.d.ts","sourceRoot":"","sources":["../src/remark-remake-blocks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAqC,MAAM,OAAO,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EAId,MAAM,YAAY,CAAC;AAQpB,QAAA,MAAM,gBAAgB,EAAE,aAAa,EAyQpC,CAAC;AA0OF,iBAAS,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE5C;AAUD,iBAAS,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAW9D;AA6TD,iBAAS,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOvC;AA89BD,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC,EAAE,IAAI,CAgMnE,CAAC;AA2GF,eAAO,MAAM,mBAAmB,oDAAqB,CAAC;AAItD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* `allowDangerousHtml: true` in plugin options to disable escaping —
|
|
15
15
|
* only do this if you fully trust your markdown source.
|
|
16
16
|
*/
|
|
17
|
+
import { visit } from "unist-util-visit";
|
|
18
|
+
// v1.10.0: DX utilities — warnings, validation, "did you mean" suggester
|
|
19
|
+
import { warn, validateCustomCallouts, suggestSimilarType } from "./dx.js";
|
|
17
20
|
// ---------------------------------------------------------------------------
|
|
18
21
|
// 27 Built-in callout configurations
|
|
19
22
|
// ---------------------------------------------------------------------------
|
|
@@ -446,6 +449,13 @@ const DEFAULT_OPTIONS = {
|
|
|
446
449
|
// v1.9.0 defaults — all opt-in
|
|
447
450
|
icons: undefined,
|
|
448
451
|
cssMinimalTheme: false,
|
|
452
|
+
// v1.10.0 defaults — devWarnings auto-resolves (undefined = auto), strict off
|
|
453
|
+
devWarnings: undefined,
|
|
454
|
+
strictConfigValidation: false,
|
|
455
|
+
// v2.0.0 defaults — roles off (all use "note"), srIconText off, ariaAccordion on
|
|
456
|
+
roles: undefined,
|
|
457
|
+
srIconText: false,
|
|
458
|
+
ariaAccordion: true,
|
|
449
459
|
};
|
|
450
460
|
// ---------------------------------------------------------------------------
|
|
451
461
|
// Helper: Validate + normalize a single custom callout config.
|
|
@@ -693,7 +703,7 @@ function parseDirectiveAttrs(attrsBlock) {
|
|
|
693
703
|
// ---------------------------------------------------------------------------
|
|
694
704
|
// Helper: Parse the first paragraph of a blockquote to detect a callout
|
|
695
705
|
// ---------------------------------------------------------------------------
|
|
696
|
-
function parseCalloutDirective(blockquote, calloutPattern, configMap, enableDisclosures, defaultCollapse) {
|
|
706
|
+
function parseCalloutDirective(blockquote, calloutPattern, configMap, enableDisclosures, defaultCollapse, options) {
|
|
697
707
|
const firstChild = blockquote.children[0];
|
|
698
708
|
if (!firstChild || firstChild.type !== "paragraph")
|
|
699
709
|
return null;
|
|
@@ -708,8 +718,19 @@ function parseCalloutDirective(blockquote, calloutPattern, configMap, enableDisc
|
|
|
708
718
|
const lowerType = rawType.toLowerCase();
|
|
709
719
|
const isPullQuote = lowerType === "pull";
|
|
710
720
|
const isEpigraph = lowerType === "epigraph";
|
|
711
|
-
if (!isDisclosure && !isPullQuote && !isEpigraph && !configMap.has(lowerType))
|
|
721
|
+
if (!isDisclosure && !isPullQuote && !isEpigraph && !configMap.has(lowerType)) {
|
|
722
|
+
// v1.10.0: dev warning for unknown callout types with "did you mean" suggestion
|
|
723
|
+
const devWarn = options?._devWarnings;
|
|
724
|
+
if (devWarn) {
|
|
725
|
+
const known = options?._knownTypes;
|
|
726
|
+
const suggestion = known && known.length > 0 ? suggestSimilarType(rawType, known) : null;
|
|
727
|
+
const msg = suggestion
|
|
728
|
+
? `Unknown callout type '${rawType}'. Did you mean '${suggestion}'?`
|
|
729
|
+
: `Unknown callout type '${rawType}'. It will render as a plain blockquote.`;
|
|
730
|
+
warn(true, msg);
|
|
731
|
+
}
|
|
712
732
|
return null;
|
|
733
|
+
}
|
|
713
734
|
const type = isDisclosure ? "disclosure"
|
|
714
735
|
: isPullQuote ? "pull"
|
|
715
736
|
: isEpigraph ? "epigraph"
|
|
@@ -927,7 +948,11 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
927
948
|
// Sanitize colors before interpolating into style attribute
|
|
928
949
|
const safeIconColor = sanitizeColor(config.iconColor || config.color, "#57606a");
|
|
929
950
|
// v1.2.0: WCAG fix — use role="note" for ALL callouts (was: role="alert" for warnings).
|
|
930
|
-
|
|
951
|
+
// v2.0.0: per-type role override via `roles` option.
|
|
952
|
+
const roleOverride = options.roles?.[parsed.type];
|
|
953
|
+
const ariaRole = roleOverride === "none" ? "" : (roleOverride ?? "note");
|
|
954
|
+
// v2.0.0: when ariaRole is empty (role="none"), omit the attribute entirely.
|
|
955
|
+
const roleAttr = ariaRole ? ` role="${ariaRole}"` : "";
|
|
931
956
|
// Escape className + calloutClass to prevent attribute breakout
|
|
932
957
|
const safeCalloutClass = escapeAttribute(options.calloutClass);
|
|
933
958
|
const safeConfigClassName = escapeAttribute(config.className);
|
|
@@ -1005,8 +1030,12 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
1005
1030
|
: "";
|
|
1006
1031
|
// v1.2.0: title-text always rendered (unless appearance="hidden")
|
|
1007
1032
|
const renderTitle = appearance !== "hidden";
|
|
1008
|
-
|
|
1009
|
-
|
|
1033
|
+
// v2.0.0: sr-only text — prepended to title for screen readers when
|
|
1034
|
+
// srIconText is enabled. Uses the callout's default title (or custom title)
|
|
1035
|
+
// so screen readers announce "Warning:" even when the visual title is
|
|
1036
|
+
// just an icon or when appearance="minimal" hides the text.
|
|
1037
|
+
const srOnlyHtml = options.srIconText && renderTitle
|
|
1038
|
+
? `<span class="sr-only">${escapeHtml(title)}:</span>`
|
|
1010
1039
|
: "";
|
|
1011
1040
|
// v1.2.0: tags option (override element names)
|
|
1012
1041
|
const tags = options.tags ?? {};
|
|
@@ -1021,10 +1050,12 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
1021
1050
|
? `<${iconTag} class="callout-icon" style="color:${safeIconColor}" aria-hidden="true">${effectiveIconHtml}</${iconTag}>`
|
|
1022
1051
|
: "";
|
|
1023
1052
|
// Build the title block (or skip for "hidden" appearance)
|
|
1053
|
+
// v2.0.0: prepend srOnlyHtml (when srIconText is on) before the visible title text.
|
|
1024
1054
|
const titleBlock = renderTitle
|
|
1025
1055
|
? [
|
|
1026
1056
|
` <${titleTag} class="${safeCalloutTitleClass}" style="color:${safeIconColor}" id="${titleId}">`,
|
|
1027
1057
|
iconSpan && ` ${iconSpan}`,
|
|
1058
|
+
srOnlyHtml && ` ${srOnlyHtml}`,
|
|
1028
1059
|
` <${titleTextTag} class="callout-title-text">${escapeHtml(title)}</${titleTextTag}>`,
|
|
1029
1060
|
` </${titleTag}>`,
|
|
1030
1061
|
].filter(Boolean).join("\n")
|
|
@@ -1040,6 +1071,7 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
1040
1071
|
? [
|
|
1041
1072
|
` <${titleTag} class="${safeCalloutTitleClass}" style="color:${safeIconColor}" id="${titleId}"${titleDirAuto}>`,
|
|
1042
1073
|
iconSpan && ` ${iconSpan}`,
|
|
1074
|
+
srOnlyHtml && ` ${srOnlyHtml}`,
|
|
1043
1075
|
` <${titleTextTag} class="callout-title-text">${escapeHtml(title)}</${titleTextTag}>`,
|
|
1044
1076
|
` </${titleTag}>`,
|
|
1045
1077
|
].filter(Boolean).join("\n")
|
|
@@ -1047,7 +1079,7 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
1047
1079
|
if (parsed.collapsible) {
|
|
1048
1080
|
const openAttr = parsed.collapsibleOpen ? " open" : "";
|
|
1049
1081
|
return html([
|
|
1050
|
-
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName} collapsible${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}
|
|
1082
|
+
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName} collapsible${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}${roleAttr}${labelledby}${dirAuto}${openAttr}${directiveId}${extraAttrs}${directiveAttrs}>`,
|
|
1051
1083
|
titleBlockWithDir,
|
|
1052
1084
|
` <${bodyTag} class="${safeCalloutBodyClass}">`,
|
|
1053
1085
|
bodyHtml,
|
|
@@ -1056,7 +1088,7 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
1056
1088
|
].filter(Boolean).join("\n"));
|
|
1057
1089
|
}
|
|
1058
1090
|
return html([
|
|
1059
|
-
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName}${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}
|
|
1091
|
+
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName}${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}${roleAttr}${labelledby}${dirAuto}${directiveId}${extraAttrs}${directiveAttrs}>`,
|
|
1060
1092
|
titleBlockWithDir,
|
|
1061
1093
|
` <${bodyTag} class="${safeCalloutBodyClass}">`,
|
|
1062
1094
|
bodyHtml,
|
|
@@ -1548,7 +1580,10 @@ function buildCalloutFromParts(parsed, config, bodyHtml, options) {
|
|
|
1548
1580
|
const dataAttr = options.dataCalloutType ? ` data-callout-type="${escapeAttribute(parsed.type)}"` : "";
|
|
1549
1581
|
const collapsibleDataAttr = ` data-collapsible="${parsed.collapsible ? "true" : "false"}"`;
|
|
1550
1582
|
const safeIconColor = sanitizeColor(config.iconColor || config.color, "#57606a");
|
|
1551
|
-
|
|
1583
|
+
// v2.0.0: per-type role override via `roles` option (same logic as buildCalloutHtml).
|
|
1584
|
+
const roleOverride = options.roles?.[parsed.type];
|
|
1585
|
+
const ariaRole = roleOverride === "none" ? "" : (roleOverride ?? "note");
|
|
1586
|
+
const roleAttr = ariaRole ? ` role="${ariaRole}"` : "";
|
|
1552
1587
|
const safeCalloutClass = escapeAttribute(options.calloutClass);
|
|
1553
1588
|
const safeConfigClassName = escapeAttribute(config.className);
|
|
1554
1589
|
const safeCalloutTitleClass = escapeAttribute(options.calloutTitleClass);
|
|
@@ -1588,10 +1623,15 @@ function buildCalloutFromParts(parsed, config, bodyHtml, options) {
|
|
|
1588
1623
|
const labelledby = renderTitle ? ` aria-labelledby="${titleId}"` : "";
|
|
1589
1624
|
const appearanceClass = appearance !== "default" ? ` callout-${appearance}` : "";
|
|
1590
1625
|
const inlineClass = parsed.overrides?.inline ? ` callout-${parsed.overrides.inline}` : "";
|
|
1626
|
+
// v2.0.0: sr-only text (same as buildCalloutHtml)
|
|
1627
|
+
const srOnlyHtml = options.srIconText && renderTitle
|
|
1628
|
+
? `<span class="sr-only">${escapeHtml(title)}:</span>`
|
|
1629
|
+
: "";
|
|
1591
1630
|
const titleBlock = renderTitle
|
|
1592
1631
|
? [
|
|
1593
1632
|
` <div class="${safeCalloutTitleClass}" style="color:${safeIconColor}" id="${titleId}" dir="auto">`,
|
|
1594
1633
|
iconSpan && ` ${iconSpan}`,
|
|
1634
|
+
srOnlyHtml && ` ${srOnlyHtml}`,
|
|
1595
1635
|
` <span class="callout-title-text">${escapeHtml(title)}</span>`,
|
|
1596
1636
|
` </div>`,
|
|
1597
1637
|
].filter(Boolean).join("\n")
|
|
@@ -1599,7 +1639,7 @@ function buildCalloutFromParts(parsed, config, bodyHtml, options) {
|
|
|
1599
1639
|
if (parsed.collapsible) {
|
|
1600
1640
|
const openAttr = parsed.collapsibleOpen ? " open" : "";
|
|
1601
1641
|
return [
|
|
1602
|
-
`<details class="${safeCalloutClass} ${safeConfigClassName} collapsible${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}
|
|
1642
|
+
`<details class="${safeCalloutClass} ${safeConfigClassName} collapsible${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}${roleAttr}${labelledby} dir="auto"${openAttr}${directiveId}${directiveAttrs}>`,
|
|
1603
1643
|
titleBlock.replace(/<div /, "<summary ").replace(/<\/div>$/, "</summary>"),
|
|
1604
1644
|
` <div class="${safeCalloutBodyClass}">`,
|
|
1605
1645
|
bodyHtml,
|
|
@@ -1608,7 +1648,7 @@ function buildCalloutFromParts(parsed, config, bodyHtml, options) {
|
|
|
1608
1648
|
].filter(Boolean).join("\n");
|
|
1609
1649
|
}
|
|
1610
1650
|
return [
|
|
1611
|
-
`<aside class="${safeCalloutClass} ${safeConfigClassName}${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}
|
|
1651
|
+
`<aside class="${safeCalloutClass} ${safeConfigClassName}${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr}${roleAttr}${labelledby} dir="auto"${directiveId}${directiveAttrs}>`,
|
|
1612
1652
|
titleBlock,
|
|
1613
1653
|
` <div class="${safeCalloutBodyClass}">`,
|
|
1614
1654
|
bodyHtml,
|
|
@@ -1624,6 +1664,19 @@ export const remarkRemakeBlocks = (userOptions) => {
|
|
|
1624
1664
|
...DEFAULT_OPTIONS,
|
|
1625
1665
|
...userOptions,
|
|
1626
1666
|
};
|
|
1667
|
+
// v1.10.0: resolve devWarnings. undefined → auto (true in dev, false in prod).
|
|
1668
|
+
const isProd = typeof process !== "undefined" && process.env && process.env.NODE_ENV === "production";
|
|
1669
|
+
const devWarningsEnabled = options.devWarnings === undefined ? !isProd : options.devWarnings;
|
|
1670
|
+
// v1.10.0: strict customCallouts validation. Throws at init time when
|
|
1671
|
+
// strictConfigValidation is true and any config entry is missing required
|
|
1672
|
+
// fields. In non-strict mode, emits a dev warning instead.
|
|
1673
|
+
if (options.customCallouts && options.customCallouts.length > 0) {
|
|
1674
|
+
const errors = validateCustomCallouts(options.customCallouts, options.strictConfigValidation);
|
|
1675
|
+
if (errors.length > 0 && !options.strictConfigValidation) {
|
|
1676
|
+
for (const e of errors)
|
|
1677
|
+
warn(devWarningsEnabled, e);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1627
1680
|
// v1.8.0: resolve `syntax` into enableDirectiveSyntax / enableMkDocsSyntax.
|
|
1628
1681
|
// Explicit `enableDirectiveSyntax` / `enableMkDocsSyntax` (v1.3.0 / v1.4.0
|
|
1629
1682
|
// API) take precedence over `syntax` for backward compatibility.
|
|
@@ -1643,6 +1696,10 @@ export const remarkRemakeBlocks = (userOptions) => {
|
|
|
1643
1696
|
}
|
|
1644
1697
|
const configMap = buildCalloutConfigMap(options);
|
|
1645
1698
|
const calloutPattern = options.calloutPattern || buildCalloutPattern(configMap, options.enableDisclosures);
|
|
1699
|
+
// v1.10.0: stash devWarnings flag + known types list on options so
|
|
1700
|
+
// buildCalloutHtml / parseCalloutDirective can emit warnings.
|
|
1701
|
+
options._devWarnings = devWarningsEnabled;
|
|
1702
|
+
options._knownTypes = Array.from(configMap.keys());
|
|
1646
1703
|
// v1.9.0: sprite collector — accumulates { symbolId: svgInnerHtml } entries
|
|
1647
1704
|
// across all callouts in this document. Populated by buildCalloutHtml when
|
|
1648
1705
|
// icons.strategy === 'sprite'. Emitted as a single <svg> sprite at the top
|
|
@@ -1667,6 +1724,37 @@ export const remarkRemakeBlocks = (userOptions) => {
|
|
|
1667
1724
|
if (options.enableMkDocsSyntax) {
|
|
1668
1725
|
transformMkDocsSyntax(tree, configMap, options);
|
|
1669
1726
|
}
|
|
1727
|
+
// v1.10.0: Pass 0c — Scan for unknown callout directives and emit dev warnings.
|
|
1728
|
+
// The calloutPattern only matches KNOWN types, so unknown types like
|
|
1729
|
+
// [!TYPO] fall through to plain blockquotes WITHOUT entering parseCalloutDirective.
|
|
1730
|
+
// This pre-scan catches them so users get a "did you mean" hint.
|
|
1731
|
+
if (devWarningsEnabled) {
|
|
1732
|
+
const knownTypes = options._knownTypes;
|
|
1733
|
+
const anyDirectiveRe = /^\[!([A-Za-z][A-Za-z0-9-]*)\]/;
|
|
1734
|
+
visit(tree, "blockquote", (bq) => {
|
|
1735
|
+
const first = bq.children[0];
|
|
1736
|
+
if (!first || first.type !== "paragraph")
|
|
1737
|
+
return;
|
|
1738
|
+
const text = extractTextContent(first);
|
|
1739
|
+
if (!text)
|
|
1740
|
+
return;
|
|
1741
|
+
const m = text.match(anyDirectiveRe);
|
|
1742
|
+
if (!m)
|
|
1743
|
+
return;
|
|
1744
|
+
const rawType = m[1];
|
|
1745
|
+
const lowerType = rawType.toLowerCase();
|
|
1746
|
+
// Skip disclosures (empty type), pull quotes, epigraphs, and known types
|
|
1747
|
+
if (lowerType === "" || lowerType === "pull" || lowerType === "epigraph")
|
|
1748
|
+
return;
|
|
1749
|
+
if (configMap.has(lowerType))
|
|
1750
|
+
return;
|
|
1751
|
+
const suggestion = suggestSimilarType(rawType, knownTypes);
|
|
1752
|
+
const msg = suggestion
|
|
1753
|
+
? `Unknown callout type '${rawType}'. Did you mean '${suggestion}'?`
|
|
1754
|
+
: `Unknown callout type '${rawType}'. It will render as a plain blockquote.`;
|
|
1755
|
+
warn(true, msg);
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1670
1758
|
// ── Pass 1: Transform blockquotes → callouts / disclosures ──────
|
|
1671
1759
|
// We must process DEEPEST blockquotes first (inside-out) so that
|
|
1672
1760
|
// nested [!] directives are converted before their parents read them.
|
|
@@ -1703,7 +1791,7 @@ export const remarkRemakeBlocks = (userOptions) => {
|
|
|
1703
1791
|
if (foundIdx === -1)
|
|
1704
1792
|
continue; // Already replaced
|
|
1705
1793
|
}
|
|
1706
|
-
const parsed = parseCalloutDirective(bq, calloutPattern, configMap, options.enableDisclosures, options.defaultCollapse);
|
|
1794
|
+
const parsed = parseCalloutDirective(bq, calloutPattern, configMap, options.enableDisclosures, options.defaultCollapse, options);
|
|
1707
1795
|
if (parsed) {
|
|
1708
1796
|
// For disclosures: depth > 0 means it's nested inside another blockquote
|
|
1709
1797
|
// (which is likely another disclosure or callout) → tree view
|
|
@@ -1733,7 +1821,7 @@ export const remarkRemakeBlocks = (userOptions) => {
|
|
|
1733
1821
|
}
|
|
1734
1822
|
// ── Pass 2: Group consecutive disclosures into accordions ───────
|
|
1735
1823
|
if (options.enableAccordion && options.enableDisclosures) {
|
|
1736
|
-
groupAccordions(tree, options.accordionClass);
|
|
1824
|
+
groupAccordions(tree, options.accordionClass, options.ariaAccordion);
|
|
1737
1825
|
}
|
|
1738
1826
|
// v1.9.0: Pass 3 — Emit icon sprite at the top of the document.
|
|
1739
1827
|
// Only when icons.strategy === 'sprite' AND at least one callout
|
|
@@ -1753,7 +1841,7 @@ export const remarkRemakeBlocks = (userOptions) => {
|
|
|
1753
1841
|
// ---------------------------------------------------------------------------
|
|
1754
1842
|
// Pass 2: Group consecutive disclosure <details> into accordion wrapper
|
|
1755
1843
|
// ---------------------------------------------------------------------------
|
|
1756
|
-
function groupAccordions(tree, accordionClass) {
|
|
1844
|
+
function groupAccordions(tree, accordionClass, ariaAccordion) {
|
|
1757
1845
|
function walk(node) {
|
|
1758
1846
|
if (!node.children)
|
|
1759
1847
|
return;
|
|
@@ -1784,9 +1872,28 @@ function groupAccordions(tree, accordionClass) {
|
|
|
1784
1872
|
}
|
|
1785
1873
|
// If we found 2+ consecutive disclosures, wrap in accordion
|
|
1786
1874
|
if (group.length >= 2) {
|
|
1875
|
+
// v2.0.0: when ariaAccordion is enabled, add role="accordion" to the
|
|
1876
|
+
// wrapper and aria-expanded to each <summary> (reflecting open state).
|
|
1877
|
+
// Also add tabindex="0" to <summary> if not already focusable (native
|
|
1878
|
+
// summary is focusable by default, but explicit tabindex aids older
|
|
1879
|
+
// browsers and signals intent for the accordion.js keyboard handler).
|
|
1880
|
+
const accordionRole = ariaAccordion ? ` role="accordion"` : "";
|
|
1881
|
+
let groupHtml = group;
|
|
1882
|
+
if (ariaAccordion) {
|
|
1883
|
+
groupHtml = group.map(detailsHtml => {
|
|
1884
|
+
// Inject aria-expanded onto each <summary> based on whether the
|
|
1885
|
+
// parent <details> has the `open` attribute.
|
|
1886
|
+
const isOpen = /<details[^>]*\sopen[\s>]/.test(detailsHtml);
|
|
1887
|
+
const expanded = isOpen ? "true" : "false";
|
|
1888
|
+
if (/<summary[^>]*>/.test(detailsHtml)) {
|
|
1889
|
+
return detailsHtml.replace(/(<summary[^>]*?)(>)/, `$1 aria-expanded="${expanded}"$2`);
|
|
1890
|
+
}
|
|
1891
|
+
return detailsHtml;
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1787
1894
|
const accordionHtml = [
|
|
1788
|
-
`<div class="${escapeAttribute(accordionClass)}" data-accordion>`,
|
|
1789
|
-
...
|
|
1895
|
+
`<div class="${escapeAttribute(accordionClass)}"${accordionRole} data-accordion>`,
|
|
1896
|
+
...groupHtml,
|
|
1790
1897
|
`</div>`,
|
|
1791
1898
|
].join("\n");
|
|
1792
1899
|
newChildren.push(html(accordionHtml));
|
|
@@ -1816,8 +1923,15 @@ function groupAccordions(tree, accordionClass) {
|
|
|
1816
1923
|
*/
|
|
1817
1924
|
function isDisclosureHtml(htmlStr) {
|
|
1818
1925
|
const trimmed = htmlStr.trim();
|
|
1926
|
+
// v2.0.0: fix pre-existing bug — the old `!trimmed.includes('callout')` check
|
|
1927
|
+
// accidentally rejected ALL disclosures because their HTML contains
|
|
1928
|
+
// `aria-labelledby="callout-disclosure-N"`. The intent was to exclude
|
|
1929
|
+
// callout-details (which have `class="callout ..."`) from accordion grouping.
|
|
1930
|
+
// The correct check: the details element must have class="disclosure" (the
|
|
1931
|
+
// exact disclosure class) and NOT have the `callout` class (which would make
|
|
1932
|
+
// it a collapsible callout instead).
|
|
1819
1933
|
return trimmed.startsWith('<details class="disclosure"') &&
|
|
1820
|
-
!trimmed.
|
|
1934
|
+
!trimmed.startsWith('<details class="disclosure-accordion"');
|
|
1821
1935
|
}
|
|
1822
1936
|
// ---------------------------------------------------------------------------
|
|
1823
1937
|
// Backward-compatible alias
|