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