@dr-ishaan/remake-blocks 1.2.0 → 1.4.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/dist/remark-remake-blocks.d.ts.map +1 -1
- package/dist/remark-remake-blocks.js +549 -12
- package/dist/remark-remake-blocks.js.map +1 -1
- package/dist/sanitize-schema.json +50 -0
- package/dist/styles.css +39 -0
- package/dist/theme/docusaurus.css +610 -0
- package/dist/theme/github.css +660 -0
- package/dist/theme/obsidian.css +610 -0
- package/dist/theme/vitepress.css +603 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -5
|
@@ -185,6 +185,120 @@ const BUILTIN_CALLOUTS = [
|
|
|
185
185
|
},
|
|
186
186
|
];
|
|
187
187
|
// ---------------------------------------------------------------------------
|
|
188
|
+
// v1.3.0: Alternative icon sets (Lucide + Emoji)
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
// These are keyed by callout type. When iconSet === "lucide" or "emoji",
|
|
191
|
+
// the plugin uses these icons instead of the default Octicon SVGs.
|
|
192
|
+
// Custom callouts always use their configured `icon` regardless of iconSet.
|
|
193
|
+
const LUCIDE_ICONS = {
|
|
194
|
+
note: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>`,
|
|
195
|
+
tip: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg>`,
|
|
196
|
+
important: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/></svg>`,
|
|
197
|
+
warning: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>`,
|
|
198
|
+
caution: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`,
|
|
199
|
+
abstract: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M16 13H8"/><path d="M16 17H8"/><path d="M10 9H8"/></svg>`,
|
|
200
|
+
info: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>`,
|
|
201
|
+
success: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="m9 11 3 3L22 4"/></svg>`,
|
|
202
|
+
question: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>`,
|
|
203
|
+
failure: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`,
|
|
204
|
+
danger: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m13 2-3 7h7l-3 7"/><path d="M12 22a10 10 0 1 1 0-20"/></svg>`,
|
|
205
|
+
quote: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"/><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3z"/></svg>`,
|
|
206
|
+
bug: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m8 2 1.88 1.88"/><path d="M14.12 3.88 16 2"/><path d="M9 7.13v-1a3.003 3.003 0 1 1 6 0v1"/><path d="M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6"/><path d="M12 20v-9"/><path d="M6.53 9C4.6 8.8 3 7.1 3 5"/><path d="M6 13H2"/><path d="M3 21c0-2.1 1.7-3.9 3.8-4"/><path d="M20.97 5c0 2.1-1.6 3.8-3.5 4"/><path d="M22 13h-4"/><path d="M17.2 17c2.1.1 3.8 1.9 3.8 4"/></svg>`,
|
|
207
|
+
example: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5z"/><polyline points="14 2 14 8 20 8"/><path d="M16 13H8"/><path d="M16 17H8"/><path d="M10 9H8"/></svg>`,
|
|
208
|
+
todo: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="m9 12 2 2 4-4"/></svg>`,
|
|
209
|
+
summary: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M16 13H8"/><path d="M16 17H8"/></svg>`,
|
|
210
|
+
tldr: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m13 2-3 7h7l-3 7"/><path d="M12 22a10 10 0 1 1 0-20"/></svg>`,
|
|
211
|
+
hint: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 18v3c0 .6.4 1 1 1h4v-3h3v-3h2l1.4-1.4a6.5 6.5 0 1 0-4-4Z"/><circle cx="16.5" cy="7.5" r=".5" fill="currentColor"/></svg>`,
|
|
212
|
+
check: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="m9 12 2 2 4-4"/></svg>`,
|
|
213
|
+
done: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="m9 11 3 3L22 4"/></svg>`,
|
|
214
|
+
help: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>`,
|
|
215
|
+
faq: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M10 7h.01"/><path d="M14 7h.01"/><path d="M8 11h.01"/><path d="M12 11h.01"/><path d="M16 11h.01"/></svg>`,
|
|
216
|
+
attention: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>`,
|
|
217
|
+
fail: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`,
|
|
218
|
+
missing: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/><path d="M8 11h6"/></svg>`,
|
|
219
|
+
error: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"/><path d="M12 8v4"/><path d="M12 16h.01"/></svg>`,
|
|
220
|
+
cite: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/></svg>`,
|
|
221
|
+
// ── v1.4.0: Extra Lucide icons for per-callout named icon ({icon="rocket"}) ──
|
|
222
|
+
rocket: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/></svg>`,
|
|
223
|
+
heart: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/></svg>`,
|
|
224
|
+
star: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>`,
|
|
225
|
+
bell: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>`,
|
|
226
|
+
flag: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" x2="4" y1="22" y2="15"/></svg>`,
|
|
227
|
+
bookmark: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z"/></svg>`,
|
|
228
|
+
lightbulb: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg>`,
|
|
229
|
+
fire: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/></svg>`,
|
|
230
|
+
zap: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`,
|
|
231
|
+
shield: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/></svg>`,
|
|
232
|
+
code: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>`,
|
|
233
|
+
book: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/></svg>`,
|
|
234
|
+
pencil: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>`,
|
|
235
|
+
eye: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>`,
|
|
236
|
+
globe: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>`,
|
|
237
|
+
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>`,
|
|
238
|
+
key: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4"/><path d="m21 2-9.6 9.6"/><circle cx="7.5" cy="15.5" r="5.5"/></svg>`,
|
|
239
|
+
clock: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>`,
|
|
240
|
+
calendar: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/></svg>`,
|
|
241
|
+
package: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7.5 4.27 9 5.15"/><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg>`,
|
|
242
|
+
download: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>`,
|
|
243
|
+
upload: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/></svg>`,
|
|
244
|
+
link: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71a5 5 0 0 0-.54 7.54"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71a5 5 0 0 0 .54-7.54"/></svg>`,
|
|
245
|
+
settings: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>`,
|
|
246
|
+
terminal: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" x2="20" y1="19" y2="19"/></svg>`,
|
|
247
|
+
database: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19A9 3 0 0 0 21 19V5"/><path d="M3 12A9 3 0 0 0 21 12"/></svg>`,
|
|
248
|
+
cloud: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"/></svg>`,
|
|
249
|
+
};
|
|
250
|
+
const EMOJI_ICONS = {
|
|
251
|
+
note: "ℹ️",
|
|
252
|
+
tip: "💡",
|
|
253
|
+
important: "⚠️",
|
|
254
|
+
warning: "⚠️",
|
|
255
|
+
caution: "⛔",
|
|
256
|
+
abstract: "📋",
|
|
257
|
+
info: "ℹ️",
|
|
258
|
+
success: "✅",
|
|
259
|
+
question: "❓",
|
|
260
|
+
failure: "❌",
|
|
261
|
+
danger: "⚡",
|
|
262
|
+
quote: "💬",
|
|
263
|
+
bug: "🐛",
|
|
264
|
+
example: "📝",
|
|
265
|
+
todo: "☑️",
|
|
266
|
+
summary: "📋",
|
|
267
|
+
tldr: "⚡",
|
|
268
|
+
hint: "💡",
|
|
269
|
+
check: "✅",
|
|
270
|
+
done: "✅",
|
|
271
|
+
help: "❓",
|
|
272
|
+
faq: "❓",
|
|
273
|
+
attention: "⚠️",
|
|
274
|
+
fail: "❌",
|
|
275
|
+
missing: "🚫",
|
|
276
|
+
error: "❌",
|
|
277
|
+
cite: "📌",
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Resolve the icon for a given callout type based on the iconSet option.
|
|
281
|
+
* Returns the icon string (SVG or emoji) or empty string if not found.
|
|
282
|
+
*/
|
|
283
|
+
function resolveIcon(type, config, iconSet) {
|
|
284
|
+
// Custom callouts always use their configured icon, regardless of iconSet.
|
|
285
|
+
// We detect this by checking if the type is NOT in BUILTIN_CALLOUTS.
|
|
286
|
+
// (config may be a normalized custom callout — its icon is the user's choice.)
|
|
287
|
+
const isBuiltin = BUILTIN_CALLOUTS.some((b) => b.type === type);
|
|
288
|
+
if (!isBuiltin) {
|
|
289
|
+
return config.icon;
|
|
290
|
+
}
|
|
291
|
+
// For builtins, swap icon based on iconSet option.
|
|
292
|
+
if (iconSet === "lucide")
|
|
293
|
+
return LUCIDE_ICONS[type] ?? config.icon;
|
|
294
|
+
if (iconSet === "emoji")
|
|
295
|
+
return EMOJI_ICONS[type] ?? config.icon;
|
|
296
|
+
if (iconSet === "none")
|
|
297
|
+
return "";
|
|
298
|
+
// Default: octicon (the builtin's own icon)
|
|
299
|
+
return config.icon;
|
|
300
|
+
}
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
188
302
|
// Default plugin options
|
|
189
303
|
// ---------------------------------------------------------------------------
|
|
190
304
|
const DEFAULT_OPTIONS = {
|
|
@@ -213,6 +327,11 @@ const DEFAULT_OPTIONS = {
|
|
|
213
327
|
props: {},
|
|
214
328
|
build: undefined,
|
|
215
329
|
tags: {},
|
|
330
|
+
// v1.3.0 defaults
|
|
331
|
+
enableDirectiveSyntax: false,
|
|
332
|
+
// v1.4.0 defaults
|
|
333
|
+
labels: {},
|
|
334
|
+
enableMkDocsSyntax: false,
|
|
216
335
|
};
|
|
217
336
|
// ---------------------------------------------------------------------------
|
|
218
337
|
// Helper: Validate + normalize a single custom callout config.
|
|
@@ -321,6 +440,7 @@ function buildCalloutPattern(configMap, enableDisclosures) {
|
|
|
321
440
|
// Supported keys (v1.2.0):
|
|
322
441
|
// - icon: true|false — override showIndicator per-callout
|
|
323
442
|
// - appearance: default|minimal|simple|hidden — override appearance per-callout
|
|
443
|
+
// - inline: inline|inline-end — v1.3.0+: float left/right (responsive)
|
|
324
444
|
// Unknown keys are silently ignored.
|
|
325
445
|
// ---------------------------------------------------------------------------
|
|
326
446
|
function parseOverrides(overridesBlock) {
|
|
@@ -330,7 +450,23 @@ function parseOverrides(overridesBlock) {
|
|
|
330
450
|
if (!inner)
|
|
331
451
|
return undefined;
|
|
332
452
|
const result = {};
|
|
333
|
-
//
|
|
453
|
+
// First pass: handle bare keywords (no `=` sign).
|
|
454
|
+
// Supported bare keywords: `inline`, `inline-end`
|
|
455
|
+
// These set the inline override without needing `inline=true`.
|
|
456
|
+
// Note: `inline-end` must be matched BEFORE `inline` to avoid the prefix
|
|
457
|
+
// matching, so we use a single regex with `inline-end` first in the
|
|
458
|
+
// alternation and consume the entire match.
|
|
459
|
+
const bareKeywordRe = /\b(inline-end|inline)\b(?!\s*=)/gi;
|
|
460
|
+
let bareMatch;
|
|
461
|
+
while ((bareMatch = bareKeywordRe.exec(inner)) !== null) {
|
|
462
|
+
const kw = bareMatch[1].toLowerCase();
|
|
463
|
+
if (kw === "inline-end")
|
|
464
|
+
result.inline = "inline-end";
|
|
465
|
+
else if (kw === "inline")
|
|
466
|
+
result.inline = "inline";
|
|
467
|
+
}
|
|
468
|
+
// Second pass: handle key=value pairs.
|
|
469
|
+
// Values can be unquoted barewords or "quoted strings".
|
|
334
470
|
const pairRe = /(\w[\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s}]+))/g;
|
|
335
471
|
let m;
|
|
336
472
|
while ((m = pairRe.exec(inner)) !== null) {
|
|
@@ -341,19 +477,78 @@ function parseOverrides(overridesBlock) {
|
|
|
341
477
|
result.icon = false;
|
|
342
478
|
else if (value === "true" || value === "1" || value === "yes")
|
|
343
479
|
result.icon = true;
|
|
480
|
+
else if (value.length > 0) {
|
|
481
|
+
// v1.4.0: per-callout named icon (e.g. {icon="rocket"} uses Lucide rocket icon)
|
|
482
|
+
result.iconName = value;
|
|
483
|
+
result.icon = true; // ensure icon is shown
|
|
484
|
+
}
|
|
344
485
|
}
|
|
345
486
|
else if (key === "appearance") {
|
|
346
487
|
if (value === "default" || value === "minimal" || value === "simple" || value === "hidden") {
|
|
347
488
|
result.appearance = value;
|
|
348
489
|
}
|
|
349
490
|
}
|
|
491
|
+
else if (key === "inline") {
|
|
492
|
+
// v1.3.0+: inline floating callouts
|
|
493
|
+
if (value === "true" || value === "left" || value === "inline") {
|
|
494
|
+
result.inline = "inline";
|
|
495
|
+
}
|
|
496
|
+
else if (value === "right" || value === "inline-end" || value === "end") {
|
|
497
|
+
result.inline = "inline-end";
|
|
498
|
+
}
|
|
499
|
+
else if (value === "false" || value === "0" || value === "no") {
|
|
500
|
+
// explicitly disable inline (clear any bare-keyword setting)
|
|
501
|
+
result.inline = undefined;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
350
504
|
// Unknown keys silently ignored
|
|
351
505
|
}
|
|
352
|
-
if (result.icon === undefined && result.appearance === undefined)
|
|
506
|
+
if (result.icon === undefined && result.appearance === undefined && result.inline === undefined && result.iconName === undefined)
|
|
353
507
|
return undefined;
|
|
354
508
|
return result;
|
|
355
509
|
}
|
|
356
510
|
// ---------------------------------------------------------------------------
|
|
511
|
+
// v1.4.0: Parse directive attribute block `{#id .class key="value"}`
|
|
512
|
+
// Supports the remark-directive attribute syntax:
|
|
513
|
+
// #id — sets element id
|
|
514
|
+
// .class-name — adds CSS class
|
|
515
|
+
// key="value" — adds arbitrary HTML attribute
|
|
516
|
+
// key=value — adds arbitrary HTML attribute (bareword)
|
|
517
|
+
// ---------------------------------------------------------------------------
|
|
518
|
+
function parseDirectiveAttrs(attrsBlock) {
|
|
519
|
+
if (!attrsBlock)
|
|
520
|
+
return undefined;
|
|
521
|
+
const inner = attrsBlock.trim().replace(/^\{|\}$/g, "").trim();
|
|
522
|
+
if (!inner)
|
|
523
|
+
return undefined;
|
|
524
|
+
const result = { classes: [], attrs: {} };
|
|
525
|
+
// Match #id, .class, key="value", key='value', key=value
|
|
526
|
+
const tokenRe = /(#)([\w-]+)|(\.)([\w-]+)|(\w[\w-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s}]+))/g;
|
|
527
|
+
let m;
|
|
528
|
+
while ((m = tokenRe.exec(inner)) !== null) {
|
|
529
|
+
if (m[1] === "#" && m[2]) {
|
|
530
|
+
result.id = m[2];
|
|
531
|
+
}
|
|
532
|
+
else if (m[3] === "." && m[4]) {
|
|
533
|
+
result.classes.push(m[4]);
|
|
534
|
+
}
|
|
535
|
+
else if (m[5]) {
|
|
536
|
+
const key = m[5];
|
|
537
|
+
const value = (m[6] ?? m[7] ?? m[8] ?? "").trim();
|
|
538
|
+
// Block event handlers and style for security
|
|
539
|
+
if (key.toLowerCase().startsWith("on"))
|
|
540
|
+
continue;
|
|
541
|
+
if (key.toLowerCase() === "style")
|
|
542
|
+
continue;
|
|
543
|
+
result.attrs[key] = value;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (!result.id && (!result.classes || result.classes.length === 0) && Object.keys(result.attrs || {}).length === 0) {
|
|
547
|
+
return undefined;
|
|
548
|
+
}
|
|
549
|
+
return result;
|
|
550
|
+
}
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
357
552
|
// Helper: Parse the first paragraph of a blockquote to detect a callout
|
|
358
553
|
// ---------------------------------------------------------------------------
|
|
359
554
|
function parseCalloutDirective(blockquote, calloutPattern, configMap, enableDisclosures) {
|
|
@@ -528,8 +723,13 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
528
723
|
}
|
|
529
724
|
// ── Regular / Collapsible Callout ──────────────────────────────────
|
|
530
725
|
const config = configMap.get(parsed.type);
|
|
531
|
-
|
|
726
|
+
// v1.4.0: i18n label customization — labels option overrides defaultTitle
|
|
727
|
+
// Use || instead of ?? so empty strings also fall back to default
|
|
728
|
+
const defaultTitle = options.labels?.[parsed.type] || config.defaultTitle;
|
|
729
|
+
const title = parsed.customTitle || defaultTitle;
|
|
532
730
|
const dataAttr = options.dataCalloutType ? ` data-callout-type="${escapeAttribute(parsed.type)}"` : "";
|
|
731
|
+
// v1.4.0: data-collapsible attribute (for CSS targeting of collapsible vs non-collapsible)
|
|
732
|
+
const collapsibleDataAttr = ` data-collapsible="${parsed.collapsible ? "true" : "false"}"`;
|
|
533
733
|
const bodyHtml = buildBodyHtml(blockquote, calloutPattern, options);
|
|
534
734
|
// v1.2.0: resolve per-callout visuals (icon visibility + appearance)
|
|
535
735
|
const { showIcon, appearance } = resolveVisuals(parsed, options);
|
|
@@ -548,9 +748,6 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
548
748
|
// Sanitize colors before interpolating into style attribute
|
|
549
749
|
const safeIconColor = sanitizeColor(config.iconColor || config.color, "#57606a");
|
|
550
750
|
// v1.2.0: WCAG fix — use role="note" for ALL callouts (was: role="alert" for warnings).
|
|
551
|
-
// role="alert" is for dynamically-inserted content and causes aggressive immediate
|
|
552
|
-
// announcement that disrupts screen reader users on static content.
|
|
553
|
-
// Use role="note" + aria-labelledby for proper title→container association.
|
|
554
751
|
const ariaRole = "note";
|
|
555
752
|
// Escape className + calloutClass to prevent attribute breakout
|
|
556
753
|
const safeCalloutClass = escapeAttribute(options.calloutClass);
|
|
@@ -562,12 +759,42 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
562
759
|
// v1.2.0: resolve per-type props (dir, style, data-*, etc.)
|
|
563
760
|
const extraProps = resolveProps(options.props, parsed.type, parsed);
|
|
564
761
|
const extraAttrs = formatPropsAsAttrs(extraProps);
|
|
762
|
+
// v1.4.0: directive attributes (#id, .class, key="value")
|
|
763
|
+
let directiveId = "";
|
|
764
|
+
let directiveClasses = "";
|
|
765
|
+
let directiveAttrs = "";
|
|
766
|
+
if (parsed.directiveAttrs) {
|
|
767
|
+
if (parsed.directiveAttrs.id) {
|
|
768
|
+
directiveId = ` id="${escapeAttribute(parsed.directiveAttrs.id)}"`;
|
|
769
|
+
}
|
|
770
|
+
if (parsed.directiveAttrs.classes && parsed.directiveAttrs.classes.length > 0) {
|
|
771
|
+
directiveClasses = " " + parsed.directiveAttrs.classes.map(c => escapeAttribute(c)).join(" ");
|
|
772
|
+
}
|
|
773
|
+
if (parsed.directiveAttrs.attrs) {
|
|
774
|
+
directiveAttrs = formatPropsAsAttrs(parsed.directiveAttrs.attrs);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
565
777
|
// v1.2.0: appearance class
|
|
566
778
|
const appearanceClass = appearance !== "default" ? ` callout-${appearance}` : "";
|
|
779
|
+
// v1.3.0: inline floating class (responsive float left/right)
|
|
780
|
+
const inlineClass = parsed.overrides?.inline ? ` callout-${parsed.overrides.inline}` : "";
|
|
567
781
|
// v1.2.0: icon HTML — only render if showIcon AND appearance allows it
|
|
782
|
+
// v1.3.0: resolve icon based on iconSet option (octicon|lucide|emoji|none)
|
|
783
|
+
// v1.4.0: per-callout named icon override ({icon="rocket"})
|
|
568
784
|
const renderIcon = showIcon && appearance !== "simple" && appearance !== "hidden";
|
|
569
|
-
|
|
570
|
-
|
|
785
|
+
let resolvedIcon = "";
|
|
786
|
+
if (renderIcon) {
|
|
787
|
+
if (parsed.overrides?.iconName) {
|
|
788
|
+
// v1.4.0: per-callout named icon (Lucide icon by name)
|
|
789
|
+
resolvedIcon = LUCIDE_ICONS[parsed.overrides.iconName] ?? resolveIcon(parsed.type, config, options.iconSet);
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
resolvedIcon = resolveIcon(parsed.type, config, options.iconSet);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
const effectiveRenderIcon = renderIcon && resolvedIcon.length > 0;
|
|
796
|
+
const iconHtml = effectiveRenderIcon
|
|
797
|
+
? `<span class="callout-icon" style="color:${safeIconColor}" aria-hidden="true">${resolvedIcon}</span>`
|
|
571
798
|
: "";
|
|
572
799
|
// v1.2.0: title-text always rendered (unless appearance="hidden")
|
|
573
800
|
const renderTitle = appearance !== "hidden";
|
|
@@ -582,8 +809,8 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
582
809
|
const titleTextTag = tags.titleText || "span";
|
|
583
810
|
const bodyTag = tags.body || "div";
|
|
584
811
|
// Build icon span with custom tag if provided
|
|
585
|
-
const iconSpan =
|
|
586
|
-
? `<${iconTag} class="callout-icon" style="color:${safeIconColor}" aria-hidden="true">${
|
|
812
|
+
const iconSpan = effectiveRenderIcon
|
|
813
|
+
? `<${iconTag} class="callout-icon" style="color:${safeIconColor}" aria-hidden="true">${resolvedIcon}</${iconTag}>`
|
|
587
814
|
: "";
|
|
588
815
|
// Build the title block (or skip for "hidden" appearance)
|
|
589
816
|
const titleBlock = renderTitle
|
|
@@ -612,7 +839,7 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
612
839
|
if (parsed.collapsible) {
|
|
613
840
|
const openAttr = parsed.collapsibleOpen ? " open" : "";
|
|
614
841
|
return html([
|
|
615
|
-
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName} collapsible${appearanceClass}"${dataAttr} role="${ariaRole}"${labelledby}${dirAuto}${openAttr}${extraAttrs}>`,
|
|
842
|
+
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName} collapsible${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr} role="${ariaRole}"${labelledby}${dirAuto}${openAttr}${directiveId}${extraAttrs}${directiveAttrs}>`,
|
|
616
843
|
titleBlockWithDir,
|
|
617
844
|
` <${bodyTag} class="${safeCalloutBodyClass}">`,
|
|
618
845
|
bodyHtml,
|
|
@@ -621,7 +848,7 @@ function buildCalloutHtml(parsed, blockquote, calloutPattern, configMap, options
|
|
|
621
848
|
].filter(Boolean).join("\n"));
|
|
622
849
|
}
|
|
623
850
|
return html([
|
|
624
|
-
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName}${appearanceClass}"${dataAttr} role="${ariaRole}"${labelledby}${dirAuto}${extraAttrs}>`,
|
|
851
|
+
`<${containerTag} class="${safeCalloutClass} ${safeConfigClassName}${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr} role="${ariaRole}"${labelledby}${dirAuto}${directiveId}${extraAttrs}${directiveAttrs}>`,
|
|
625
852
|
titleBlockWithDir,
|
|
626
853
|
` <${bodyTag} class="${safeCalloutBodyClass}">`,
|
|
627
854
|
bodyHtml,
|
|
@@ -797,6 +1024,306 @@ function getBlockquoteDepth(node, parent, tree) {
|
|
|
797
1024
|
return depth;
|
|
798
1025
|
}
|
|
799
1026
|
// ---------------------------------------------------------------------------
|
|
1027
|
+
// v1.3.0: Directive syntax transformer (:::type[Title] ... :::)
|
|
1028
|
+
// ---------------------------------------------------------------------------
|
|
1029
|
+
// Detects :::type paragraphs and converts them to callouts. This is a
|
|
1030
|
+
// lightweight alternative to installing remark-directive — we detect the
|
|
1031
|
+
// pattern in raw paragraph text and synthesize a ParsedCallout + HTML node.
|
|
1032
|
+
//
|
|
1033
|
+
// Supported syntax:
|
|
1034
|
+
// :::note — basic callout (default title)
|
|
1035
|
+
// :::note[Custom Title] — with custom title
|
|
1036
|
+
// :::note{fold=-} — with overrides (icon, appearance, fold, inline)
|
|
1037
|
+
// :::note[Title]{fold=-} — title + overrides
|
|
1038
|
+
// ::::note ... :::: — 4-colon variant (for nesting; same behavior)
|
|
1039
|
+
//
|
|
1040
|
+
// The closing ::: must be on its own line. Body content between the opening
|
|
1041
|
+
// and closing lines is rendered as the callout body.
|
|
1042
|
+
function transformDirectiveSyntax(tree, configMap, options) {
|
|
1043
|
+
if (!tree.children)
|
|
1044
|
+
return;
|
|
1045
|
+
const newChildren = [];
|
|
1046
|
+
let i = 0;
|
|
1047
|
+
while (i < tree.children.length) {
|
|
1048
|
+
const child = tree.children[i];
|
|
1049
|
+
// Check if this is a paragraph containing a :::type ... ::: directive
|
|
1050
|
+
if (child.type === "paragraph") {
|
|
1051
|
+
const text = extractTextContent(child);
|
|
1052
|
+
// Use string methods (more reliable than regex with ^ anchor in some Node versions)
|
|
1053
|
+
// Pattern: :::type[Title]{overrides}\n<body>\n:::
|
|
1054
|
+
// Note: We use startsWith() + manual parsing instead of /^.../ regex
|
|
1055
|
+
// because the ^ anchor has issues in some Node.js environments.
|
|
1056
|
+
if (text.startsWith(":::")) {
|
|
1057
|
+
// Count the colons
|
|
1058
|
+
let colonCount = 0;
|
|
1059
|
+
while (colonCount < text.length && text[colonCount] === ":")
|
|
1060
|
+
colonCount++;
|
|
1061
|
+
if (colonCount >= 3) {
|
|
1062
|
+
const afterColons = text.slice(colonCount);
|
|
1063
|
+
// Match type name (letters, digits, hyphens)
|
|
1064
|
+
const typeMatch = afterColons.match(/^([a-zA-Z][\w-]*)/);
|
|
1065
|
+
if (typeMatch) {
|
|
1066
|
+
const rawType = typeMatch[1];
|
|
1067
|
+
const type = rawType.toLowerCase();
|
|
1068
|
+
const afterType = afterColons.slice(typeMatch[0].length);
|
|
1069
|
+
// Parse optional [Title] and {overrides}
|
|
1070
|
+
let title;
|
|
1071
|
+
let overridesBlock;
|
|
1072
|
+
let remaining = afterType;
|
|
1073
|
+
// Optional [Title]
|
|
1074
|
+
if (remaining.startsWith("[")) {
|
|
1075
|
+
const closeIdx = remaining.indexOf("]");
|
|
1076
|
+
if (closeIdx !== -1) {
|
|
1077
|
+
title = remaining.slice(1, closeIdx);
|
|
1078
|
+
remaining = remaining.slice(closeIdx + 1);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
// Optional {overrides}
|
|
1082
|
+
if (remaining.startsWith("{")) {
|
|
1083
|
+
const closeIdx = remaining.indexOf("}");
|
|
1084
|
+
if (closeIdx !== -1) {
|
|
1085
|
+
overridesBlock = `{${remaining.slice(1, closeIdx)}}`;
|
|
1086
|
+
remaining = remaining.slice(closeIdx + 1);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
// Must be followed by \n
|
|
1090
|
+
if (remaining.startsWith("\n")) {
|
|
1091
|
+
remaining = remaining.slice(1);
|
|
1092
|
+
// Find the closing ::: (must be on its own line at the end)
|
|
1093
|
+
// Look for \n::: at the end
|
|
1094
|
+
const closingMatch = remaining.match(/\n(:{3,})$/);
|
|
1095
|
+
if (closingMatch) {
|
|
1096
|
+
const bodyText = remaining.slice(0, closingMatch.index);
|
|
1097
|
+
// Only convert if the type is a known callout type
|
|
1098
|
+
if (configMap.has(type)) {
|
|
1099
|
+
const config = configMap.get(type);
|
|
1100
|
+
const customTitle = title?.trim() || undefined;
|
|
1101
|
+
const overrides = parseOverrides(overridesBlock);
|
|
1102
|
+
// v1.4.0: parse directive attributes (#id, .class, key="value")
|
|
1103
|
+
const directiveAttrs = parseDirectiveAttrs(overridesBlock);
|
|
1104
|
+
// Check for fold override
|
|
1105
|
+
let foldMarker = "";
|
|
1106
|
+
if (overridesBlock) {
|
|
1107
|
+
const foldMatch = overridesBlock.match(/fold\s*=\s*([+-])/);
|
|
1108
|
+
if (foldMatch)
|
|
1109
|
+
foldMarker = foldMatch[1];
|
|
1110
|
+
}
|
|
1111
|
+
const collapsible = foldMarker === "+" || foldMarker === "-";
|
|
1112
|
+
const collapsibleOpen = foldMarker === "+";
|
|
1113
|
+
const parsed = {
|
|
1114
|
+
type: type,
|
|
1115
|
+
customTitle,
|
|
1116
|
+
collapsible,
|
|
1117
|
+
collapsibleOpen,
|
|
1118
|
+
isDisclosure: false,
|
|
1119
|
+
overrides,
|
|
1120
|
+
directiveAttrs,
|
|
1121
|
+
};
|
|
1122
|
+
// Build body HTML from the bodyText
|
|
1123
|
+
const bodyLines = bodyText.split("\n");
|
|
1124
|
+
const bodyHtml = bodyLines
|
|
1125
|
+
.map((line) => {
|
|
1126
|
+
if (line.trim() === "")
|
|
1127
|
+
return "";
|
|
1128
|
+
const escaped = options.allowDangerousHtml ? line : escapeHtml(line);
|
|
1129
|
+
return `<p>${escaped}</p>\n`;
|
|
1130
|
+
})
|
|
1131
|
+
.join("");
|
|
1132
|
+
const calloutHtml = buildCalloutFromParts(parsed, config, bodyHtml, options);
|
|
1133
|
+
newChildren.push(html(calloutHtml));
|
|
1134
|
+
i++;
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
newChildren.push(child);
|
|
1144
|
+
i++;
|
|
1145
|
+
}
|
|
1146
|
+
tree.children = newChildren;
|
|
1147
|
+
}
|
|
1148
|
+
// ---------------------------------------------------------------------------
|
|
1149
|
+
// v1.4.0: MkDocs admonition syntax transformer (!!! / ??? / ???+)
|
|
1150
|
+
// ---------------------------------------------------------------------------
|
|
1151
|
+
// Detects !!! type, ??? type, ???+ type paragraphs and converts them to
|
|
1152
|
+
// callouts. This enables content portability with MkDocs Material.
|
|
1153
|
+
//
|
|
1154
|
+
// Supported syntax:
|
|
1155
|
+
// !!! note — non-collapsible callout
|
|
1156
|
+
// ??? note — collapsed callout (same as > [!NOTE]-)
|
|
1157
|
+
// ???+ note — expanded collapsible callout (same as > [!NOTE]+)
|
|
1158
|
+
// !!! note "Title" — with custom title (quoted)
|
|
1159
|
+
// !!! note "" — no title (strips default)
|
|
1160
|
+
//
|
|
1161
|
+
// The body is indented 4 spaces under the directive.
|
|
1162
|
+
function transformMkDocsSyntax(tree, configMap, options) {
|
|
1163
|
+
if (!tree.children)
|
|
1164
|
+
return;
|
|
1165
|
+
const newChildren = [];
|
|
1166
|
+
let i = 0;
|
|
1167
|
+
while (i < tree.children.length) {
|
|
1168
|
+
const child = tree.children[i];
|
|
1169
|
+
if (child.type === "paragraph") {
|
|
1170
|
+
const text = extractTextContent(child);
|
|
1171
|
+
// Check for !!! type, ??? type, or ???+ type
|
|
1172
|
+
// Pattern: (!!!|???\+?) type ["Title"]\n body (4-space indented)
|
|
1173
|
+
if (text.startsWith("!!!") || text.startsWith("???")) {
|
|
1174
|
+
// Determine the marker: !!!, ???, or ???+
|
|
1175
|
+
let marker = "";
|
|
1176
|
+
let markerEnd = 3;
|
|
1177
|
+
if (text.startsWith("???+")) {
|
|
1178
|
+
marker = "???+";
|
|
1179
|
+
markerEnd = 4;
|
|
1180
|
+
}
|
|
1181
|
+
else if (text.startsWith("???")) {
|
|
1182
|
+
marker = "???";
|
|
1183
|
+
markerEnd = 3;
|
|
1184
|
+
}
|
|
1185
|
+
else if (text.startsWith("!!!")) {
|
|
1186
|
+
marker = "!!!";
|
|
1187
|
+
markerEnd = 3;
|
|
1188
|
+
}
|
|
1189
|
+
const afterMarker = text.slice(markerEnd);
|
|
1190
|
+
// Parse type name
|
|
1191
|
+
const typeMatch = afterMarker.match(/^\s+([a-zA-Z][\w-]*)/);
|
|
1192
|
+
if (typeMatch) {
|
|
1193
|
+
const type = typeMatch[1].toLowerCase();
|
|
1194
|
+
let remaining = afterMarker.slice(typeMatch[0].length);
|
|
1195
|
+
// Parse optional "Title" (quoted)
|
|
1196
|
+
let title;
|
|
1197
|
+
const titleMatch = remaining.match(/^\s+"([^"]*)"/);
|
|
1198
|
+
if (titleMatch) {
|
|
1199
|
+
title = titleMatch[1];
|
|
1200
|
+
remaining = remaining.slice(titleMatch[0].length);
|
|
1201
|
+
}
|
|
1202
|
+
// Must be followed by \n
|
|
1203
|
+
if (remaining.startsWith("\n")) {
|
|
1204
|
+
remaining = remaining.slice(1);
|
|
1205
|
+
// Only convert if the type is a known callout type
|
|
1206
|
+
if (configMap.has(type)) {
|
|
1207
|
+
const config = configMap.get(type);
|
|
1208
|
+
const customTitle = title !== undefined ? (title.trim() || undefined) : undefined;
|
|
1209
|
+
// Determine collapsible state from marker
|
|
1210
|
+
const collapsible = marker === "???" || marker === "???+";
|
|
1211
|
+
const collapsibleOpen = marker === "???+";
|
|
1212
|
+
const parsed = {
|
|
1213
|
+
type: type,
|
|
1214
|
+
customTitle,
|
|
1215
|
+
collapsible,
|
|
1216
|
+
collapsibleOpen,
|
|
1217
|
+
isDisclosure: false,
|
|
1218
|
+
};
|
|
1219
|
+
// Build body HTML from the remaining text
|
|
1220
|
+
// Strip 4-space indentation from each line
|
|
1221
|
+
const bodyLines = remaining.split("\n").map((line) => {
|
|
1222
|
+
if (line.startsWith(" "))
|
|
1223
|
+
return line.slice(4);
|
|
1224
|
+
return line;
|
|
1225
|
+
});
|
|
1226
|
+
const bodyHtml = bodyLines
|
|
1227
|
+
.map((line) => {
|
|
1228
|
+
if (line.trim() === "")
|
|
1229
|
+
return "";
|
|
1230
|
+
const escaped = options.allowDangerousHtml ? line : escapeHtml(line);
|
|
1231
|
+
return `<p>${escaped}</p>\n`;
|
|
1232
|
+
})
|
|
1233
|
+
.join("");
|
|
1234
|
+
const calloutHtml = buildCalloutFromParts(parsed, config, bodyHtml, options);
|
|
1235
|
+
newChildren.push(html(calloutHtml));
|
|
1236
|
+
i++;
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
newChildren.push(child);
|
|
1244
|
+
i++;
|
|
1245
|
+
}
|
|
1246
|
+
tree.children = newChildren;
|
|
1247
|
+
}
|
|
1248
|
+
// ---------------------------------------------------------------------------
|
|
1249
|
+
// Helper: Build callout HTML from parsed parts (used by directive syntax)
|
|
1250
|
+
// ---------------------------------------------------------------------------
|
|
1251
|
+
function buildCalloutFromParts(parsed, config, bodyHtml, options) {
|
|
1252
|
+
// v1.4.0: i18n label customization
|
|
1253
|
+
const defaultTitle = options.labels?.[parsed.type] || config.defaultTitle;
|
|
1254
|
+
const title = parsed.customTitle || defaultTitle;
|
|
1255
|
+
const dataAttr = options.dataCalloutType ? ` data-callout-type="${escapeAttribute(parsed.type)}"` : "";
|
|
1256
|
+
const collapsibleDataAttr = ` data-collapsible="${parsed.collapsible ? "true" : "false"}"`;
|
|
1257
|
+
const safeIconColor = sanitizeColor(config.iconColor || config.color, "#57606a");
|
|
1258
|
+
const ariaRole = "note";
|
|
1259
|
+
const safeCalloutClass = escapeAttribute(options.calloutClass);
|
|
1260
|
+
const safeConfigClassName = escapeAttribute(config.className);
|
|
1261
|
+
const safeCalloutTitleClass = escapeAttribute(options.calloutTitleClass);
|
|
1262
|
+
const safeCalloutBodyClass = escapeAttribute(options.calloutBodyClass);
|
|
1263
|
+
const titleId = generateCalloutId(parsed.type);
|
|
1264
|
+
// v1.4.0: directive attributes
|
|
1265
|
+
let directiveId = "";
|
|
1266
|
+
let directiveClasses = "";
|
|
1267
|
+
let directiveAttrs = "";
|
|
1268
|
+
if (parsed.directiveAttrs) {
|
|
1269
|
+
if (parsed.directiveAttrs.id) {
|
|
1270
|
+
directiveId = ` id="${escapeAttribute(parsed.directiveAttrs.id)}"`;
|
|
1271
|
+
}
|
|
1272
|
+
if (parsed.directiveAttrs.classes && parsed.directiveAttrs.classes.length > 0) {
|
|
1273
|
+
directiveClasses = " " + parsed.directiveAttrs.classes.map(c => escapeAttribute(c)).join(" ");
|
|
1274
|
+
}
|
|
1275
|
+
if (parsed.directiveAttrs.attrs) {
|
|
1276
|
+
directiveAttrs = formatPropsAsAttrs(parsed.directiveAttrs.attrs);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
const { showIcon, appearance } = resolveVisuals(parsed, options);
|
|
1280
|
+
// v1.4.0: per-callout named icon
|
|
1281
|
+
let resolvedIcon = "";
|
|
1282
|
+
if (showIcon && appearance !== "simple" && appearance !== "hidden") {
|
|
1283
|
+
if (parsed.overrides?.iconName) {
|
|
1284
|
+
resolvedIcon = LUCIDE_ICONS[parsed.overrides.iconName] ?? resolveIcon(parsed.type, config, options.iconSet);
|
|
1285
|
+
}
|
|
1286
|
+
else {
|
|
1287
|
+
resolvedIcon = resolveIcon(parsed.type, config, options.iconSet);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const effectiveRenderIcon = showIcon && appearance !== "simple" && appearance !== "hidden" && resolvedIcon.length > 0;
|
|
1291
|
+
const iconSpan = effectiveRenderIcon
|
|
1292
|
+
? `<span class="callout-icon" style="color:${safeIconColor}" aria-hidden="true">${resolvedIcon}</span>`
|
|
1293
|
+
: "";
|
|
1294
|
+
const renderTitle = appearance !== "hidden";
|
|
1295
|
+
const labelledby = renderTitle ? ` aria-labelledby="${titleId}"` : "";
|
|
1296
|
+
const appearanceClass = appearance !== "default" ? ` callout-${appearance}` : "";
|
|
1297
|
+
const inlineClass = parsed.overrides?.inline ? ` callout-${parsed.overrides.inline}` : "";
|
|
1298
|
+
const titleBlock = renderTitle
|
|
1299
|
+
? [
|
|
1300
|
+
` <div class="${safeCalloutTitleClass}" style="color:${safeIconColor}" id="${titleId}" dir="auto">`,
|
|
1301
|
+
iconSpan && ` ${iconSpan}`,
|
|
1302
|
+
` <span class="callout-title-text">${escapeHtml(title)}</span>`,
|
|
1303
|
+
` </div>`,
|
|
1304
|
+
].filter(Boolean).join("\n")
|
|
1305
|
+
: "";
|
|
1306
|
+
if (parsed.collapsible) {
|
|
1307
|
+
const openAttr = parsed.collapsibleOpen ? " open" : "";
|
|
1308
|
+
return [
|
|
1309
|
+
`<details class="${safeCalloutClass} ${safeConfigClassName} collapsible${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr} role="${ariaRole}"${labelledby} dir="auto"${openAttr}${directiveId}${directiveAttrs}>`,
|
|
1310
|
+
titleBlock.replace(/<div /, "<summary ").replace(/<\/div>$/, "</summary>"),
|
|
1311
|
+
` <div class="${safeCalloutBodyClass}">`,
|
|
1312
|
+
bodyHtml,
|
|
1313
|
+
` </div>`,
|
|
1314
|
+
`</details>`,
|
|
1315
|
+
].filter(Boolean).join("\n");
|
|
1316
|
+
}
|
|
1317
|
+
return [
|
|
1318
|
+
`<aside class="${safeCalloutClass} ${safeConfigClassName}${appearanceClass}${inlineClass}${directiveClasses}"${dataAttr}${collapsibleDataAttr} role="${ariaRole}"${labelledby} dir="auto"${directiveId}${directiveAttrs}>`,
|
|
1319
|
+
titleBlock,
|
|
1320
|
+
` <div class="${safeCalloutBodyClass}">`,
|
|
1321
|
+
bodyHtml,
|
|
1322
|
+
` </div>`,
|
|
1323
|
+
`</aside>`,
|
|
1324
|
+
].filter(Boolean).join("\n");
|
|
1325
|
+
}
|
|
1326
|
+
// ---------------------------------------------------------------------------
|
|
800
1327
|
// Plugin implementation
|
|
801
1328
|
// ---------------------------------------------------------------------------
|
|
802
1329
|
export const remarkRemakeBlocks = (userOptions) => {
|
|
@@ -811,6 +1338,16 @@ export const remarkRemakeBlocks = (userOptions) => {
|
|
|
811
1338
|
// same HTML, including same ids). This is important for snapshot tests
|
|
812
1339
|
// and for SSR/deterministic rendering.
|
|
813
1340
|
resetCalloutIdCounter();
|
|
1341
|
+
// v1.3.0: Pass 0 — Transform :::type[Title]{overrides} ... ::: directive syntax
|
|
1342
|
+
// into callouts (Starlight/Docusaurus/MkDocs content portability).
|
|
1343
|
+
if (options.enableDirectiveSyntax) {
|
|
1344
|
+
transformDirectiveSyntax(tree, configMap, options);
|
|
1345
|
+
}
|
|
1346
|
+
// v1.4.0: Pass 0b — Transform !!! note / ??? note / ???+ note MkDocs admonition syntax
|
|
1347
|
+
// into callouts (MkDocs Material content portability).
|
|
1348
|
+
if (options.enableMkDocsSyntax) {
|
|
1349
|
+
transformMkDocsSyntax(tree, configMap, options);
|
|
1350
|
+
}
|
|
814
1351
|
// ── Pass 1: Transform blockquotes → callouts / disclosures ──────
|
|
815
1352
|
// We must process DEEPEST blockquotes first (inside-out) so that
|
|
816
1353
|
// nested [!] directives are converted before their parents read them.
|