@changerawr/markdown 1.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/dist/index.mjs ADDED
@@ -0,0 +1,2184 @@
1
+ "use client";
2
+
3
+ // src/parser.ts
4
+ var MarkdownParser = class {
5
+ constructor(config) {
6
+ this.rules = [];
7
+ this.warnings = [];
8
+ this.config = config || {};
9
+ }
10
+ addRule(rule) {
11
+ this.rules.push(rule);
12
+ this.rules.sort((a, b) => {
13
+ if (a.name.includes("alert") || a.name.includes("embed") || a.name.includes("button")) return -1;
14
+ if (b.name.includes("alert") || b.name.includes("embed") || b.name.includes("button")) return 1;
15
+ if (a.name === "task-list") return -1;
16
+ if (b.name === "task-list") return 1;
17
+ if (a.name === "list" && b.name === "task-list") return 1;
18
+ if (b.name === "list" && a.name === "task-list") return -1;
19
+ return a.name.localeCompare(b.name);
20
+ });
21
+ }
22
+ setupDefaultRulesIfEmpty() {
23
+ const hasDefaultRules = this.rules.some(
24
+ (rule) => !["alert", "button", "embed"].includes(rule.name)
25
+ );
26
+ if (!hasDefaultRules) {
27
+ this.setupDefaultRules();
28
+ }
29
+ }
30
+ hasRule(name) {
31
+ return this.rules.some((rule) => rule.name === name);
32
+ }
33
+ parse(markdown3) {
34
+ this.warnings = [];
35
+ this.setupDefaultRulesIfEmpty();
36
+ const processedMarkdown = this.preprocessMarkdown(markdown3);
37
+ const tokens = [];
38
+ let remaining = processedMarkdown;
39
+ let iterationCount = 0;
40
+ const maxIterations = this.config.maxIterations || markdown3.length * 2;
41
+ while (remaining.length > 0 && iterationCount < maxIterations) {
42
+ iterationCount++;
43
+ let matched = false;
44
+ let bestMatch = null;
45
+ for (const rule of this.rules) {
46
+ try {
47
+ const pattern = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", ""));
48
+ const match = remaining.match(pattern);
49
+ if (match && match.index !== void 0) {
50
+ const priority = match.index === 0 ? 1e3 : 1e3 - match.index;
51
+ if (!bestMatch || priority > bestMatch.priority || priority === bestMatch.priority && match.index < (bestMatch.match.index || 0)) {
52
+ bestMatch = { rule, match, priority };
53
+ }
54
+ }
55
+ } catch (error) {
56
+ if (this.config.debugMode) {
57
+ console.warn(`Error in rule "${rule.name}":`, error);
58
+ }
59
+ }
60
+ }
61
+ if (bestMatch && bestMatch.match.index !== void 0) {
62
+ const { rule, match } = bestMatch;
63
+ const matchIndex = match.index;
64
+ if (matchIndex > 0) {
65
+ const textBefore = remaining.slice(0, matchIndex);
66
+ tokens.push({
67
+ type: "text",
68
+ content: textBefore,
69
+ raw: textBefore
70
+ });
71
+ remaining = remaining.slice(matchIndex);
72
+ continue;
73
+ }
74
+ try {
75
+ const token = rule.render(match);
76
+ tokens.push({
77
+ ...token,
78
+ raw: match[0] || ""
79
+ });
80
+ remaining = remaining.slice(match[0]?.length || 0);
81
+ matched = true;
82
+ } catch (error) {
83
+ const errorMessage = error instanceof Error ? error.message : String(error);
84
+ this.warnings.push(`Failed to render ${rule.name}: ${errorMessage}`);
85
+ const char = remaining[0];
86
+ if (char) {
87
+ tokens.push({
88
+ type: "text",
89
+ content: char,
90
+ raw: char
91
+ });
92
+ remaining = remaining.slice(1);
93
+ }
94
+ }
95
+ }
96
+ if (!matched) {
97
+ const char = remaining[0];
98
+ if (char) {
99
+ tokens.push({
100
+ type: "text",
101
+ content: char,
102
+ raw: char
103
+ });
104
+ remaining = remaining.slice(1);
105
+ }
106
+ }
107
+ }
108
+ if (iterationCount >= maxIterations) {
109
+ this.warnings.push("Parser hit maximum iterations - possible infinite loop detected");
110
+ }
111
+ const processedTokens = this.postProcessTokens(tokens);
112
+ return processedTokens;
113
+ }
114
+ getWarnings() {
115
+ return [...this.warnings];
116
+ }
117
+ getConfig() {
118
+ return { ...this.config };
119
+ }
120
+ updateConfig(config) {
121
+ this.config = { ...this.config, ...config };
122
+ }
123
+ setDebugMode(enabled) {
124
+ this.config.debugMode = enabled;
125
+ }
126
+ clearWarnings() {
127
+ this.warnings = [];
128
+ }
129
+ getIterationCount() {
130
+ return 0;
131
+ }
132
+ preprocessMarkdown(markdown3) {
133
+ if (this.config.validateMarkdown) {
134
+ this.validateMarkdown(markdown3);
135
+ }
136
+ return markdown3.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
137
+ }
138
+ validateMarkdown(markdown3) {
139
+ const boldMatches = markdown3.match(/\*\*/g);
140
+ if (boldMatches && boldMatches.length % 2 !== 0) {
141
+ this.warnings.push("Unclosed bold markers (**) detected - some bold formatting may not work");
142
+ }
143
+ const italicMatches = markdown3.match(/(?<!\*)\*(?!\*)/g);
144
+ if (italicMatches && italicMatches.length % 2 !== 0) {
145
+ this.warnings.push("Unclosed italic markers (*) detected - some italic formatting may not work");
146
+ }
147
+ const codeBlockMatches = markdown3.match(/```/g);
148
+ if (codeBlockMatches && codeBlockMatches.length % 2 !== 0) {
149
+ this.warnings.push("Unclosed code blocks (```) detected - some code formatting may not work");
150
+ }
151
+ const inlineCodeMatches = markdown3.match(/`/g);
152
+ if (inlineCodeMatches && inlineCodeMatches.length % 2 !== 0) {
153
+ this.warnings.push("Unclosed inline code markers (`) detected - some code formatting may not work");
154
+ }
155
+ }
156
+ postProcessTokens(tokens) {
157
+ const processed = [];
158
+ let i = 0;
159
+ while (i < tokens.length) {
160
+ const token = tokens[i];
161
+ if (token && token.type === "text") {
162
+ let textContent = token.content || token.raw || "";
163
+ let j = i + 1;
164
+ while (j < tokens.length && tokens[j] && tokens[j].type === "text") {
165
+ const nextToken = tokens[j];
166
+ textContent += nextToken.content || nextToken.raw || "";
167
+ j++;
168
+ }
169
+ if (textContent.trim().length > 0 || textContent.includes("\n")) {
170
+ processed.push({
171
+ type: "text",
172
+ content: textContent,
173
+ raw: textContent
174
+ });
175
+ }
176
+ i = j;
177
+ } else if (token) {
178
+ processed.push(token);
179
+ i++;
180
+ } else {
181
+ i++;
182
+ }
183
+ }
184
+ return processed;
185
+ }
186
+ setupDefaultRules() {
187
+ this.addRule({
188
+ name: "heading",
189
+ pattern: /^(#{1,6})\s+(.+)$/m,
190
+ render: (match) => ({
191
+ type: "heading",
192
+ content: match[2]?.trim() || "",
193
+ raw: match[0] || "",
194
+ attributes: {
195
+ level: String(match[1]?.length || 1)
196
+ }
197
+ })
198
+ });
199
+ this.addRule({
200
+ name: "codeblock",
201
+ pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
202
+ render: (match) => ({
203
+ type: "codeblock",
204
+ content: match[2] || "",
205
+ raw: match[0] || "",
206
+ attributes: {
207
+ language: match[1] || "text"
208
+ }
209
+ })
210
+ });
211
+ this.addRule({
212
+ name: "hard-break-backslash",
213
+ pattern: /\\\s*\n/,
214
+ render: (match) => ({
215
+ type: "line-break",
216
+ content: "",
217
+ raw: match[0] || ""
218
+ })
219
+ });
220
+ this.addRule({
221
+ name: "hard-break-spaces",
222
+ pattern: / +\n/,
223
+ render: (match) => ({
224
+ type: "line-break",
225
+ content: "",
226
+ raw: match[0] || ""
227
+ })
228
+ });
229
+ this.addRule({
230
+ name: "paragraph-break",
231
+ pattern: /\n\s*\n/,
232
+ render: (match) => ({
233
+ type: "paragraph-break",
234
+ content: "",
235
+ raw: match[0] || ""
236
+ })
237
+ });
238
+ this.addRule({
239
+ name: "bold",
240
+ pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
241
+ render: (match) => ({
242
+ type: "bold",
243
+ content: match[1] || "",
244
+ raw: match[0] || ""
245
+ })
246
+ });
247
+ this.addRule({
248
+ name: "italic",
249
+ pattern: /\*((?:(?!\*).)+)\*/,
250
+ render: (match) => ({
251
+ type: "italic",
252
+ content: match[1] || "",
253
+ raw: match[0] || ""
254
+ })
255
+ });
256
+ this.addRule({
257
+ name: "code",
258
+ pattern: /`([^`]+)`/,
259
+ render: (match) => ({
260
+ type: "code",
261
+ content: match[1] || "",
262
+ raw: match[0] || ""
263
+ })
264
+ });
265
+ this.addRule({
266
+ name: "image",
267
+ pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
268
+ render: (match) => ({
269
+ type: "image",
270
+ content: match[1] || "",
271
+ raw: match[0] || "",
272
+ attributes: {
273
+ alt: match[1] || "",
274
+ src: match[2] || "",
275
+ title: match[3] || ""
276
+ }
277
+ })
278
+ });
279
+ this.addRule({
280
+ name: "link",
281
+ pattern: /\[([^\]]+)\]\(([^)]+)\)/,
282
+ render: (match) => ({
283
+ type: "link",
284
+ content: match[1] || "",
285
+ raw: match[0] || "",
286
+ attributes: {
287
+ href: match[2] || ""
288
+ }
289
+ })
290
+ });
291
+ this.addRule({
292
+ name: "task-list",
293
+ pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
294
+ render: (match) => ({
295
+ type: "task-item",
296
+ content: match[3] || "",
297
+ raw: match[0] || "",
298
+ attributes: {
299
+ checked: String((match[2] || "").toLowerCase() === "x")
300
+ }
301
+ })
302
+ });
303
+ this.addRule({
304
+ name: "list",
305
+ pattern: /^(\s*)[-*+]\s+(.+)$/m,
306
+ render: (match) => ({
307
+ type: "list-item",
308
+ content: match[2] || "",
309
+ raw: match[0] || ""
310
+ })
311
+ });
312
+ this.addRule({
313
+ name: "blockquote",
314
+ pattern: /^>\s+(.+)$/m,
315
+ render: (match) => ({
316
+ type: "blockquote",
317
+ content: match[1] || "",
318
+ raw: match[0] || ""
319
+ })
320
+ });
321
+ this.addRule({
322
+ name: "hr",
323
+ pattern: /^---$/m,
324
+ render: (match) => ({
325
+ type: "hr",
326
+ content: "",
327
+ raw: match[0] || ""
328
+ })
329
+ });
330
+ this.addRule({
331
+ name: "soft-break",
332
+ pattern: /\n/,
333
+ render: (match) => ({
334
+ type: "soft-break",
335
+ content: " ",
336
+ // Convert to space for inline text
337
+ raw: match[0] || ""
338
+ })
339
+ });
340
+ }
341
+ };
342
+
343
+ // src/utils.ts
344
+ var DOMPurify = {
345
+ sanitize: (html) => html
346
+ };
347
+ if (typeof window !== "undefined") {
348
+ import("dompurify").then((module) => {
349
+ DOMPurify = module.default;
350
+ }).catch((err) => {
351
+ console.error("Failed to load DOMPurify", err);
352
+ });
353
+ }
354
+ var ALLOWED_TAGS = [
355
+ // Standard HTML
356
+ "h1",
357
+ "h2",
358
+ "h3",
359
+ "h4",
360
+ "h5",
361
+ "h6",
362
+ "p",
363
+ "br",
364
+ "strong",
365
+ "em",
366
+ "del",
367
+ "ins",
368
+ "a",
369
+ "img",
370
+ "ul",
371
+ "ol",
372
+ "li",
373
+ "blockquote",
374
+ "pre",
375
+ "code",
376
+ "table",
377
+ "thead",
378
+ "tbody",
379
+ "tr",
380
+ "th",
381
+ "td",
382
+ "div",
383
+ "span",
384
+ "sup",
385
+ "sub",
386
+ "hr",
387
+ "input",
388
+ // Embeds
389
+ "iframe",
390
+ "embed",
391
+ "object",
392
+ "param",
393
+ "video",
394
+ "audio",
395
+ "source",
396
+ // SVG
397
+ "svg",
398
+ "path",
399
+ "polyline",
400
+ "line",
401
+ "circle",
402
+ "rect",
403
+ "g",
404
+ "defs",
405
+ "use",
406
+ // Form elements
407
+ "form",
408
+ "fieldset",
409
+ "legend",
410
+ "label",
411
+ "select",
412
+ "option",
413
+ "textarea",
414
+ "button"
415
+ ];
416
+ var ALLOWED_ATTR = [
417
+ // Standard attributes
418
+ "href",
419
+ "title",
420
+ "alt",
421
+ "src",
422
+ "class",
423
+ "id",
424
+ "target",
425
+ "rel",
426
+ "type",
427
+ "checked",
428
+ "disabled",
429
+ "loading",
430
+ "width",
431
+ "height",
432
+ "style",
433
+ "role",
434
+ // Iframe attributes
435
+ "frameborder",
436
+ "allowfullscreen",
437
+ "allow",
438
+ "sandbox",
439
+ "scrolling",
440
+ "allowtransparency",
441
+ "name",
442
+ "seamless",
443
+ "srcdoc",
444
+ // Data attributes (for embeds)
445
+ "data-*",
446
+ // SVG attributes
447
+ "viewBox",
448
+ "fill",
449
+ "stroke",
450
+ "stroke-width",
451
+ "stroke-linecap",
452
+ "stroke-linejoin",
453
+ "d",
454
+ "points",
455
+ "x1",
456
+ "y1",
457
+ "x2",
458
+ "y2",
459
+ "cx",
460
+ "cy",
461
+ "r",
462
+ "rx",
463
+ "ry",
464
+ // Media attributes
465
+ "autoplay",
466
+ "controls",
467
+ "loop",
468
+ "muted",
469
+ "preload",
470
+ "poster",
471
+ // Form attributes
472
+ "value",
473
+ "placeholder",
474
+ "required",
475
+ "readonly",
476
+ "maxlength",
477
+ "minlength",
478
+ "max",
479
+ "min",
480
+ "step",
481
+ "pattern",
482
+ "autocomplete",
483
+ "autofocus"
484
+ ];
485
+ function escapeHtml(text) {
486
+ const map = {
487
+ "&": "&amp;",
488
+ "<": "&lt;",
489
+ ">": "&gt;",
490
+ '"': "&quot;",
491
+ "'": "&#39;"
492
+ };
493
+ return text.replace(/[&<>"']/g, (char) => map[char] || char);
494
+ }
495
+ function generateId(text) {
496
+ return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").trim();
497
+ }
498
+ function sanitizeHtml(html) {
499
+ try {
500
+ if (html.includes("codepen.io/") || html.includes("youtube.com/embed/")) {
501
+ return html;
502
+ }
503
+ if (typeof DOMPurify?.sanitize === "function") {
504
+ const sanitized = DOMPurify.sanitize(html, {
505
+ ALLOWED_TAGS,
506
+ ALLOWED_ATTR,
507
+ ALLOW_DATA_ATTR: true,
508
+ ALLOW_UNKNOWN_PROTOCOLS: false,
509
+ SAFE_FOR_TEMPLATES: false,
510
+ WHOLE_DOCUMENT: false,
511
+ RETURN_DOM: false,
512
+ RETURN_DOM_FRAGMENT: false,
513
+ FORCE_BODY: false,
514
+ SANITIZE_DOM: false,
515
+ SANITIZE_NAMED_PROPS: false,
516
+ FORBID_ATTR: ["onload", "onerror", "onclick", "onmouseover", "onmouseout", "onfocus", "onblur"],
517
+ ADD_TAGS: ["iframe", "embed", "object", "param"],
518
+ ADD_ATTR: [
519
+ "allow",
520
+ "allowfullscreen",
521
+ "frameborder",
522
+ "scrolling",
523
+ "allowtransparency",
524
+ "sandbox",
525
+ "loading",
526
+ "style",
527
+ "title",
528
+ "name",
529
+ "seamless",
530
+ "srcdoc"
531
+ ],
532
+ ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|xxx):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
533
+ });
534
+ if (sanitized.length < html.length * 0.7) {
535
+ return basicSanitize(html);
536
+ }
537
+ return sanitized;
538
+ }
539
+ return basicSanitize(html);
540
+ } catch (error) {
541
+ console.error("Sanitization failed:", error);
542
+ return basicSanitize(html);
543
+ }
544
+ }
545
+ function basicSanitize(html) {
546
+ return html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/on\w+\s*=\s*"[^"]*"/gi, "").replace(/on\w+\s*=\s*'[^']*'/gi, "").replace(/javascript:/gi, "");
547
+ }
548
+ function isBrowser() {
549
+ return typeof window !== "undefined" && typeof document !== "undefined";
550
+ }
551
+ function isNode() {
552
+ return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
553
+ }
554
+ function debounce(func, wait) {
555
+ let timeout;
556
+ return function executedFunction(...args) {
557
+ const later = () => {
558
+ clearTimeout(timeout);
559
+ func(...args);
560
+ };
561
+ clearTimeout(timeout);
562
+ timeout = setTimeout(later, wait);
563
+ };
564
+ }
565
+ function deepMerge(target, source) {
566
+ const output = { ...target };
567
+ for (const key in source) {
568
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
569
+ if (key in target && typeof target[key] === "object" && !Array.isArray(target[key])) {
570
+ output[key] = deepMerge(target[key], source[key]);
571
+ } else {
572
+ output[key] = source[key];
573
+ }
574
+ } else {
575
+ output[key] = source[key];
576
+ }
577
+ }
578
+ return output;
579
+ }
580
+ function extractDomain(url) {
581
+ try {
582
+ return new URL(url).hostname;
583
+ } catch {
584
+ return url;
585
+ }
586
+ }
587
+ function isValidUrl(url) {
588
+ try {
589
+ new URL(url);
590
+ return true;
591
+ } catch {
592
+ return false;
593
+ }
594
+ }
595
+ function parseOptions(options) {
596
+ const parsed = {};
597
+ if (!options) return parsed;
598
+ options.split(",").forEach((option) => {
599
+ const [key, value] = option.split(":").map((s) => s.trim());
600
+ if (key && value) {
601
+ parsed[key] = value;
602
+ }
603
+ });
604
+ return parsed;
605
+ }
606
+ var PerformanceTimer = class {
607
+ constructor() {
608
+ this.marks = {};
609
+ this.startTime = performance.now();
610
+ }
611
+ mark(name) {
612
+ this.marks[name] = performance.now() - this.startTime;
613
+ }
614
+ getTime(name) {
615
+ if (name && this.marks[name] !== void 0) {
616
+ return this.marks[name];
617
+ }
618
+ return performance.now() - this.startTime;
619
+ }
620
+ getAllMarks() {
621
+ return { ...this.marks };
622
+ }
623
+ };
624
+ var Logger = class {
625
+ constructor(debugMode = false) {
626
+ this.debugMode = debugMode;
627
+ }
628
+ debug(...args) {
629
+ if (this.debugMode) {
630
+ console.log("[DEBUG]", ...args);
631
+ }
632
+ }
633
+ info(...args) {
634
+ console.info("[INFO]", ...args);
635
+ }
636
+ warn(...args) {
637
+ console.warn("[WARN]", ...args);
638
+ }
639
+ error(...args) {
640
+ console.error("[ERROR]", ...args);
641
+ }
642
+ setDebugMode(enabled) {
643
+ this.debugMode = enabled;
644
+ }
645
+ };
646
+
647
+ // src/renderer.ts
648
+ var MarkdownRenderer = class {
649
+ constructor(config) {
650
+ this.rules = /* @__PURE__ */ new Map();
651
+ this.warnings = [];
652
+ this.config = {
653
+ format: "tailwind",
654
+ sanitize: true,
655
+ allowUnsafeHtml: false,
656
+ debugMode: false,
657
+ ...config
658
+ };
659
+ this.setupDefaultRules();
660
+ }
661
+ addRule(rule) {
662
+ this.rules.set(rule.type, rule);
663
+ }
664
+ hasRule(type) {
665
+ return this.rules.has(type);
666
+ }
667
+ render(tokens) {
668
+ this.warnings = [];
669
+ const htmlParts = tokens.map((token) => this.renderToken(token));
670
+ const combinedHtml = htmlParts.join("");
671
+ if (this.config.sanitize && !this.config.allowUnsafeHtml) {
672
+ return sanitizeHtml(combinedHtml);
673
+ }
674
+ return combinedHtml;
675
+ }
676
+ getWarnings() {
677
+ return [...this.warnings];
678
+ }
679
+ getConfig() {
680
+ return { ...this.config };
681
+ }
682
+ updateConfig(config) {
683
+ this.config = { ...this.config, ...config };
684
+ }
685
+ setDebugMode(enabled) {
686
+ this.config.debugMode = enabled;
687
+ }
688
+ clearWarnings() {
689
+ this.warnings = [];
690
+ }
691
+ renderToken(token) {
692
+ const rule = this.rules.get(token.type);
693
+ if (rule) {
694
+ try {
695
+ return rule.render(token);
696
+ } catch (error) {
697
+ const errorMessage = error instanceof Error ? error.message : String(error);
698
+ this.warnings.push(`Render error for ${token.type}: ${errorMessage}`);
699
+ return this.createErrorBlock(`Render error for ${token.type}: ${errorMessage}`);
700
+ }
701
+ }
702
+ if (token.type === "text") {
703
+ return escapeHtml(token.content || token.raw || "");
704
+ }
705
+ if (this.config.debugMode) {
706
+ return this.createDebugBlock(token);
707
+ }
708
+ return escapeHtml(token.content || token.raw || "");
709
+ }
710
+ createErrorBlock(message) {
711
+ if (this.config.format === "html") {
712
+ return `<div style="background-color: #fee; border: 1px solid #fcc; color: #c66; padding: 8px; border-radius: 4px; margin: 8px 0; font-size: 14px;">
713
+ <strong>Render Error:</strong> ${escapeHtml(message)}
714
+ </div>`;
715
+ }
716
+ return `<div class="bg-red-100 border border-red-300 text-red-800 p-2 rounded text-sm mb-2">
717
+ <strong>Render Error:</strong> ${escapeHtml(message)}
718
+ </div>`;
719
+ }
720
+ createDebugBlock(token) {
721
+ if (this.config.format === "html") {
722
+ return `<div style="background-color: #fffbf0; border: 1px solid #fed; color: #b8860b; padding: 8px; border-radius: 4px; margin: 8px 0; font-size: 14px;">
723
+ <strong>Unknown token type:</strong> ${escapeHtml(token.type)}<br>
724
+ <strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
725
+ </div>`;
726
+ }
727
+ return `<div class="bg-yellow-100 border border-yellow-300 text-yellow-800 p-2 rounded text-sm mb-2">
728
+ <strong>Unknown token type:</strong> ${escapeHtml(token.type)}<br>
729
+ <strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
730
+ </div>`;
731
+ }
732
+ setupDefaultRules() {
733
+ this.addRule({
734
+ type: "heading",
735
+ render: (token) => {
736
+ const level = parseInt(token.attributes?.level || "1");
737
+ const text = token.content;
738
+ const id = generateId(text);
739
+ const escapedContent = escapeHtml(text);
740
+ if (this.config.format === "html") {
741
+ return `<h${level} id="${id}">${escapedContent}</h${level}>`;
742
+ }
743
+ let headingClasses = "group relative flex items-center gap-2";
744
+ switch (level) {
745
+ case 1:
746
+ headingClasses += " text-3xl font-bold mt-8 mb-4";
747
+ break;
748
+ case 2:
749
+ headingClasses += " text-2xl font-semibold mt-6 mb-3";
750
+ break;
751
+ case 3:
752
+ headingClasses += " text-xl font-medium mt-5 mb-3";
753
+ break;
754
+ case 4:
755
+ headingClasses += " text-lg font-medium mt-4 mb-2";
756
+ break;
757
+ case 5:
758
+ headingClasses += " text-base font-medium mt-3 mb-2";
759
+ break;
760
+ case 6:
761
+ headingClasses += " text-sm font-medium mt-3 mb-2";
762
+ break;
763
+ }
764
+ return `<h${level} id="${id}" class="${headingClasses}">
765
+ ${escapedContent}
766
+ <a href="#${id}" class="opacity-0 group-hover:opacity-100 text-muted-foreground transition-opacity">
767
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
768
+ <path d="M7.5 4H5.75A3.75 3.75 0 002 7.75v.5a3.75 3.75 0 003.75 3.75h1.5m-1.5-4h3m1.5-4h1.75A3.75 3.75 0 0114 7.75v.5a3.75 3.75 0 01-3.75 3.75H8.5"/>
769
+ </svg>
770
+ </a>
771
+ </h${level}>`;
772
+ }
773
+ });
774
+ this.addRule({
775
+ type: "bold",
776
+ render: (token) => {
777
+ const content = escapeHtml(token.content);
778
+ if (this.config.format === "html") {
779
+ return `<strong>${content}</strong>`;
780
+ }
781
+ return `<strong class="font-bold">${content}</strong>`;
782
+ }
783
+ });
784
+ this.addRule({
785
+ type: "italic",
786
+ render: (token) => {
787
+ const content = escapeHtml(token.content);
788
+ if (this.config.format === "html") {
789
+ return `<em>${content}</em>`;
790
+ }
791
+ return `<em class="italic">${content}</em>`;
792
+ }
793
+ });
794
+ this.addRule({
795
+ type: "code",
796
+ render: (token) => {
797
+ const content = escapeHtml(token.content);
798
+ if (this.config.format === "html") {
799
+ return `<code style="background-color: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 0.875rem;">${content}</code>`;
800
+ }
801
+ return `<code class="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">${content}</code>`;
802
+ }
803
+ });
804
+ this.addRule({
805
+ type: "codeblock",
806
+ render: (token) => {
807
+ const language = token.attributes?.language || "text";
808
+ const escapedCode = escapeHtml(token.content);
809
+ if (this.config.format === "html") {
810
+ return `<pre style="background-color: #f3f4f6; padding: 16px; border-radius: 6px; overflow-x: auto; margin: 16px 0;"><code class="language-${escapeHtml(language)}">${escapedCode}</code></pre>`;
811
+ }
812
+ return `<pre class="bg-muted p-4 rounded-md overflow-x-auto my-4"><code class="language-${escapeHtml(language)}">${escapedCode}</code></pre>`;
813
+ }
814
+ });
815
+ this.addRule({
816
+ type: "link",
817
+ render: (token) => {
818
+ const href = token.attributes?.href || "#";
819
+ const escapedHref = escapeHtml(href);
820
+ const escapedText = escapeHtml(token.content);
821
+ if (this.config.format === "html") {
822
+ return `<a href="${escapedHref}" target="_blank" rel="noopener noreferrer">${escapedText}</a>`;
823
+ }
824
+ return `<a href="${escapedHref}" class="text-primary hover:underline inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
825
+ ${escapedText}
826
+ <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link">
827
+ <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
828
+ <polyline points="15 3 21 3 21 9"></polyline>
829
+ <line x1="10" y1="14" x2="21" y2="3"></line>
830
+ </svg>
831
+ </a>`;
832
+ }
833
+ });
834
+ this.addRule({
835
+ type: "list-item",
836
+ render: (token) => `<li>${escapeHtml(token.content)}</li>`
837
+ });
838
+ this.addRule({
839
+ type: "blockquote",
840
+ render: (token) => {
841
+ const content = escapeHtml(token.content);
842
+ if (this.config.format === "html") {
843
+ return `<blockquote style="border-left: 2px solid #d1d5db; padding: 8px 0 8px 16px; margin: 16px 0; font-style: italic; color: #6b7280;">${content}</blockquote>`;
844
+ }
845
+ return `<blockquote class="pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4">${content}</blockquote>`;
846
+ }
847
+ });
848
+ this.addRule({
849
+ type: "text",
850
+ render: (token) => {
851
+ if (!token.content) return "";
852
+ return escapeHtml(token.content);
853
+ }
854
+ });
855
+ this.addRule({
856
+ type: "paragraph",
857
+ render: (token) => {
858
+ if (!token.content) return "";
859
+ const content = token.content.trim();
860
+ if (!content) return "";
861
+ const processedContent = content.includes("<br>") ? content : escapeHtml(content);
862
+ if (this.config.format === "html") {
863
+ return `<p style="line-height: 1.75; margin-bottom: 16px;">${processedContent}</p>`;
864
+ }
865
+ return `<p class="leading-7 mb-4">${processedContent}</p>`;
866
+ }
867
+ });
868
+ this.addRule({
869
+ type: "task-item",
870
+ render: (token) => {
871
+ const isChecked = token.attributes?.checked === "true";
872
+ const escapedContent = escapeHtml(token.content);
873
+ if (this.config.format === "html") {
874
+ return `<div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
875
+ <input type="checkbox" ${isChecked ? "checked" : ""} disabled style="margin: 0;" />
876
+ <span${isChecked ? ' style="text-decoration: line-through; color: #6b7280;"' : ""}>${escapedContent}</span>
877
+ </div>`;
878
+ }
879
+ return `<div class="flex items-center gap-2 my-2 task-list-item">
880
+ <input type="checkbox" ${isChecked ? "checked" : ""} disabled
881
+ class="form-checkbox h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary" />
882
+ <span${isChecked ? ' class="line-through text-muted-foreground"' : ""}>${escapedContent}</span>
883
+ </div>`;
884
+ }
885
+ });
886
+ this.addRule({
887
+ type: "image",
888
+ render: (token) => {
889
+ const src = token.attributes?.src || "";
890
+ const alt = token.attributes?.alt || "";
891
+ const title = token.attributes?.title || "";
892
+ const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
893
+ if (this.config.format === "html") {
894
+ return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} style="max-width: 100%; height: auto; border-radius: 8px; margin: 16px 0;" loading="lazy" />`;
895
+ }
896
+ return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} class="max-w-full h-auto rounded-lg my-4" loading="lazy" />`;
897
+ }
898
+ });
899
+ this.addRule({
900
+ type: "hr",
901
+ render: () => {
902
+ if (this.config.format === "html") {
903
+ return '<hr style="margin: 24px 0; border: none; border-top: 1px solid #d1d5db;">';
904
+ }
905
+ return '<hr class="my-6 border-t border-border">';
906
+ }
907
+ });
908
+ this.addRule({
909
+ type: "line-break",
910
+ render: () => "<br>"
911
+ });
912
+ this.addRule({
913
+ type: "paragraph-break",
914
+ render: () => "</p><p>"
915
+ });
916
+ this.addRule({
917
+ type: "soft-break",
918
+ render: (token) => token.content || " "
919
+ });
920
+ }
921
+ };
922
+
923
+ // src/extensions/alert.ts
924
+ var AlertExtension = {
925
+ name: "alert",
926
+ parseRules: [
927
+ {
928
+ name: "alert",
929
+ pattern: /:::(\w+)(?:\s+(.*?))?\s*\n([\s\S]*?)\n:::/,
930
+ render: (match) => {
931
+ return {
932
+ type: "alert",
933
+ content: match[3]?.trim() || "",
934
+ raw: match[0] || "",
935
+ attributes: {
936
+ type: match[1] || "info",
937
+ title: match[2] || ""
938
+ }
939
+ };
940
+ }
941
+ }
942
+ ],
943
+ renderRules: [
944
+ {
945
+ type: "alert",
946
+ render: (token) => {
947
+ const type = token.attributes?.type || "info";
948
+ const title = token.attributes?.title || "";
949
+ const typeConfig = {
950
+ info: {
951
+ icon: "\u2139\uFE0F",
952
+ classes: "bg-blue-500/10 border-blue-500/30 text-blue-600 border-l-blue-500"
953
+ },
954
+ warning: {
955
+ icon: "\u26A0\uFE0F",
956
+ classes: "bg-amber-500/10 border-amber-500/30 text-amber-600 border-l-amber-500"
957
+ },
958
+ error: {
959
+ icon: "\u274C",
960
+ classes: "bg-red-500/10 border-red-500/30 text-red-600 border-l-red-500"
961
+ },
962
+ success: {
963
+ icon: "\u2705",
964
+ classes: "bg-green-500/10 border-green-500/30 text-green-600 border-l-green-500"
965
+ },
966
+ tip: {
967
+ icon: "\u{1F4A1}",
968
+ classes: "bg-purple-500/10 border-purple-500/30 text-purple-600 border-l-purple-500"
969
+ },
970
+ note: {
971
+ icon: "\u{1F4DD}",
972
+ classes: "bg-gray-500/10 border-gray-500/30 text-gray-600 border-l-gray-500"
973
+ }
974
+ };
975
+ const config = typeConfig[type] || typeConfig.info;
976
+ const baseClasses = "border-l-4 p-4 mb-4 rounded-md transition-colors duration-200";
977
+ const classes = `${baseClasses} ${config?.classes}`;
978
+ const titleHtml = title ? `<div class="font-medium mb-2 flex items-center gap-2">
979
+ <span class="text-lg" role="img" aria-label="${type}">${config?.icon}</span>
980
+ <span>${title}</span>
981
+ </div>` : `<div class="font-medium mb-2 flex items-center gap-2">
982
+ <span class="text-lg" role="img" aria-label="${type}">${config?.icon}</span>
983
+ <span>${type.charAt(0).toUpperCase() + type.slice(1)}</span>
984
+ </div>`;
985
+ return `<div class="${classes}" role="alert" aria-live="polite">
986
+ ${titleHtml}
987
+ <div class="leading-relaxed">${token.content}</div>
988
+ </div>`;
989
+ }
990
+ }
991
+ ]
992
+ };
993
+
994
+ // src/extensions/button.ts
995
+ var ButtonExtension = {
996
+ name: "button",
997
+ parseRules: [
998
+ {
999
+ name: "button",
1000
+ pattern: /\[button:([^\]]+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
1001
+ render: (match) => {
1002
+ const text = match[1] || "";
1003
+ const href = match[2] || "";
1004
+ const optionsString = match[3] || "";
1005
+ const options = optionsString.split(",").map((opt) => opt.trim()).filter(Boolean);
1006
+ const styleOptions = ["default", "primary", "secondary", "success", "danger", "outline", "ghost"];
1007
+ const style = options.find((opt) => styleOptions.includes(opt)) || "primary";
1008
+ const sizeOptions = ["sm", "md", "lg"];
1009
+ const size = options.find((opt) => sizeOptions.includes(opt)) || "md";
1010
+ const disabled = options.includes("disabled");
1011
+ const target = options.includes("self") ? "_self" : "_blank";
1012
+ return {
1013
+ type: "button",
1014
+ content: text,
1015
+ raw: match[0] || "",
1016
+ attributes: {
1017
+ href,
1018
+ style,
1019
+ size,
1020
+ disabled: disabled.toString(),
1021
+ target
1022
+ }
1023
+ };
1024
+ }
1025
+ }
1026
+ ],
1027
+ renderRules: [
1028
+ {
1029
+ type: "button",
1030
+ render: (token) => {
1031
+ const href = token.attributes?.href || "#";
1032
+ const style = token.attributes?.style || "primary";
1033
+ const size = token.attributes?.size || "md";
1034
+ const disabled = token.attributes?.disabled === "true";
1035
+ const target = token.attributes?.target || "_blank";
1036
+ const text = token.content;
1037
+ const classes = buildButtonClasses(style, size);
1038
+ const targetAttr = target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : target === "_self" ? ' target="_self"' : "";
1039
+ const disabledAttr = disabled ? ' aria-disabled="true" tabindex="-1"' : "";
1040
+ const externalIcon = target === "_blank" && !disabled ? '<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>' : "";
1041
+ return `<a href="${href}" class="${classes}"${targetAttr}${disabledAttr}>
1042
+ ${text}${externalIcon}
1043
+ </a>`;
1044
+ }
1045
+ }
1046
+ ]
1047
+ };
1048
+ function buildButtonClasses(style, size) {
1049
+ const base = [
1050
+ "inline-flex",
1051
+ "items-center",
1052
+ "justify-center",
1053
+ "font-medium",
1054
+ "rounded-lg",
1055
+ "transition-colors",
1056
+ "focus:outline-none",
1057
+ "focus:ring-2",
1058
+ "focus:ring-offset-2",
1059
+ "disabled:opacity-50",
1060
+ "disabled:cursor-not-allowed"
1061
+ ];
1062
+ const sizes = {
1063
+ sm: ["px-3", "py-1.5", "text-sm"],
1064
+ md: ["px-4", "py-2", "text-base"],
1065
+ lg: ["px-6", "py-3", "text-lg"]
1066
+ };
1067
+ const styles = {
1068
+ default: ["bg-slate-600", "text-white", "hover:bg-slate-700", "focus:ring-slate-500"],
1069
+ primary: ["bg-blue-600", "text-white", "hover:bg-blue-700", "focus:ring-blue-500"],
1070
+ secondary: ["bg-gray-600", "text-white", "hover:bg-gray-700", "focus:ring-gray-500"],
1071
+ success: ["bg-green-600", "text-white", "hover:bg-green-700", "focus:ring-green-500"],
1072
+ danger: ["bg-red-600", "text-white", "hover:bg-red-700", "focus:ring-red-500"],
1073
+ outline: ["border", "border-blue-600", "text-blue-600", "hover:bg-blue-50", "focus:ring-blue-500"],
1074
+ ghost: ["text-gray-700", "hover:bg-gray-100", "focus:ring-gray-500"]
1075
+ };
1076
+ const allClasses = [
1077
+ ...base,
1078
+ ...sizes[size] ?? sizes.md ?? [],
1079
+ ...styles[style] ?? styles.primary ?? []
1080
+ ];
1081
+ return allClasses.join(" ");
1082
+ }
1083
+
1084
+ // src/extensions/embed.ts
1085
+ var EmbedExtension = {
1086
+ name: "embed",
1087
+ parseRules: [
1088
+ {
1089
+ name: "embed",
1090
+ pattern: /\[embed:(\w+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
1091
+ render: (match) => {
1092
+ return {
1093
+ type: "embed",
1094
+ content: match[2] || "",
1095
+ // URL
1096
+ raw: match[0] || "",
1097
+ attributes: {
1098
+ provider: match[1] || "generic",
1099
+ url: match[2] || "",
1100
+ options: match[3] || ""
1101
+ }
1102
+ };
1103
+ }
1104
+ }
1105
+ ],
1106
+ renderRules: [
1107
+ {
1108
+ type: "embed",
1109
+ render: (token) => {
1110
+ const provider = token.attributes?.provider || "generic";
1111
+ const url = token.attributes?.url || "";
1112
+ const options = token.attributes?.options || "";
1113
+ return renderEmbed(provider, url, options);
1114
+ }
1115
+ }
1116
+ ]
1117
+ };
1118
+ function renderEmbed(provider, url, options) {
1119
+ const baseClasses = "rounded-lg border bg-card text-card-foreground shadow-sm mb-6 overflow-hidden";
1120
+ const parsedOptions = parseOptions(options);
1121
+ switch (provider.toLowerCase()) {
1122
+ case "youtube":
1123
+ return renderYouTubeEmbed(url, parsedOptions, baseClasses);
1124
+ case "codepen":
1125
+ return renderCodePenEmbed(url, parsedOptions, baseClasses);
1126
+ case "figma":
1127
+ return renderFigmaEmbed(url, parsedOptions, baseClasses);
1128
+ case "twitter":
1129
+ case "tweet":
1130
+ return renderTwitterEmbed(url, parsedOptions, baseClasses);
1131
+ case "github":
1132
+ return renderGitHubEmbed(url, parsedOptions, baseClasses);
1133
+ case "vimeo":
1134
+ return renderVimeoEmbed(url, parsedOptions, baseClasses);
1135
+ case "spotify":
1136
+ return renderSpotifyEmbed(url, parsedOptions, baseClasses);
1137
+ case "codesandbox":
1138
+ return renderCodeSandboxEmbed(url, parsedOptions, baseClasses);
1139
+ default:
1140
+ return renderGenericEmbed(url, parsedOptions, baseClasses);
1141
+ }
1142
+ }
1143
+ function renderYouTubeEmbed(url, options, classes) {
1144
+ const videoId = extractYouTubeId(url);
1145
+ if (!videoId) {
1146
+ return createErrorEmbed("Invalid YouTube URL", url, classes);
1147
+ }
1148
+ const params = new URLSearchParams();
1149
+ if (options.autoplay === "1") params.set("autoplay", "1");
1150
+ if (options.mute === "1") params.set("mute", "1");
1151
+ if (options.loop === "1") {
1152
+ params.set("loop", "1");
1153
+ params.set("playlist", videoId);
1154
+ }
1155
+ if (options.controls === "0") params.set("controls", "0");
1156
+ if (options.start) params.set("start", options.start);
1157
+ params.set("rel", "0");
1158
+ params.set("modestbranding", "1");
1159
+ const embedUrl = `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
1160
+ return `
1161
+ <div class="${classes}">
1162
+ <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
1163
+ <iframe
1164
+ style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
1165
+ src="${embedUrl}"
1166
+ title="YouTube video player"
1167
+ frameborder="0"
1168
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
1169
+ allowfullscreen>
1170
+ </iframe>
1171
+ </div>
1172
+ </div>`;
1173
+ }
1174
+ function renderCodePenEmbed(url, options, classes) {
1175
+ const match = url.match(/codepen\.io\/([^\/]+)\/(?:pen|embed)\/([^\/\?#]+)/);
1176
+ if (!match) {
1177
+ return createErrorEmbed("Invalid CodePen URL", url, classes);
1178
+ }
1179
+ const [, user, penId] = match;
1180
+ const height = options.height || "400";
1181
+ const theme = options.theme === "light" ? "light" : "dark";
1182
+ const defaultTab = options.tab || "result";
1183
+ const embedParams = new URLSearchParams({
1184
+ "default-tab": defaultTab,
1185
+ "theme-id": theme,
1186
+ "editable": "true"
1187
+ });
1188
+ const embedUrl = `https://codepen.io/${user}/embed/${penId}?${embedParams.toString()}`;
1189
+ return `
1190
+ <div class="${classes}">
1191
+ <iframe
1192
+ height="${height}"
1193
+ style="width: 100%; border: 0;"
1194
+ scrolling="no"
1195
+ title="CodePen Embed - ${penId}"
1196
+ src="${embedUrl}"
1197
+ frameborder="0"
1198
+ loading="lazy"
1199
+ allowtransparency="true"
1200
+ allowfullscreen="true">
1201
+ </iframe>
1202
+ </div>`;
1203
+ }
1204
+ function renderVimeoEmbed(url, options, classes) {
1205
+ const videoId = extractVimeoId(url);
1206
+ if (!videoId) {
1207
+ return createErrorEmbed("Invalid Vimeo URL", url, classes);
1208
+ }
1209
+ const params = new URLSearchParams();
1210
+ if (options.autoplay === "1") params.set("autoplay", "1");
1211
+ if (options.mute === "1") params.set("muted", "1");
1212
+ if (options.loop === "1") params.set("loop", "1");
1213
+ const embedUrl = `https://player.vimeo.com/video/${videoId}?${params.toString()}`;
1214
+ return `
1215
+ <div class="${classes}">
1216
+ <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
1217
+ <iframe
1218
+ style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
1219
+ src="${embedUrl}"
1220
+ title="Vimeo video player"
1221
+ frameborder="0"
1222
+ allow="autoplay; fullscreen; picture-in-picture"
1223
+ allowfullscreen>
1224
+ </iframe>
1225
+ </div>
1226
+ </div>`;
1227
+ }
1228
+ function renderSpotifyEmbed(url, options, classes) {
1229
+ const embedUrl = url.replace("open.spotify.com", "open.spotify.com/embed");
1230
+ const height = options.height || "380";
1231
+ return `
1232
+ <div class="${classes}">
1233
+ <iframe
1234
+ style="border-radius: 12px;"
1235
+ src="${embedUrl}"
1236
+ width="100%"
1237
+ height="${height}"
1238
+ frameborder="0"
1239
+ allowfullscreen=""
1240
+ allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
1241
+ loading="lazy">
1242
+ </iframe>
1243
+ </div>`;
1244
+ }
1245
+ function renderCodeSandboxEmbed(url, options, classes) {
1246
+ let embedUrl = url;
1247
+ if (url.includes("/s/")) {
1248
+ embedUrl = url.replace("/s/", "/embed/");
1249
+ }
1250
+ const height = options.height || "500";
1251
+ const view = options.view || "preview";
1252
+ if (!embedUrl.includes("?")) {
1253
+ embedUrl += `?view=${view}`;
1254
+ }
1255
+ return `
1256
+ <div class="${classes}">
1257
+ <iframe
1258
+ src="${embedUrl}"
1259
+ style="width: 100%; height: ${height}px; border: 0; border-radius: 4px; overflow: hidden;"
1260
+ title="CodeSandbox Embed"
1261
+ allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
1262
+ sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts">
1263
+ </iframe>
1264
+ </div>`;
1265
+ }
1266
+ function renderFigmaEmbed(url, options, classes) {
1267
+ const embedUrl = `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
1268
+ const height = options.height || "450";
1269
+ return `
1270
+ <div class="${classes}">
1271
+ <iframe
1272
+ style="border: none;"
1273
+ width="100%"
1274
+ height="${height}"
1275
+ src="${embedUrl}"
1276
+ allowfullscreen>
1277
+ </iframe>
1278
+ </div>`;
1279
+ }
1280
+ function renderTwitterEmbed(url, _options, classes) {
1281
+ return `
1282
+ <div class="${classes}">
1283
+ <div class="p-4">
1284
+ <div class="flex items-center gap-3 mb-3">
1285
+ <svg class="w-6 h-6 fill-current text-blue-500" viewBox="0 0 24 24">
1286
+ <path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
1287
+ </svg>
1288
+ <div>
1289
+ <div class="font-semibold text-foreground">Twitter Post</div>
1290
+ <div class="text-sm text-muted-foreground">External Link</div>
1291
+ </div>
1292
+ </div>
1293
+ <a href="${url}" target="_blank"
1294
+ class="inline-flex items-center gap-2 text-primary hover:text-primary/80 font-medium transition-colors">
1295
+ View on Twitter
1296
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1297
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
1298
+ </svg>
1299
+ </a>
1300
+ </div>
1301
+ </div>`;
1302
+ }
1303
+ function renderGitHubEmbed(url, _options, classes) {
1304
+ const parts = url.replace("https://github.com/", "").split("/");
1305
+ const owner = parts[0];
1306
+ const repo = parts[1];
1307
+ if (!owner || !repo) {
1308
+ return createErrorEmbed("Invalid GitHub URL", url, classes);
1309
+ }
1310
+ return `
1311
+ <div class="${classes}">
1312
+ <div class="p-4">
1313
+ <div class="flex items-center gap-3 mb-3">
1314
+ <svg class="w-6 h-6 fill-current" viewBox="0 0 24 24">
1315
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
1316
+ </svg>
1317
+ <div>
1318
+ <div class="font-semibold text-foreground text-lg">${owner}/${repo}</div>
1319
+ <div class="text-sm text-muted-foreground">GitHub Repository</div>
1320
+ </div>
1321
+ </div>
1322
+ <a href="${url}" target="_blank"
1323
+ class="inline-flex items-center gap-2 text-primary hover:text-primary/80 font-medium transition-colors">
1324
+ View on GitHub
1325
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1326
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
1327
+ </svg>
1328
+ </a>
1329
+ </div>
1330
+ </div>`;
1331
+ }
1332
+ function renderGenericEmbed(url, _options, classes) {
1333
+ const domain = extractDomain(url);
1334
+ return `
1335
+ <div class="${classes}">
1336
+ <div class="p-4">
1337
+ <div class="flex items-center gap-3 mb-3">
1338
+ <div class="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
1339
+ <svg class="w-5 h-5 text-muted-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1340
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
1341
+ </svg>
1342
+ </div>
1343
+ <div>
1344
+ <div class="font-semibold text-foreground">External Link</div>
1345
+ <div class="text-sm text-muted-foreground">${domain}</div>
1346
+ </div>
1347
+ </div>
1348
+ <a href="${url}" target="_blank"
1349
+ class="inline-flex items-center gap-2 text-primary hover:text-primary/80 font-medium transition-colors break-all">
1350
+ ${url}
1351
+ <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1352
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
1353
+ </svg>
1354
+ </a>
1355
+ </div>
1356
+ </div>`;
1357
+ }
1358
+ function createErrorEmbed(error, url, classes) {
1359
+ return `
1360
+ <div class="${classes}">
1361
+ <div class="p-4 text-destructive">
1362
+ <div class="font-medium flex items-center gap-2">
1363
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1364
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
1365
+ </svg>
1366
+ ${error}
1367
+ </div>
1368
+ <div class="text-sm text-muted-foreground mt-1 break-all">${url}</div>
1369
+ </div>
1370
+ </div>`;
1371
+ }
1372
+ function extractYouTubeId(url) {
1373
+ const patterns = [
1374
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/)([^&\n?#]+)/,
1375
+ /youtube\.com\/watch\?.*v=([^&\n?#]+)/
1376
+ ];
1377
+ for (const pattern of patterns) {
1378
+ const match = url.match(pattern);
1379
+ if (match) return match[1] || null;
1380
+ }
1381
+ return null;
1382
+ }
1383
+ function extractVimeoId(url) {
1384
+ const match = url.match(/vimeo\.com\/(?:.*\/)?(\d+)/);
1385
+ return match?.[1] ?? null;
1386
+ }
1387
+
1388
+ // src/engine.ts
1389
+ var ChangerawrMarkdown = class {
1390
+ constructor(config) {
1391
+ this.extensions = /* @__PURE__ */ new Map();
1392
+ this.parser = new MarkdownParser(config?.parser);
1393
+ this.renderer = new MarkdownRenderer(config?.renderer);
1394
+ this.registerBuiltInExtensions();
1395
+ if (config?.extensions) {
1396
+ config.extensions.forEach((extension) => {
1397
+ this.registerExtension(extension);
1398
+ });
1399
+ }
1400
+ this.parser.setupDefaultRulesIfEmpty();
1401
+ }
1402
+ /**
1403
+ * Register a custom extension
1404
+ */
1405
+ registerExtension(extension) {
1406
+ try {
1407
+ this.extensions.set(extension.name, extension);
1408
+ extension.parseRules.forEach((rule) => {
1409
+ this.parser.addRule(rule);
1410
+ });
1411
+ extension.renderRules.forEach((rule) => {
1412
+ this.renderer.addRule(rule);
1413
+ });
1414
+ return {
1415
+ success: true,
1416
+ extensionName: extension.name
1417
+ };
1418
+ } catch (error) {
1419
+ const errorMessage = error instanceof Error ? error.message : String(error);
1420
+ return {
1421
+ success: false,
1422
+ extensionName: extension.name,
1423
+ error: errorMessage
1424
+ };
1425
+ }
1426
+ }
1427
+ /**
1428
+ * Unregister an extension
1429
+ */
1430
+ unregisterExtension(name) {
1431
+ const extension = this.extensions.get(name);
1432
+ if (!extension) {
1433
+ return false;
1434
+ }
1435
+ try {
1436
+ this.extensions.delete(name);
1437
+ this.rebuildParserAndRenderer();
1438
+ return true;
1439
+ } catch {
1440
+ return false;
1441
+ }
1442
+ }
1443
+ /**
1444
+ * Parse markdown content into tokens
1445
+ */
1446
+ parse(markdown3) {
1447
+ return this.parser.parse(markdown3);
1448
+ }
1449
+ /**
1450
+ * Render tokens to HTML
1451
+ */
1452
+ render(tokens) {
1453
+ return this.renderer.render(tokens);
1454
+ }
1455
+ /**
1456
+ * Parse and render markdown to HTML in one step
1457
+ */
1458
+ toHtml(markdown3) {
1459
+ const tokens = this.parse(markdown3);
1460
+ return this.render(tokens);
1461
+ }
1462
+ /**
1463
+ * Get list of registered extensions
1464
+ */
1465
+ getExtensions() {
1466
+ return Array.from(this.extensions.keys());
1467
+ }
1468
+ /**
1469
+ * Check if extension is registered
1470
+ */
1471
+ hasExtension(name) {
1472
+ return this.extensions.has(name);
1473
+ }
1474
+ /**
1475
+ * Get parser warnings
1476
+ */
1477
+ getWarnings() {
1478
+ return [...this.parser.getWarnings(), ...this.renderer.getWarnings()];
1479
+ }
1480
+ /**
1481
+ * Get debug information from last render
1482
+ */
1483
+ getDebugInfo() {
1484
+ return {
1485
+ warnings: this.getWarnings(),
1486
+ parseTime: 0,
1487
+ renderTime: 0,
1488
+ tokenCount: 0,
1489
+ iterationCount: 0
1490
+ };
1491
+ }
1492
+ /**
1493
+ * Get performance metrics for the last operation
1494
+ */
1495
+ getPerformanceMetrics() {
1496
+ return {
1497
+ parseTime: 0,
1498
+ renderTime: 0,
1499
+ totalTime: 0,
1500
+ tokenCount: 0
1501
+ };
1502
+ }
1503
+ /**
1504
+ * Register built-in extensions
1505
+ */
1506
+ registerBuiltInExtensions() {
1507
+ this.registerExtension(AlertExtension);
1508
+ this.registerExtension(ButtonExtension);
1509
+ this.registerExtension(EmbedExtension);
1510
+ }
1511
+ /**
1512
+ * Rebuild parser and renderer with current extensions
1513
+ */
1514
+ rebuildParserAndRenderer() {
1515
+ const parserConfig = this.parser.getConfig();
1516
+ const rendererConfig = this.renderer.getConfig();
1517
+ this.parser = new MarkdownParser(parserConfig);
1518
+ this.renderer = new MarkdownRenderer(rendererConfig);
1519
+ const extensionsToRegister = Array.from(this.extensions.values());
1520
+ this.extensions.clear();
1521
+ extensionsToRegister.forEach((extension) => {
1522
+ this.registerExtension(extension);
1523
+ });
1524
+ this.parser.setupDefaultRulesIfEmpty();
1525
+ }
1526
+ };
1527
+ var markdown = new ChangerawrMarkdown();
1528
+ function parseMarkdown(content) {
1529
+ return markdown.parse(content);
1530
+ }
1531
+ function renderMarkdown(content) {
1532
+ return markdown.toHtml(content);
1533
+ }
1534
+
1535
+ // src/outputs/html.ts
1536
+ function renderToHTML(markdown3, config) {
1537
+ const engine = new ChangerawrMarkdown({
1538
+ ...config,
1539
+ renderer: {
1540
+ format: "html",
1541
+ sanitize: true,
1542
+ allowUnsafeHtml: false
1543
+ }
1544
+ });
1545
+ return engine.toHtml(markdown3);
1546
+ }
1547
+ function renderToHTMLWithConfig(markdown3, rendererConfig) {
1548
+ const engine = new ChangerawrMarkdown({
1549
+ renderer: {
1550
+ format: "html",
1551
+ ...rendererConfig
1552
+ }
1553
+ });
1554
+ return engine.toHtml(markdown3);
1555
+ }
1556
+ function renderToHTMLUnsafe(markdown3) {
1557
+ return renderToHTMLWithConfig(markdown3, {
1558
+ sanitize: false,
1559
+ allowUnsafeHtml: true
1560
+ });
1561
+ }
1562
+
1563
+ // src/outputs/json.ts
1564
+ function renderToJSON(markdown3, config) {
1565
+ const engine = new ChangerawrMarkdown(config);
1566
+ return engine.parse(markdown3);
1567
+ }
1568
+ function renderToAST(markdown3, config) {
1569
+ const tokens = renderToJSON(markdown3, config);
1570
+ return tokensToAST(tokens);
1571
+ }
1572
+ function tokensToAST(tokens) {
1573
+ const ast = [];
1574
+ let currentList = null;
1575
+ let currentBlockquote = null;
1576
+ for (let i = 0; i < tokens.length; i++) {
1577
+ const token = tokens[i];
1578
+ if (!token) continue;
1579
+ const node = {
1580
+ type: token.type,
1581
+ content: token.content,
1582
+ ...token.attributes && { attributes: token.attributes }
1583
+ };
1584
+ if (token.type === "list-item" || token.type === "task-item") {
1585
+ if (!currentList) {
1586
+ currentList = {
1587
+ type: token.type === "task-item" ? "task-list" : "list",
1588
+ content: "",
1589
+ children: []
1590
+ };
1591
+ ast.push(currentList);
1592
+ }
1593
+ if (!currentList.children) {
1594
+ currentList.children = [];
1595
+ }
1596
+ currentList.children.push(node);
1597
+ continue;
1598
+ } else {
1599
+ currentList = null;
1600
+ }
1601
+ if (token.type === "blockquote") {
1602
+ if (!currentBlockquote) {
1603
+ currentBlockquote = {
1604
+ type: "blockquote-group",
1605
+ content: "",
1606
+ children: []
1607
+ };
1608
+ ast.push(currentBlockquote);
1609
+ }
1610
+ if (!currentBlockquote.children) {
1611
+ currentBlockquote.children = [];
1612
+ }
1613
+ currentBlockquote.children.push(node);
1614
+ continue;
1615
+ } else {
1616
+ currentBlockquote = null;
1617
+ }
1618
+ if (isParagraphContent(token)) {
1619
+ const paragraphTokens = [token];
1620
+ let j = i + 1;
1621
+ while (j < tokens.length && tokens[j] && isParagraphContent(tokens[j])) {
1622
+ paragraphTokens.push(tokens[j]);
1623
+ j++;
1624
+ }
1625
+ if (paragraphTokens.length > 1 || token.type === "text") {
1626
+ const paragraphNode = {
1627
+ type: "paragraph",
1628
+ content: paragraphTokens.map((t) => t.content).join(""),
1629
+ children: paragraphTokens.map((t) => ({
1630
+ type: t.type,
1631
+ content: t.content,
1632
+ ...t.attributes && { attributes: t.attributes }
1633
+ }))
1634
+ };
1635
+ ast.push(paragraphNode);
1636
+ i = j - 1;
1637
+ continue;
1638
+ }
1639
+ }
1640
+ ast.push(node);
1641
+ }
1642
+ return ast;
1643
+ }
1644
+ function astToTokens(ast) {
1645
+ const tokens = [];
1646
+ function processNode(node) {
1647
+ if (node.children && node.children.length > 0) {
1648
+ if (node.type === "list" || node.type === "task-list" || node.type === "blockquote-group") {
1649
+ node.children.forEach(processNode);
1650
+ return;
1651
+ }
1652
+ if (node.type === "paragraph") {
1653
+ node.children.forEach((child, index) => {
1654
+ const token2 = {
1655
+ type: child.type,
1656
+ content: child.content,
1657
+ raw: child.content,
1658
+ ...child.attributes && { attributes: child.attributes }
1659
+ };
1660
+ tokens.push(token2);
1661
+ if (index < node.children.length - 1 && isInlineToken(child)) {
1662
+ tokens.push({
1663
+ type: "soft-break",
1664
+ content: " ",
1665
+ raw: " "
1666
+ });
1667
+ }
1668
+ });
1669
+ tokens.push({
1670
+ type: "paragraph-break",
1671
+ content: "",
1672
+ raw: "\n\n"
1673
+ });
1674
+ return;
1675
+ }
1676
+ }
1677
+ const token = {
1678
+ type: node.type,
1679
+ content: node.content,
1680
+ raw: node.content,
1681
+ ...node.attributes && { attributes: node.attributes }
1682
+ };
1683
+ tokens.push(token);
1684
+ }
1685
+ ast.forEach(processNode);
1686
+ return tokens;
1687
+ }
1688
+ function tokensToJSONString(tokens, pretty = true) {
1689
+ if (pretty) {
1690
+ return JSON.stringify(tokens, null, 2);
1691
+ }
1692
+ return JSON.stringify(tokens);
1693
+ }
1694
+ function astToJSONString(ast, pretty = true) {
1695
+ if (pretty) {
1696
+ return JSON.stringify(ast, null, 2);
1697
+ }
1698
+ return JSON.stringify(ast);
1699
+ }
1700
+ function parseTokensFromJSON(jsonString) {
1701
+ try {
1702
+ const parsed = JSON.parse(jsonString);
1703
+ if (!Array.isArray(parsed)) {
1704
+ throw new Error("JSON must be an array of tokens");
1705
+ }
1706
+ for (const token of parsed) {
1707
+ if (!token.type || typeof token.type !== "string") {
1708
+ throw new Error("Invalid token structure: missing or invalid type");
1709
+ }
1710
+ if (token.content === void 0 && token.raw === void 0) {
1711
+ throw new Error("Invalid token structure: missing content and raw");
1712
+ }
1713
+ }
1714
+ return parsed;
1715
+ } catch (error) {
1716
+ const message = error instanceof Error ? error.message : String(error);
1717
+ throw new Error(`Failed to parse tokens from JSON: ${message}`);
1718
+ }
1719
+ }
1720
+ function parseASTFromJSON(jsonString) {
1721
+ try {
1722
+ const parsed = JSON.parse(jsonString);
1723
+ if (!Array.isArray(parsed)) {
1724
+ throw new Error("JSON must be an array of AST nodes");
1725
+ }
1726
+ for (const node of parsed) {
1727
+ if (!node.type || typeof node.type !== "string") {
1728
+ throw new Error("Invalid AST node: missing or invalid type");
1729
+ }
1730
+ }
1731
+ return parsed;
1732
+ } catch (error) {
1733
+ const message = error instanceof Error ? error.message : String(error);
1734
+ throw new Error(`Failed to parse AST from JSON: ${message}`);
1735
+ }
1736
+ }
1737
+ function getTokenStats(tokens) {
1738
+ const tokenTypes = {};
1739
+ const extensionTokens = {};
1740
+ let totalContent = 0;
1741
+ let totalRaw = 0;
1742
+ let tokensWithAttributes = 0;
1743
+ for (const token of tokens) {
1744
+ tokenTypes[token.type] = (tokenTypes[token.type] || 0) + 1;
1745
+ if (["alert", "button", "embed"].includes(token.type)) {
1746
+ extensionTokens[token.type] = (extensionTokens[token.type] || 0) + 1;
1747
+ }
1748
+ totalContent += (token.content || "").length;
1749
+ totalRaw += (token.raw || "").length;
1750
+ if (token.attributes && Object.keys(token.attributes).length > 0) {
1751
+ tokensWithAttributes++;
1752
+ }
1753
+ }
1754
+ const totalTokens = tokens.length;
1755
+ return {
1756
+ totalTokens,
1757
+ tokenTypes,
1758
+ extensionTokens,
1759
+ totalContent,
1760
+ totalRaw,
1761
+ tokensWithAttributes,
1762
+ averageContentLength: totalTokens > 0 ? Math.round(totalContent / totalTokens) : 0,
1763
+ averageRawLength: totalTokens > 0 ? Math.round(totalRaw / totalTokens) : 0,
1764
+ attributeUsageRate: totalTokens > 0 ? Math.round(tokensWithAttributes / totalTokens * 100) : 0
1765
+ };
1766
+ }
1767
+ function getASTStats(ast) {
1768
+ let totalNodes = 0;
1769
+ let maxDepth = 0;
1770
+ let nodesWithChildren = 0;
1771
+ let totalChildren = 0;
1772
+ const nodeTypes = {};
1773
+ function analyzeNode(node, depth = 0) {
1774
+ totalNodes++;
1775
+ maxDepth = Math.max(maxDepth, depth);
1776
+ nodeTypes[node.type] = (nodeTypes[node.type] || 0) + 1;
1777
+ if (node.children && node.children.length > 0) {
1778
+ nodesWithChildren++;
1779
+ totalChildren += node.children.length;
1780
+ node.children.forEach((child) => analyzeNode(child, depth + 1));
1781
+ }
1782
+ }
1783
+ ast.forEach((node) => analyzeNode(node));
1784
+ return {
1785
+ totalNodes,
1786
+ maxDepth,
1787
+ nodesWithChildren,
1788
+ totalChildren,
1789
+ nodeTypes,
1790
+ averageChildrenPerParent: nodesWithChildren > 0 ? Math.round(totalChildren / nodesWithChildren) : 0,
1791
+ hierarchyComplexity: maxDepth > 0 ? Math.round(totalChildren / totalNodes * maxDepth) : 0
1792
+ };
1793
+ }
1794
+ function compareTokens(tokensA, tokensB) {
1795
+ const differences = [];
1796
+ const maxLength = Math.max(tokensA.length, tokensB.length);
1797
+ for (let i = 0; i < maxLength; i++) {
1798
+ const tokenA = tokensA[i];
1799
+ const tokenB = tokensB[i];
1800
+ if (!tokenA && tokenB) {
1801
+ differences.push({
1802
+ index: i,
1803
+ type: "added",
1804
+ tokenB
1805
+ });
1806
+ } else if (tokenA && !tokenB) {
1807
+ differences.push({
1808
+ index: i,
1809
+ type: "removed",
1810
+ tokenA
1811
+ });
1812
+ } else if (tokenA && tokenB) {
1813
+ if (tokenA.type !== tokenB.type || tokenA.content !== tokenB.content) {
1814
+ differences.push({
1815
+ index: i,
1816
+ type: "modified",
1817
+ tokenA,
1818
+ tokenB
1819
+ });
1820
+ }
1821
+ }
1822
+ }
1823
+ return {
1824
+ identical: differences.length === 0,
1825
+ differences,
1826
+ addedCount: differences.filter((d) => d.type === "added").length,
1827
+ removedCount: differences.filter((d) => d.type === "removed").length,
1828
+ modifiedCount: differences.filter((d) => d.type === "modified").length
1829
+ };
1830
+ }
1831
+ function isParagraphContent(token) {
1832
+ return ["text", "bold", "italic", "code", "link", "soft-break"].includes(token.type);
1833
+ }
1834
+ function isInlineToken(node) {
1835
+ return ["bold", "italic", "code", "link"].includes(node.type);
1836
+ }
1837
+
1838
+ // src/outputs/tailwind.ts
1839
+ function renderToTailwind(markdown3, config) {
1840
+ const engine = new ChangerawrMarkdown({
1841
+ ...config,
1842
+ renderer: {
1843
+ format: "tailwind",
1844
+ sanitize: true,
1845
+ allowUnsafeHtml: false
1846
+ }
1847
+ });
1848
+ return engine.toHtml(markdown3);
1849
+ }
1850
+ function renderToTailwindWithClasses(markdown3, customClasses) {
1851
+ const engine = new ChangerawrMarkdown({
1852
+ renderer: {
1853
+ format: "tailwind",
1854
+ ...customClasses && { customClasses }
1855
+ }
1856
+ });
1857
+ return engine.toHtml(markdown3);
1858
+ }
1859
+ function renderToTailwindWithConfig(markdown3, rendererConfig) {
1860
+ const engine = new ChangerawrMarkdown({
1861
+ renderer: {
1862
+ format: "tailwind",
1863
+ ...rendererConfig
1864
+ }
1865
+ });
1866
+ return engine.toHtml(markdown3);
1867
+ }
1868
+ var defaultTailwindClasses = {
1869
+ // Typography
1870
+ "heading-1": "text-3xl font-bold mt-8 mb-4",
1871
+ "heading-2": "text-2xl font-semibold mt-6 mb-3",
1872
+ "heading-3": "text-xl font-medium mt-5 mb-3",
1873
+ "heading-4": "text-lg font-medium mt-4 mb-2",
1874
+ "heading-5": "text-base font-medium mt-3 mb-2",
1875
+ "heading-6": "text-sm font-medium mt-3 mb-2",
1876
+ "paragraph": "leading-7 mb-4",
1877
+ "blockquote": "pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4",
1878
+ // Code
1879
+ "code-inline": "bg-muted px-1.5 py-0.5 rounded text-sm font-mono",
1880
+ "code-block": "bg-muted p-4 rounded-md overflow-x-auto my-4",
1881
+ // Links and buttons
1882
+ "link": "text-primary hover:underline inline-flex items-center gap-1",
1883
+ "button": "inline-flex items-center justify-center font-medium rounded-lg transition-all duration-200",
1884
+ // Lists
1885
+ "list": "list-disc list-inside space-y-1",
1886
+ "list-item": "ml-4",
1887
+ // Images and media
1888
+ "image": "max-w-full h-auto rounded-lg my-4",
1889
+ "embed": "rounded-lg border bg-card text-card-foreground shadow-sm mb-6 overflow-hidden",
1890
+ // Alerts
1891
+ "alert": "border-l-4 p-4 mb-4 rounded-md transition-colors duration-200",
1892
+ "alert-info": "bg-blue-500/10 border-blue-500/30 text-blue-600 border-l-blue-500",
1893
+ "alert-warning": "bg-amber-500/10 border-amber-500/30 text-amber-600 border-l-amber-500",
1894
+ "alert-error": "bg-red-500/10 border-red-500/30 text-red-600 border-l-red-500",
1895
+ "alert-success": "bg-green-500/10 border-green-500/30 text-green-600 border-l-green-500"
1896
+ };
1897
+ var proseClasses = {
1898
+ "heading-1": "text-4xl font-bold tracking-tight mt-10 mb-6",
1899
+ "heading-2": "text-3xl font-semibold tracking-tight mt-8 mb-4",
1900
+ "heading-3": "text-2xl font-medium tracking-tight mt-6 mb-3",
1901
+ "paragraph": "text-lg leading-8 mb-6",
1902
+ "blockquote": "border-l-4 border-gray-300 pl-6 py-2 italic text-gray-700 my-6",
1903
+ "code-inline": "bg-gray-100 text-gray-800 px-2 py-1 rounded text-sm font-mono",
1904
+ "code-block": "bg-gray-900 text-gray-100 p-6 rounded-lg overflow-x-auto my-6 text-sm",
1905
+ "link": "text-blue-600 hover:text-blue-800 underline decoration-2 underline-offset-2"
1906
+ };
1907
+ var minimalClasses = {
1908
+ "heading-1": "text-2xl font-semibold mb-4",
1909
+ "heading-2": "text-xl font-medium mb-3",
1910
+ "heading-3": "text-lg font-medium mb-2",
1911
+ "paragraph": "mb-4",
1912
+ "blockquote": "border-l-2 border-gray-300 pl-4 italic mb-4",
1913
+ "code-inline": "bg-gray-100 px-1 rounded font-mono text-sm",
1914
+ "code-block": "bg-gray-100 p-3 rounded font-mono text-sm mb-4",
1915
+ "link": "text-blue-600 hover:underline"
1916
+ };
1917
+
1918
+ // src/standalone.ts
1919
+ function renderCum(markdown3, config) {
1920
+ const engine = new ChangerawrMarkdown(config);
1921
+ return engine.toHtml(markdown3);
1922
+ }
1923
+ function parseCum(markdown3, config) {
1924
+ const engine = new ChangerawrMarkdown(config);
1925
+ return engine.parse(markdown3);
1926
+ }
1927
+ function createCumEngine(config) {
1928
+ return new ChangerawrMarkdown(config);
1929
+ }
1930
+ function renderCumToHtml(markdown3) {
1931
+ return renderCum(markdown3, {
1932
+ renderer: { format: "html" }
1933
+ });
1934
+ }
1935
+ function renderCumToTailwind(markdown3) {
1936
+ return renderCum(markdown3, {
1937
+ renderer: { format: "tailwind" }
1938
+ });
1939
+ }
1940
+ function renderCumToJson(markdown3) {
1941
+ return parseCum(markdown3);
1942
+ }
1943
+ if (typeof window !== "undefined") {
1944
+ window.ChangerawrMarkdown = {
1945
+ renderCum,
1946
+ parseCum,
1947
+ createCumEngine,
1948
+ renderCumToHtml,
1949
+ renderCumToTailwind,
1950
+ renderCumToJson,
1951
+ // Legacy aliases
1952
+ render: renderCum,
1953
+ parse: parseCum
1954
+ };
1955
+ }
1956
+
1957
+ // src/index.ts
1958
+ function createEngine(config) {
1959
+ return new ChangerawrMarkdown(config);
1960
+ }
1961
+ function createHTMLEngine(config) {
1962
+ return new ChangerawrMarkdown({
1963
+ ...config,
1964
+ renderer: {
1965
+ format: "html",
1966
+ sanitize: true,
1967
+ allowUnsafeHtml: false,
1968
+ ...config?.parser
1969
+ }
1970
+ });
1971
+ }
1972
+ function createTailwindEngine(config) {
1973
+ return new ChangerawrMarkdown({
1974
+ ...config,
1975
+ renderer: {
1976
+ format: "tailwind",
1977
+ sanitize: true,
1978
+ allowUnsafeHtml: false,
1979
+ ...config?.parser
1980
+ }
1981
+ });
1982
+ }
1983
+ function createDebugEngine(config) {
1984
+ return new ChangerawrMarkdown({
1985
+ ...config,
1986
+ parser: {
1987
+ debugMode: true,
1988
+ validateMarkdown: true,
1989
+ ...config?.parser
1990
+ },
1991
+ renderer: {
1992
+ format: "tailwind",
1993
+ debugMode: true,
1994
+ ...config?.renderer
1995
+ }
1996
+ });
1997
+ }
1998
+ function createMinimalEngine(config) {
1999
+ const engine = new ChangerawrMarkdown({
2000
+ ...config,
2001
+ extensions: []
2002
+ // Override to prevent built-in extensions
2003
+ });
2004
+ if (engine.hasExtension("alert")) {
2005
+ engine.unregisterExtension("alert");
2006
+ }
2007
+ if (engine.hasExtension("button")) {
2008
+ engine.unregisterExtension("button");
2009
+ }
2010
+ if (engine.hasExtension("embed")) {
2011
+ engine.unregisterExtension("embed");
2012
+ }
2013
+ return engine;
2014
+ }
2015
+ function createCustomEngine(extensions, config) {
2016
+ return new ChangerawrMarkdown({
2017
+ ...config,
2018
+ extensions
2019
+ });
2020
+ }
2021
+ var markdown2 = {
2022
+ // Core rendering functions
2023
+ render: renderMarkdown,
2024
+ parse: parseMarkdown,
2025
+ // Output format functions
2026
+ toHtml: renderToHTML,
2027
+ toTailwind: renderToTailwind,
2028
+ toJson: renderToJSON,
2029
+ toAst: renderToAST,
2030
+ // Engine creation
2031
+ createEngine,
2032
+ createHTMLEngine,
2033
+ createTailwindEngine,
2034
+ createDebugEngine,
2035
+ createMinimalEngine,
2036
+ createCustomEngine,
2037
+ // Standalone functions
2038
+ renderCum,
2039
+ parseCum,
2040
+ // Main class
2041
+ ChangerawrMarkdown,
2042
+ // Extensions
2043
+ extensions: {
2044
+ Alert: AlertExtension,
2045
+ Button: ButtonExtension,
2046
+ Embed: EmbedExtension
2047
+ },
2048
+ // Utilities
2049
+ utils: {
2050
+ escapeHtml,
2051
+ generateId,
2052
+ sanitizeHtml,
2053
+ extractDomain,
2054
+ parseOptions,
2055
+ PerformanceTimer,
2056
+ Logger
2057
+ }
2058
+ };
2059
+ var index_default = markdown2;
2060
+ var presets = {
2061
+ /**
2062
+ * Blog/article preset with prose-friendly styling
2063
+ */
2064
+ blog: {
2065
+ renderer: {
2066
+ format: "tailwind",
2067
+ customClasses: {
2068
+ "heading-1": "text-4xl font-bold tracking-tight mt-10 mb-6",
2069
+ "heading-2": "text-3xl font-semibold tracking-tight mt-8 mb-4",
2070
+ "heading-3": "text-2xl font-medium tracking-tight mt-6 mb-3",
2071
+ "paragraph": "text-lg leading-8 mb-6",
2072
+ "blockquote": "border-l-4 border-gray-300 pl-6 py-2 italic text-gray-700 my-6",
2073
+ "code-inline": "bg-gray-100 text-gray-800 px-2 py-1 rounded text-sm font-mono",
2074
+ "code-block": "bg-gray-900 text-gray-100 p-6 rounded-lg overflow-x-auto my-6 text-sm"
2075
+ }
2076
+ }
2077
+ },
2078
+ /**
2079
+ * Documentation preset with clean, technical styling
2080
+ */
2081
+ docs: {
2082
+ renderer: {
2083
+ format: "tailwind",
2084
+ customClasses: {
2085
+ "heading-1": "text-3xl font-bold border-b border-gray-200 pb-2 mb-6",
2086
+ "heading-2": "text-2xl font-semibold mt-8 mb-4",
2087
+ "heading-3": "text-xl font-medium mt-6 mb-3",
2088
+ "paragraph": "leading-7 mb-4",
2089
+ "code-inline": "bg-blue-50 text-blue-800 px-2 py-1 rounded text-sm font-mono",
2090
+ "code-block": "bg-gray-50 border border-gray-200 p-4 rounded-lg overflow-x-auto my-4 text-sm",
2091
+ "alert": "border border-blue-200 bg-blue-50 text-blue-800 p-4 rounded-lg mb-4"
2092
+ }
2093
+ }
2094
+ },
2095
+ /**
2096
+ * Minimal preset with basic styling
2097
+ */
2098
+ minimal: {
2099
+ renderer: {
2100
+ format: "html",
2101
+ sanitize: true
2102
+ }
2103
+ },
2104
+ /**
2105
+ * Performance preset with minimal processing
2106
+ */
2107
+ fast: {
2108
+ parser: {
2109
+ validateMarkdown: false,
2110
+ maxIterations: 1e3
2111
+ },
2112
+ renderer: {
2113
+ format: "html",
2114
+ sanitize: false
2115
+ }
2116
+ }
2117
+ };
2118
+ function createEngineWithPreset(presetName, additionalConfig) {
2119
+ const preset = presets[presetName];
2120
+ return new ChangerawrMarkdown({
2121
+ ...preset,
2122
+ ...additionalConfig
2123
+ });
2124
+ }
2125
+ export {
2126
+ AlertExtension,
2127
+ ButtonExtension,
2128
+ ChangerawrMarkdown,
2129
+ EmbedExtension,
2130
+ Logger,
2131
+ MarkdownParser,
2132
+ MarkdownRenderer,
2133
+ PerformanceTimer,
2134
+ astToJSONString,
2135
+ astToTokens,
2136
+ basicSanitize,
2137
+ compareTokens,
2138
+ createCumEngine,
2139
+ createCustomEngine,
2140
+ createDebugEngine,
2141
+ createEngine,
2142
+ createEngineWithPreset,
2143
+ createHTMLEngine,
2144
+ createMinimalEngine,
2145
+ createTailwindEngine,
2146
+ debounce,
2147
+ deepMerge,
2148
+ index_default as default,
2149
+ defaultTailwindClasses,
2150
+ escapeHtml,
2151
+ extractDomain,
2152
+ generateId,
2153
+ getASTStats,
2154
+ getTokenStats,
2155
+ isBrowser,
2156
+ isNode,
2157
+ isValidUrl,
2158
+ markdown2 as markdown,
2159
+ minimalClasses,
2160
+ parseASTFromJSON,
2161
+ parseCum,
2162
+ parseMarkdown,
2163
+ parseOptions,
2164
+ parseTokensFromJSON,
2165
+ presets,
2166
+ proseClasses,
2167
+ renderCum,
2168
+ renderCumToHtml,
2169
+ renderCumToJson,
2170
+ renderCumToTailwind,
2171
+ renderMarkdown,
2172
+ renderToAST,
2173
+ renderToHTML,
2174
+ renderToHTMLUnsafe,
2175
+ renderToHTMLWithConfig,
2176
+ renderToJSON,
2177
+ renderToTailwind,
2178
+ renderToTailwindWithClasses,
2179
+ renderToTailwindWithConfig,
2180
+ sanitizeHtml,
2181
+ tokensToAST,
2182
+ tokensToJSONString
2183
+ };
2184
+ //# sourceMappingURL=index.mjs.map