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