@changerawr/markdown 1.0.4 → 1.1.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/LICENSE +21 -0
- package/README.md +129 -11
- package/dist/index.d.mts +63 -47
- package/dist/index.d.ts +63 -47
- package/dist/index.js +677 -448
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +661 -449
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +2 -37
- package/dist/react/index.d.ts +2 -37
- package/dist/react/index.js +604 -432
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +604 -433
- package/dist/react/index.mjs.map +1 -1
- package/dist/standalone.browser.js +2722 -0
- package/dist/standalone.d.mts +2 -37
- package/dist/standalone.d.ts +2 -37
- package/dist/standalone.js +604 -431
- package/dist/standalone.js.map +1 -1
- package/dist/standalone.mjs +604 -431
- package/dist/standalone.mjs.map +1 -1
- package/dist/tailwind/index.d.mts +6 -2
- package/dist/tailwind/index.d.ts +6 -2
- package/dist/tailwind/index.js +204 -95
- package/dist/tailwind/index.js.map +1 -1
- package/dist/tailwind/index.mjs +204 -95
- package/dist/tailwind/index.mjs.map +1 -1
- package/package.json +4 -2
package/dist/react/index.mjs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
1
|
// src/react/MarkdownRenderer.tsx
|
|
4
2
|
import React, { useMemo as useMemo2, useEffect as useEffect2 } from "react";
|
|
5
3
|
|
|
@@ -11,39 +9,56 @@ var MarkdownParser = class {
|
|
|
11
9
|
constructor(config) {
|
|
12
10
|
this.rules = [];
|
|
13
11
|
this.warnings = [];
|
|
14
|
-
this.config =
|
|
12
|
+
this.config = {
|
|
13
|
+
debugMode: false,
|
|
14
|
+
maxIterations: 1e4,
|
|
15
|
+
validateMarkdown: false,
|
|
16
|
+
...config
|
|
17
|
+
};
|
|
15
18
|
}
|
|
16
19
|
addRule(rule) {
|
|
17
20
|
this.rules.push(rule);
|
|
18
21
|
this.rules.sort((a, b) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const aFeatureExtension = ["alert", "button", "embed"].includes(a.name);
|
|
23
|
+
const bFeatureExtension = ["alert", "button", "embed"].includes(b.name);
|
|
24
|
+
if (aFeatureExtension && !bFeatureExtension) return -1;
|
|
25
|
+
if (!aFeatureExtension && bFeatureExtension) return 1;
|
|
26
|
+
const aCoreExtension = ["text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(a.name);
|
|
27
|
+
const bCoreExtension = ["text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(b.name);
|
|
28
|
+
if (aCoreExtension && !bCoreExtension) return -1;
|
|
29
|
+
if (!aCoreExtension && bCoreExtension) return 1;
|
|
30
|
+
if (a.name === "image" && b.name === "link") return -1;
|
|
31
|
+
if (a.name === "link" && b.name === "image") return 1;
|
|
32
|
+
if (a.name === "task-item" && b.name === "list-item") return -1;
|
|
33
|
+
if (a.name === "list-item" && b.name === "task-item") return 1;
|
|
34
|
+
if (a.name === "codeblock" && b.name === "code") return -1;
|
|
35
|
+
if (a.name === "code" && b.name === "codeblock") return 1;
|
|
36
|
+
if (a.name === "bold" && b.name === "italic") return -1;
|
|
37
|
+
if (a.name === "italic" && b.name === "bold") return 1;
|
|
25
38
|
return a.name.localeCompare(b.name);
|
|
26
39
|
});
|
|
27
40
|
}
|
|
28
|
-
setupDefaultRulesIfEmpty() {
|
|
29
|
-
const hasDefaultRules = this.rules.some(
|
|
30
|
-
(rule) => !["alert", "button", "embed"].includes(rule.name)
|
|
31
|
-
);
|
|
32
|
-
if (!hasDefaultRules) {
|
|
33
|
-
this.setupDefaultRules();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
41
|
hasRule(name) {
|
|
37
42
|
return this.rules.some((rule) => rule.name === name);
|
|
38
43
|
}
|
|
39
44
|
parse(markdown2) {
|
|
40
45
|
this.warnings = [];
|
|
41
|
-
|
|
46
|
+
if (!markdown2.trim()) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
if (this.rules.length === 0) {
|
|
50
|
+
this.warnings.push("No parse rules registered - consider using CoreExtensions");
|
|
51
|
+
return [{
|
|
52
|
+
type: "text",
|
|
53
|
+
content: markdown2,
|
|
54
|
+
raw: markdown2
|
|
55
|
+
}];
|
|
56
|
+
}
|
|
42
57
|
const processedMarkdown = this.preprocessMarkdown(markdown2);
|
|
43
58
|
const tokens = [];
|
|
44
59
|
let remaining = processedMarkdown;
|
|
45
60
|
let iterationCount = 0;
|
|
46
|
-
const maxIterations = this.config.maxIterations ||
|
|
61
|
+
const maxIterations = this.config.maxIterations || 1e4;
|
|
47
62
|
while (remaining.length > 0 && iterationCount < maxIterations) {
|
|
48
63
|
iterationCount++;
|
|
49
64
|
let matched = false;
|
|
@@ -132,9 +147,6 @@ var MarkdownParser = class {
|
|
|
132
147
|
clearWarnings() {
|
|
133
148
|
this.warnings = [];
|
|
134
149
|
}
|
|
135
|
-
getIterationCount() {
|
|
136
|
-
return 0;
|
|
137
|
-
}
|
|
138
150
|
preprocessMarkdown(markdown2) {
|
|
139
151
|
if (this.config.validateMarkdown) {
|
|
140
152
|
this.validateMarkdown(markdown2);
|
|
@@ -189,161 +201,6 @@ var MarkdownParser = class {
|
|
|
189
201
|
}
|
|
190
202
|
return processed;
|
|
191
203
|
}
|
|
192
|
-
setupDefaultRules() {
|
|
193
|
-
this.addRule({
|
|
194
|
-
name: "heading",
|
|
195
|
-
pattern: /^(#{1,6})\s+(.+)$/m,
|
|
196
|
-
render: (match) => ({
|
|
197
|
-
type: "heading",
|
|
198
|
-
content: match[2]?.trim() || "",
|
|
199
|
-
raw: match[0] || "",
|
|
200
|
-
attributes: {
|
|
201
|
-
level: String(match[1]?.length || 1)
|
|
202
|
-
}
|
|
203
|
-
})
|
|
204
|
-
});
|
|
205
|
-
this.addRule({
|
|
206
|
-
name: "codeblock",
|
|
207
|
-
pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
|
|
208
|
-
render: (match) => ({
|
|
209
|
-
type: "codeblock",
|
|
210
|
-
content: match[2] || "",
|
|
211
|
-
raw: match[0] || "",
|
|
212
|
-
attributes: {
|
|
213
|
-
language: match[1] || "text"
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
});
|
|
217
|
-
this.addRule({
|
|
218
|
-
name: "hard-break-backslash",
|
|
219
|
-
pattern: /\\\s*\n/,
|
|
220
|
-
render: (match) => ({
|
|
221
|
-
type: "line-break",
|
|
222
|
-
content: "",
|
|
223
|
-
raw: match[0] || ""
|
|
224
|
-
})
|
|
225
|
-
});
|
|
226
|
-
this.addRule({
|
|
227
|
-
name: "hard-break-spaces",
|
|
228
|
-
pattern: / +\n/,
|
|
229
|
-
render: (match) => ({
|
|
230
|
-
type: "line-break",
|
|
231
|
-
content: "",
|
|
232
|
-
raw: match[0] || ""
|
|
233
|
-
})
|
|
234
|
-
});
|
|
235
|
-
this.addRule({
|
|
236
|
-
name: "paragraph-break",
|
|
237
|
-
pattern: /\n\s*\n/,
|
|
238
|
-
render: (match) => ({
|
|
239
|
-
type: "paragraph-break",
|
|
240
|
-
content: "",
|
|
241
|
-
raw: match[0] || ""
|
|
242
|
-
})
|
|
243
|
-
});
|
|
244
|
-
this.addRule({
|
|
245
|
-
name: "bold",
|
|
246
|
-
pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
|
|
247
|
-
render: (match) => ({
|
|
248
|
-
type: "bold",
|
|
249
|
-
content: match[1] || "",
|
|
250
|
-
raw: match[0] || ""
|
|
251
|
-
})
|
|
252
|
-
});
|
|
253
|
-
this.addRule({
|
|
254
|
-
name: "italic",
|
|
255
|
-
pattern: /\*((?:(?!\*).)+)\*/,
|
|
256
|
-
render: (match) => ({
|
|
257
|
-
type: "italic",
|
|
258
|
-
content: match[1] || "",
|
|
259
|
-
raw: match[0] || ""
|
|
260
|
-
})
|
|
261
|
-
});
|
|
262
|
-
this.addRule({
|
|
263
|
-
name: "code",
|
|
264
|
-
pattern: /`([^`]+)`/,
|
|
265
|
-
render: (match) => ({
|
|
266
|
-
type: "code",
|
|
267
|
-
content: match[1] || "",
|
|
268
|
-
raw: match[0] || ""
|
|
269
|
-
})
|
|
270
|
-
});
|
|
271
|
-
this.addRule({
|
|
272
|
-
name: "image",
|
|
273
|
-
pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
|
|
274
|
-
render: (match) => ({
|
|
275
|
-
type: "image",
|
|
276
|
-
content: match[1] || "",
|
|
277
|
-
raw: match[0] || "",
|
|
278
|
-
attributes: {
|
|
279
|
-
alt: match[1] || "",
|
|
280
|
-
src: match[2] || "",
|
|
281
|
-
title: match[3] || ""
|
|
282
|
-
}
|
|
283
|
-
})
|
|
284
|
-
});
|
|
285
|
-
this.addRule({
|
|
286
|
-
name: "link",
|
|
287
|
-
pattern: /\[([^\]]+)\]\(([^)]+)\)/,
|
|
288
|
-
render: (match) => ({
|
|
289
|
-
type: "link",
|
|
290
|
-
content: match[1] || "",
|
|
291
|
-
raw: match[0] || "",
|
|
292
|
-
attributes: {
|
|
293
|
-
href: match[2] || ""
|
|
294
|
-
}
|
|
295
|
-
})
|
|
296
|
-
});
|
|
297
|
-
this.addRule({
|
|
298
|
-
name: "task-list",
|
|
299
|
-
pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
|
|
300
|
-
render: (match) => ({
|
|
301
|
-
type: "task-item",
|
|
302
|
-
content: match[3] || "",
|
|
303
|
-
raw: match[0] || "",
|
|
304
|
-
attributes: {
|
|
305
|
-
checked: String((match[2] || "").toLowerCase() === "x")
|
|
306
|
-
}
|
|
307
|
-
})
|
|
308
|
-
});
|
|
309
|
-
this.addRule({
|
|
310
|
-
name: "list",
|
|
311
|
-
pattern: /^(\s*)[-*+]\s+(.+)$/m,
|
|
312
|
-
render: (match) => ({
|
|
313
|
-
type: "list-item",
|
|
314
|
-
content: match[2] || "",
|
|
315
|
-
raw: match[0] || ""
|
|
316
|
-
})
|
|
317
|
-
});
|
|
318
|
-
this.addRule({
|
|
319
|
-
name: "blockquote",
|
|
320
|
-
pattern: /^>\s+(.+)$/m,
|
|
321
|
-
render: (match) => ({
|
|
322
|
-
type: "blockquote",
|
|
323
|
-
content: match[1] || "",
|
|
324
|
-
raw: match[0] || ""
|
|
325
|
-
})
|
|
326
|
-
});
|
|
327
|
-
this.addRule({
|
|
328
|
-
name: "hr",
|
|
329
|
-
pattern: /^---$/m,
|
|
330
|
-
render: (match) => ({
|
|
331
|
-
type: "hr",
|
|
332
|
-
content: "",
|
|
333
|
-
raw: match[0] || ""
|
|
334
|
-
})
|
|
335
|
-
});
|
|
336
|
-
this.addRule({
|
|
337
|
-
name: "soft-break",
|
|
338
|
-
pattern: /\n/,
|
|
339
|
-
render: (match) => ({
|
|
340
|
-
type: "soft-break",
|
|
341
|
-
content: " ",
|
|
342
|
-
// Convert to space for inline text
|
|
343
|
-
raw: match[0] || ""
|
|
344
|
-
})
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
204
|
};
|
|
348
205
|
|
|
349
206
|
// src/utils.ts
|
|
@@ -582,7 +439,6 @@ var MarkdownRenderer = class {
|
|
|
582
439
|
debugMode: false,
|
|
583
440
|
...config
|
|
584
441
|
};
|
|
585
|
-
this.setupDefaultRules();
|
|
586
442
|
}
|
|
587
443
|
addRule(rule) {
|
|
588
444
|
this.rules.set(rule.type, rule);
|
|
@@ -592,28 +448,20 @@ var MarkdownRenderer = class {
|
|
|
592
448
|
}
|
|
593
449
|
render(tokens) {
|
|
594
450
|
this.warnings = [];
|
|
595
|
-
const
|
|
451
|
+
const tokensWithFormat = tokens.map((token) => ({
|
|
452
|
+
...token,
|
|
453
|
+
attributes: {
|
|
454
|
+
...token.attributes,
|
|
455
|
+
format: this.config.format
|
|
456
|
+
}
|
|
457
|
+
}));
|
|
458
|
+
const htmlParts = tokensWithFormat.map((token) => this.renderToken(token));
|
|
596
459
|
const combinedHtml = htmlParts.join("");
|
|
597
460
|
if (this.config.sanitize && !this.config.allowUnsafeHtml) {
|
|
598
461
|
return sanitizeHtml(combinedHtml);
|
|
599
462
|
}
|
|
600
463
|
return combinedHtml;
|
|
601
464
|
}
|
|
602
|
-
getWarnings() {
|
|
603
|
-
return [...this.warnings];
|
|
604
|
-
}
|
|
605
|
-
getConfig() {
|
|
606
|
-
return { ...this.config };
|
|
607
|
-
}
|
|
608
|
-
updateConfig(config) {
|
|
609
|
-
this.config = { ...this.config, ...config };
|
|
610
|
-
}
|
|
611
|
-
setDebugMode(enabled) {
|
|
612
|
-
this.config.debugMode = enabled;
|
|
613
|
-
}
|
|
614
|
-
clearWarnings() {
|
|
615
|
-
this.warnings = [];
|
|
616
|
-
}
|
|
617
465
|
renderToken(token) {
|
|
618
466
|
const rule = this.rules.get(token.type);
|
|
619
467
|
if (rule) {
|
|
@@ -625,9 +473,6 @@ var MarkdownRenderer = class {
|
|
|
625
473
|
return this.createErrorBlock(`Render error for ${token.type}: ${errorMessage}`);
|
|
626
474
|
}
|
|
627
475
|
}
|
|
628
|
-
if (token.type === "text") {
|
|
629
|
-
return escapeHtml(token.content || token.raw || "");
|
|
630
|
-
}
|
|
631
476
|
if (this.config.debugMode) {
|
|
632
477
|
return this.createDebugBlock(token);
|
|
633
478
|
}
|
|
@@ -650,20 +495,239 @@ var MarkdownRenderer = class {
|
|
|
650
495
|
<strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
|
|
651
496
|
</div>`;
|
|
652
497
|
}
|
|
653
|
-
return `<div class="bg-yellow-100 border border-yellow-300 text-yellow-800 p-2 rounded text-sm mb-2">
|
|
654
|
-
<strong>Unknown token type:</strong> ${escapeHtml(token.type)}<br>
|
|
655
|
-
<strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
|
|
656
|
-
</div>`;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
this.
|
|
498
|
+
return `<div class="bg-yellow-100 border border-yellow-300 text-yellow-800 p-2 rounded text-sm mb-2">
|
|
499
|
+
<strong>Unknown token type:</strong> ${escapeHtml(token.type)}<br>
|
|
500
|
+
<strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
|
|
501
|
+
</div>`;
|
|
502
|
+
}
|
|
503
|
+
getWarnings() {
|
|
504
|
+
return [...this.warnings];
|
|
505
|
+
}
|
|
506
|
+
getConfig() {
|
|
507
|
+
return { ...this.config };
|
|
508
|
+
}
|
|
509
|
+
updateConfig(config) {
|
|
510
|
+
this.config = { ...this.config, ...config };
|
|
511
|
+
}
|
|
512
|
+
setDebugMode(enabled) {
|
|
513
|
+
this.config.debugMode = enabled;
|
|
514
|
+
}
|
|
515
|
+
clearWarnings() {
|
|
516
|
+
this.warnings = [];
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// src/extensions/core/blockquote.ts
|
|
521
|
+
var BlockquoteExtension = {
|
|
522
|
+
name: "blockquote",
|
|
523
|
+
parseRules: [
|
|
524
|
+
{
|
|
525
|
+
name: "blockquote",
|
|
526
|
+
pattern: /^>\s+(.+)$/m,
|
|
527
|
+
render: (match) => ({
|
|
528
|
+
type: "blockquote",
|
|
529
|
+
content: match[1] || "",
|
|
530
|
+
raw: match[0] || ""
|
|
531
|
+
})
|
|
532
|
+
}
|
|
533
|
+
],
|
|
534
|
+
renderRules: [
|
|
535
|
+
{
|
|
536
|
+
type: "blockquote",
|
|
537
|
+
render: (token) => {
|
|
538
|
+
const content = escapeHtml(token.content);
|
|
539
|
+
const format = token.attributes?.format || "html";
|
|
540
|
+
if (format === "html") {
|
|
541
|
+
return `<blockquote style="border-left: 2px solid #d1d5db; padding: 8px 0 8px 16px; margin: 16px 0; font-style: italic; color: #6b7280;">${content}</blockquote>`;
|
|
542
|
+
}
|
|
543
|
+
return `<blockquote class="pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4">${content}</blockquote>`;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
]
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// src/extensions/core/breaks.ts
|
|
550
|
+
var LineBreakExtension = {
|
|
551
|
+
name: "line-break",
|
|
552
|
+
parseRules: [
|
|
553
|
+
{
|
|
554
|
+
name: "hard-break-backslash",
|
|
555
|
+
pattern: /\\\s*\n/,
|
|
556
|
+
render: (match) => ({
|
|
557
|
+
type: "line-break",
|
|
558
|
+
content: "",
|
|
559
|
+
raw: match[0] || ""
|
|
560
|
+
})
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
name: "hard-break-spaces",
|
|
564
|
+
pattern: / +\n/,
|
|
565
|
+
render: (match) => ({
|
|
566
|
+
type: "line-break",
|
|
567
|
+
content: "",
|
|
568
|
+
raw: match[0] || ""
|
|
569
|
+
})
|
|
570
|
+
}
|
|
571
|
+
],
|
|
572
|
+
renderRules: [
|
|
573
|
+
{
|
|
574
|
+
type: "line-break",
|
|
575
|
+
render: () => "<br>"
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
type: "paragraph-break",
|
|
579
|
+
render: () => "</p><p>"
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
type: "soft-break",
|
|
583
|
+
render: (token) => token.content || " "
|
|
584
|
+
}
|
|
585
|
+
]
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
// src/extensions/core/code.ts
|
|
589
|
+
var InlineCodeExtension = {
|
|
590
|
+
name: "inline-code",
|
|
591
|
+
parseRules: [
|
|
592
|
+
{
|
|
593
|
+
name: "code",
|
|
594
|
+
pattern: /`([^`]+)`/,
|
|
595
|
+
render: (match) => ({
|
|
596
|
+
type: "code",
|
|
597
|
+
content: match[1] || "",
|
|
598
|
+
raw: match[0] || ""
|
|
599
|
+
})
|
|
600
|
+
}
|
|
601
|
+
],
|
|
602
|
+
renderRules: [
|
|
603
|
+
{
|
|
604
|
+
type: "code",
|
|
605
|
+
render: (token) => {
|
|
606
|
+
const content = escapeHtml(token.content);
|
|
607
|
+
const format = token.attributes?.format;
|
|
608
|
+
if (format === "html") {
|
|
609
|
+
return `<code style="background-color: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 0.875rem;">${content}</code>`;
|
|
610
|
+
}
|
|
611
|
+
return `<code class="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">${content}</code>`;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
]
|
|
615
|
+
};
|
|
616
|
+
var CodeBlockExtension = {
|
|
617
|
+
name: "codeblock",
|
|
618
|
+
parseRules: [
|
|
619
|
+
{
|
|
620
|
+
name: "codeblock",
|
|
621
|
+
pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
|
|
622
|
+
render: (match) => ({
|
|
623
|
+
type: "codeblock",
|
|
624
|
+
content: match[2] || "",
|
|
625
|
+
raw: match[0] || "",
|
|
626
|
+
attributes: {
|
|
627
|
+
language: match[1] || "text"
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
}
|
|
631
|
+
],
|
|
632
|
+
renderRules: [
|
|
633
|
+
{
|
|
634
|
+
type: "codeblock",
|
|
635
|
+
render: (token) => {
|
|
636
|
+
const language = token.attributes?.language || "text";
|
|
637
|
+
const escapedCode = escapeHtml(token.content);
|
|
638
|
+
const format = token.attributes?.format || "html";
|
|
639
|
+
if (format === "html") {
|
|
640
|
+
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>`;
|
|
641
|
+
}
|
|
642
|
+
return `<pre class="bg-muted p-4 rounded-md overflow-x-auto my-4"><code class="language-${escapeHtml(language)}">${escapedCode}</code></pre>`;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
]
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// src/extensions/core/emphasis.ts
|
|
649
|
+
var BoldExtension = {
|
|
650
|
+
name: "bold",
|
|
651
|
+
parseRules: [
|
|
652
|
+
{
|
|
653
|
+
name: "bold",
|
|
654
|
+
pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
|
|
655
|
+
render: (match) => ({
|
|
656
|
+
type: "bold",
|
|
657
|
+
content: match[1] || "",
|
|
658
|
+
raw: match[0] || ""
|
|
659
|
+
})
|
|
660
|
+
}
|
|
661
|
+
],
|
|
662
|
+
renderRules: [
|
|
663
|
+
{
|
|
664
|
+
type: "bold",
|
|
665
|
+
render: (token) => {
|
|
666
|
+
const content = escapeHtml(token.content);
|
|
667
|
+
const format = token.attributes?.format;
|
|
668
|
+
if (format === "html") {
|
|
669
|
+
return `<strong>${content}</strong>`;
|
|
670
|
+
}
|
|
671
|
+
return `<strong class="font-bold">${content}</strong>`;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
]
|
|
675
|
+
};
|
|
676
|
+
var ItalicExtension = {
|
|
677
|
+
name: "italic",
|
|
678
|
+
parseRules: [
|
|
679
|
+
{
|
|
680
|
+
name: "italic",
|
|
681
|
+
pattern: /\*((?:(?!\*).)+)\*/,
|
|
682
|
+
render: (match) => ({
|
|
683
|
+
type: "italic",
|
|
684
|
+
content: match[1] || "",
|
|
685
|
+
raw: match[0] || ""
|
|
686
|
+
})
|
|
687
|
+
}
|
|
688
|
+
],
|
|
689
|
+
renderRules: [
|
|
690
|
+
{
|
|
691
|
+
type: "italic",
|
|
692
|
+
render: (token) => {
|
|
693
|
+
const content = escapeHtml(token.content);
|
|
694
|
+
const format = token.attributes?.format;
|
|
695
|
+
if (format === "html") {
|
|
696
|
+
return `<em>${content}</em>`;
|
|
697
|
+
}
|
|
698
|
+
return `<em class="italic">${content}</em>`;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
]
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// src/extensions/core/heading.ts
|
|
705
|
+
var HeadingExtension = {
|
|
706
|
+
name: "heading",
|
|
707
|
+
parseRules: [
|
|
708
|
+
{
|
|
709
|
+
name: "heading",
|
|
710
|
+
pattern: /^(#{1,6})\s+(.+)$/m,
|
|
711
|
+
render: (match) => ({
|
|
712
|
+
type: "heading",
|
|
713
|
+
content: match[2]?.trim() || "",
|
|
714
|
+
raw: match[0] || "",
|
|
715
|
+
attributes: {
|
|
716
|
+
level: String(match[1]?.length || 1)
|
|
717
|
+
}
|
|
718
|
+
})
|
|
719
|
+
}
|
|
720
|
+
],
|
|
721
|
+
renderRules: [
|
|
722
|
+
{
|
|
660
723
|
type: "heading",
|
|
661
724
|
render: (token) => {
|
|
662
725
|
const level = parseInt(token.attributes?.level || "1");
|
|
663
726
|
const text = token.content;
|
|
664
727
|
const id = generateId(text);
|
|
665
728
|
const escapedContent = escapeHtml(text);
|
|
666
|
-
|
|
729
|
+
const format = token.attributes?.format || "html";
|
|
730
|
+
if (format === "html") {
|
|
667
731
|
return `<h${level} id="${id}">${escapedContent}</h${level}>`;
|
|
668
732
|
}
|
|
669
733
|
let headingClasses = "group relative flex items-center gap-2";
|
|
@@ -690,61 +754,108 @@ var MarkdownRenderer = class {
|
|
|
690
754
|
return `<h${level} id="${id}" class="${headingClasses}">
|
|
691
755
|
${escapedContent}
|
|
692
756
|
<a href="#${id}" class="opacity-0 group-hover:opacity-100 text-muted-foreground transition-opacity">
|
|
693
|
-
<svg width="16" height="16" viewBox="0 0
|
|
694
|
-
|
|
695
|
-
|
|
757
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
758
|
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
|
759
|
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
|
760
|
+
</svg>
|
|
696
761
|
</a>
|
|
697
762
|
</h${level}>`;
|
|
698
763
|
}
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
|
|
764
|
+
}
|
|
765
|
+
]
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// src/extensions/core/hr.ts
|
|
769
|
+
var HorizontalRuleExtension = {
|
|
770
|
+
name: "hr",
|
|
771
|
+
parseRules: [
|
|
772
|
+
{
|
|
773
|
+
name: "hr",
|
|
774
|
+
pattern: /^---$/m,
|
|
775
|
+
render: (match) => ({
|
|
776
|
+
type: "hr",
|
|
777
|
+
content: "",
|
|
778
|
+
raw: match[0] || ""
|
|
779
|
+
})
|
|
780
|
+
}
|
|
781
|
+
],
|
|
782
|
+
renderRules: [
|
|
783
|
+
{
|
|
784
|
+
type: "hr",
|
|
702
785
|
render: (token) => {
|
|
703
|
-
const
|
|
704
|
-
if (
|
|
705
|
-
return
|
|
786
|
+
const format = token.attributes?.format;
|
|
787
|
+
if (format === "html") {
|
|
788
|
+
return '<hr style="margin: 24px 0; border: none; border-top: 1px solid #d1d5db;">';
|
|
706
789
|
}
|
|
707
|
-
return
|
|
790
|
+
return '<hr class="my-6 border-t border-border">';
|
|
708
791
|
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
792
|
+
}
|
|
793
|
+
]
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
// src/extensions/core/image.ts
|
|
797
|
+
var ImageExtension = {
|
|
798
|
+
name: "image",
|
|
799
|
+
parseRules: [
|
|
800
|
+
{
|
|
801
|
+
name: "image",
|
|
802
|
+
pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
|
|
803
|
+
render: (match) => ({
|
|
804
|
+
type: "image",
|
|
805
|
+
content: match[1] || "",
|
|
806
|
+
raw: match[0] || "",
|
|
807
|
+
attributes: {
|
|
808
|
+
alt: match[1] || "",
|
|
809
|
+
src: match[2] || "",
|
|
810
|
+
title: match[3] || ""
|
|
716
811
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
812
|
+
})
|
|
813
|
+
}
|
|
814
|
+
],
|
|
815
|
+
renderRules: [
|
|
816
|
+
{
|
|
817
|
+
type: "image",
|
|
722
818
|
render: (token) => {
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
819
|
+
const src = token.attributes?.src || "";
|
|
820
|
+
const alt = token.attributes?.alt || "";
|
|
821
|
+
const title = token.attributes?.title || "";
|
|
822
|
+
const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
|
|
823
|
+
const format = token.attributes?.format || "html";
|
|
824
|
+
if (format === "html") {
|
|
825
|
+
return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} style="max-width: 100%; height: auto; border-radius: 8px; margin: 16px 0;" loading="lazy" />`;
|
|
726
826
|
}
|
|
727
|
-
return `<
|
|
827
|
+
return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} class="max-w-full h-auto rounded-lg my-4" loading="lazy" />`;
|
|
728
828
|
}
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
829
|
+
}
|
|
830
|
+
]
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
// src/extensions/core/links.ts
|
|
834
|
+
var LinkExtension = {
|
|
835
|
+
name: "link",
|
|
836
|
+
parseRules: [
|
|
837
|
+
{
|
|
838
|
+
name: "link",
|
|
839
|
+
pattern: /\[(?!(?:button|embed):)([^\]]+)\]\(([^)]+)\)/,
|
|
840
|
+
render: (match) => ({
|
|
841
|
+
type: "link",
|
|
842
|
+
content: match[1] || "",
|
|
843
|
+
raw: match[0] || "",
|
|
844
|
+
attributes: {
|
|
845
|
+
href: match[2] || ""
|
|
737
846
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
847
|
+
})
|
|
848
|
+
}
|
|
849
|
+
],
|
|
850
|
+
renderRules: [
|
|
851
|
+
{
|
|
742
852
|
type: "link",
|
|
743
853
|
render: (token) => {
|
|
744
854
|
const href = token.attributes?.href || "#";
|
|
745
855
|
const escapedHref = escapeHtml(href);
|
|
746
856
|
const escapedText = escapeHtml(token.content);
|
|
747
|
-
|
|
857
|
+
const format = token.attributes?.format || "html";
|
|
858
|
+
if (format === "html") {
|
|
748
859
|
return `<a href="${escapedHref}" target="_blank" rel="noopener noreferrer">${escapedText}</a>`;
|
|
749
860
|
}
|
|
750
861
|
return `<a href="${escapedHref}" class="text-primary hover:underline inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
|
|
@@ -756,47 +867,55 @@ var MarkdownRenderer = class {
|
|
|
756
867
|
</svg>
|
|
757
868
|
</a>`;
|
|
758
869
|
}
|
|
759
|
-
}
|
|
760
|
-
|
|
870
|
+
}
|
|
871
|
+
]
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
// src/extensions/core/lists.ts
|
|
875
|
+
var ListExtension = {
|
|
876
|
+
name: "list",
|
|
877
|
+
parseRules: [
|
|
878
|
+
{
|
|
879
|
+
name: "list-item",
|
|
880
|
+
pattern: /^(\s*)[-*+]\s+(.+)$/m,
|
|
881
|
+
render: (match) => ({
|
|
882
|
+
type: "list-item",
|
|
883
|
+
content: match[2] || "",
|
|
884
|
+
raw: match[0] || ""
|
|
885
|
+
})
|
|
886
|
+
}
|
|
887
|
+
],
|
|
888
|
+
renderRules: [
|
|
889
|
+
{
|
|
761
890
|
type: "list-item",
|
|
762
891
|
render: (token) => `<li>${escapeHtml(token.content)}</li>`
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
return escapeHtml(token.content);
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
this.addRule({
|
|
782
|
-
type: "paragraph",
|
|
783
|
-
render: (token) => {
|
|
784
|
-
if (!token.content) return "";
|
|
785
|
-
const content = token.content.trim();
|
|
786
|
-
if (!content) return "";
|
|
787
|
-
const processedContent = content.includes("<br>") ? content : escapeHtml(content);
|
|
788
|
-
if (this.config.format === "html") {
|
|
789
|
-
return `<p style="line-height: 1.75; margin-bottom: 16px;">${processedContent}</p>`;
|
|
892
|
+
}
|
|
893
|
+
]
|
|
894
|
+
};
|
|
895
|
+
var TaskListExtension = {
|
|
896
|
+
name: "task-list",
|
|
897
|
+
parseRules: [
|
|
898
|
+
{
|
|
899
|
+
name: "task-item",
|
|
900
|
+
pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
|
|
901
|
+
render: (match) => ({
|
|
902
|
+
type: "task-item",
|
|
903
|
+
content: match[3] || "",
|
|
904
|
+
raw: match[0] || "",
|
|
905
|
+
attributes: {
|
|
906
|
+
checked: String((match[2] || "").toLowerCase() === "x")
|
|
790
907
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
908
|
+
})
|
|
909
|
+
}
|
|
910
|
+
],
|
|
911
|
+
renderRules: [
|
|
912
|
+
{
|
|
795
913
|
type: "task-item",
|
|
796
914
|
render: (token) => {
|
|
797
915
|
const isChecked = token.attributes?.checked === "true";
|
|
798
916
|
const escapedContent = escapeHtml(token.content);
|
|
799
|
-
|
|
917
|
+
const format = token.attributes?.format || "html";
|
|
918
|
+
if (format === "html") {
|
|
800
919
|
return `<div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
|
|
801
920
|
<input type="checkbox" ${isChecked ? "checked" : ""} disabled style="margin: 0;" />
|
|
802
921
|
<span${isChecked ? ' style="text-decoration: line-through; color: #6b7280;"' : ""}>${escapedContent}</span>
|
|
@@ -808,44 +927,65 @@ var MarkdownRenderer = class {
|
|
|
808
927
|
<span${isChecked ? ' class="line-through text-muted-foreground"' : ""}>${escapedContent}</span>
|
|
809
928
|
</div>`;
|
|
810
929
|
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
|
|
930
|
+
}
|
|
931
|
+
]
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
// src/extensions/core/paragraph.ts
|
|
935
|
+
var ParagraphExtension = {
|
|
936
|
+
name: "paragraph",
|
|
937
|
+
parseRules: [],
|
|
938
|
+
renderRules: [
|
|
939
|
+
{
|
|
940
|
+
type: "paragraph",
|
|
814
941
|
render: (token) => {
|
|
815
|
-
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
942
|
+
if (!token.content) return "";
|
|
943
|
+
const content = token.content.trim();
|
|
944
|
+
if (!content) return "";
|
|
945
|
+
const processedContent = content.includes("<br>") ? content : escapeHtml(content);
|
|
946
|
+
const format = token.attributes?.format || "html";
|
|
947
|
+
if (format === "html") {
|
|
948
|
+
return `<p style="line-height: 1.75; margin-bottom: 16px;">${processedContent}</p>`;
|
|
821
949
|
}
|
|
822
|
-
return `<
|
|
950
|
+
return `<p class="leading-7 mb-4">${processedContent}</p>`;
|
|
823
951
|
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
952
|
+
}
|
|
953
|
+
]
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
// src/extensions/core/text.ts
|
|
957
|
+
var TextExtension = {
|
|
958
|
+
name: "text",
|
|
959
|
+
parseRules: [],
|
|
960
|
+
renderRules: [
|
|
961
|
+
{
|
|
962
|
+
type: "text",
|
|
963
|
+
render: (token) => {
|
|
964
|
+
if (!token.content) return "";
|
|
965
|
+
return escapeHtml(token.content);
|
|
832
966
|
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
type: "line-break",
|
|
836
|
-
render: () => "<br>"
|
|
837
|
-
});
|
|
838
|
-
this.addRule({
|
|
839
|
-
type: "paragraph-break",
|
|
840
|
-
render: () => "</p><p>"
|
|
841
|
-
});
|
|
842
|
-
this.addRule({
|
|
843
|
-
type: "soft-break",
|
|
844
|
-
render: (token) => token.content || " "
|
|
845
|
-
});
|
|
846
|
-
}
|
|
967
|
+
}
|
|
968
|
+
]
|
|
847
969
|
};
|
|
848
970
|
|
|
971
|
+
// src/extensions/core/index.ts
|
|
972
|
+
var CoreExtensions = [
|
|
973
|
+
TextExtension,
|
|
974
|
+
HeadingExtension,
|
|
975
|
+
BoldExtension,
|
|
976
|
+
ItalicExtension,
|
|
977
|
+
InlineCodeExtension,
|
|
978
|
+
CodeBlockExtension,
|
|
979
|
+
LinkExtension,
|
|
980
|
+
ImageExtension,
|
|
981
|
+
ListExtension,
|
|
982
|
+
TaskListExtension,
|
|
983
|
+
BlockquoteExtension,
|
|
984
|
+
HorizontalRuleExtension,
|
|
985
|
+
ParagraphExtension,
|
|
986
|
+
LineBreakExtension
|
|
987
|
+
];
|
|
988
|
+
|
|
849
989
|
// src/extensions/alert.ts
|
|
850
990
|
var AlertExtension = {
|
|
851
991
|
name: "alert",
|
|
@@ -925,26 +1065,19 @@ var ButtonExtension = {
|
|
|
925
1065
|
name: "button",
|
|
926
1066
|
pattern: /\[button:([^\]]+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
|
|
927
1067
|
render: (match) => {
|
|
928
|
-
const
|
|
929
|
-
const href = match[2] || "";
|
|
930
|
-
const optionsString = match[3] || "";
|
|
931
|
-
const options = optionsString.split(",").map((opt) => opt.trim()).filter(Boolean);
|
|
932
|
-
const styleOptions = ["default", "primary", "secondary", "success", "danger", "outline", "ghost"];
|
|
933
|
-
const style = options.find((opt) => styleOptions.includes(opt)) || "primary";
|
|
934
|
-
const sizeOptions = ["sm", "md", "lg"];
|
|
935
|
-
const size = options.find((opt) => sizeOptions.includes(opt)) || "md";
|
|
936
|
-
const disabled = options.includes("disabled");
|
|
937
|
-
const target = options.includes("self") ? "_self" : "_blank";
|
|
1068
|
+
const options = match[3] ? match[3].split(",").map((opt) => opt.trim()) : [];
|
|
938
1069
|
return {
|
|
939
1070
|
type: "button",
|
|
940
|
-
content:
|
|
1071
|
+
content: match[1] || "",
|
|
941
1072
|
raw: match[0] || "",
|
|
942
1073
|
attributes: {
|
|
943
|
-
href,
|
|
944
|
-
style
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1074
|
+
href: match[2] || "",
|
|
1075
|
+
style: options.find(
|
|
1076
|
+
(opt) => ["default", "primary", "secondary", "success", "danger", "outline", "ghost"].includes(opt)
|
|
1077
|
+
) || "primary",
|
|
1078
|
+
size: options.find((opt) => ["sm", "md", "lg"].includes(opt)) || "md",
|
|
1079
|
+
disabled: String(options.includes("disabled")),
|
|
1080
|
+
target: options.includes("self") ? "_self" : "_blank"
|
|
948
1081
|
}
|
|
949
1082
|
};
|
|
950
1083
|
}
|
|
@@ -959,53 +1092,92 @@ var ButtonExtension = {
|
|
|
959
1092
|
const size = token.attributes?.size || "md";
|
|
960
1093
|
const disabled = token.attributes?.disabled === "true";
|
|
961
1094
|
const target = token.attributes?.target || "_blank";
|
|
962
|
-
const
|
|
963
|
-
|
|
1095
|
+
const baseClasses = `
|
|
1096
|
+
inline-flex items-center justify-center font-medium rounded-lg
|
|
1097
|
+
transition-all duration-200 ease-out
|
|
1098
|
+
focus:outline-none focus:ring-2 focus:ring-offset-2
|
|
1099
|
+
disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none
|
|
1100
|
+
transform hover:scale-[1.02] active:scale-[0.98]
|
|
1101
|
+
shadow-sm hover:shadow-md active:shadow-sm
|
|
1102
|
+
border border-transparent
|
|
1103
|
+
relative overflow-hidden
|
|
1104
|
+
before:absolute before:inset-0 before:rounded-lg
|
|
1105
|
+
before:bg-gradient-to-br before:from-white/20 before:to-transparent
|
|
1106
|
+
before:opacity-0 hover:before:opacity-100 before:transition-opacity before:duration-200
|
|
1107
|
+
`.replace(/\s+/g, " ").trim();
|
|
1108
|
+
const sizeClasses = {
|
|
1109
|
+
sm: "px-3 py-1.5 text-sm gap-1.5",
|
|
1110
|
+
md: "px-4 py-2 text-base gap-2",
|
|
1111
|
+
lg: "px-6 py-3 text-lg gap-2.5"
|
|
1112
|
+
};
|
|
1113
|
+
const styleClasses = {
|
|
1114
|
+
default: `
|
|
1115
|
+
bg-slate-600 text-white border-slate-500
|
|
1116
|
+
hover:bg-slate-700 hover:border-slate-400
|
|
1117
|
+
focus:ring-slate-500
|
|
1118
|
+
shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
|
|
1119
|
+
hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
|
|
1120
|
+
`,
|
|
1121
|
+
primary: `
|
|
1122
|
+
bg-blue-600 text-white border-blue-500
|
|
1123
|
+
hover:bg-blue-700 hover:border-blue-400
|
|
1124
|
+
focus:ring-blue-500
|
|
1125
|
+
shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
|
|
1126
|
+
hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
|
|
1127
|
+
`,
|
|
1128
|
+
secondary: `
|
|
1129
|
+
bg-gray-600 text-white border-gray-500
|
|
1130
|
+
hover:bg-gray-700 hover:border-gray-400
|
|
1131
|
+
focus:ring-gray-500
|
|
1132
|
+
shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
|
|
1133
|
+
hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
|
|
1134
|
+
`,
|
|
1135
|
+
success: `
|
|
1136
|
+
bg-green-600 text-white border-green-500
|
|
1137
|
+
hover:bg-green-700 hover:border-green-400
|
|
1138
|
+
focus:ring-green-500
|
|
1139
|
+
shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
|
|
1140
|
+
hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
|
|
1141
|
+
`,
|
|
1142
|
+
danger: `
|
|
1143
|
+
bg-red-600 text-white border-red-500
|
|
1144
|
+
hover:bg-red-700 hover:border-red-400
|
|
1145
|
+
focus:ring-red-500
|
|
1146
|
+
shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
|
|
1147
|
+
hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
|
|
1148
|
+
`,
|
|
1149
|
+
outline: `
|
|
1150
|
+
bg-transparent text-blue-600 border-blue-600
|
|
1151
|
+
hover:bg-blue-50 hover:border-blue-700 hover:text-blue-700
|
|
1152
|
+
focus:ring-blue-500
|
|
1153
|
+
shadow-[0_0_0_1px_rgba(59,130,246,0.5)_inset]
|
|
1154
|
+
hover:shadow-[0_0_0_1px_rgba(29,78,216,0.6)_inset,0_1px_2px_0_rgba(0,0,0,0.05)]
|
|
1155
|
+
`,
|
|
1156
|
+
ghost: `
|
|
1157
|
+
bg-transparent text-gray-700 border-transparent
|
|
1158
|
+
hover:bg-gray-100 hover:text-gray-900
|
|
1159
|
+
focus:ring-gray-500
|
|
1160
|
+
shadow-none
|
|
1161
|
+
hover:shadow-[0_1px_2px_0_rgba(0,0,0,0.05)]
|
|
1162
|
+
`
|
|
1163
|
+
};
|
|
1164
|
+
const classes = `
|
|
1165
|
+
${baseClasses}
|
|
1166
|
+
${sizeClasses[size] || sizeClasses.md}
|
|
1167
|
+
${styleClasses[style] || styleClasses.primary}
|
|
1168
|
+
`.replace(/\s+/g, " ").trim();
|
|
964
1169
|
const targetAttr = target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : target === "_self" ? ' target="_self"' : "";
|
|
965
1170
|
const disabledAttr = disabled ? ' aria-disabled="true" tabindex="-1"' : "";
|
|
966
|
-
const externalIcon = target === "_blank" && !disabled ?
|
|
1171
|
+
const externalIcon = target === "_blank" && !disabled ? `<svg class="w-4 h-4 ml-1 opacity-75" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1172
|
+
<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"/>
|
|
1173
|
+
</svg>` : "";
|
|
967
1174
|
return `<a href="${href}" class="${classes}"${targetAttr}${disabledAttr}>
|
|
968
|
-
|
|
969
|
-
|
|
1175
|
+
<span class="relative z-10">${token.content}</span>${externalIcon}
|
|
1176
|
+
</a>`;
|
|
970
1177
|
}
|
|
971
1178
|
}
|
|
972
1179
|
]
|
|
973
1180
|
};
|
|
974
|
-
function buildButtonClasses(style, size) {
|
|
975
|
-
const base = [
|
|
976
|
-
"inline-flex",
|
|
977
|
-
"items-center",
|
|
978
|
-
"justify-center",
|
|
979
|
-
"font-medium",
|
|
980
|
-
"rounded-lg",
|
|
981
|
-
"transition-colors",
|
|
982
|
-
"focus:outline-none",
|
|
983
|
-
"focus:ring-2",
|
|
984
|
-
"focus:ring-offset-2",
|
|
985
|
-
"disabled:opacity-50",
|
|
986
|
-
"disabled:cursor-not-allowed"
|
|
987
|
-
];
|
|
988
|
-
const sizes = {
|
|
989
|
-
sm: ["px-3", "py-1.5", "text-sm"],
|
|
990
|
-
md: ["px-4", "py-2", "text-base"],
|
|
991
|
-
lg: ["px-6", "py-3", "text-lg"]
|
|
992
|
-
};
|
|
993
|
-
const styles = {
|
|
994
|
-
default: ["bg-slate-600", "text-white", "hover:bg-slate-700", "focus:ring-slate-500"],
|
|
995
|
-
primary: ["bg-blue-600", "text-white", "hover:bg-blue-700", "focus:ring-blue-500"],
|
|
996
|
-
secondary: ["bg-gray-600", "text-white", "hover:bg-gray-700", "focus:ring-gray-500"],
|
|
997
|
-
success: ["bg-green-600", "text-white", "hover:bg-green-700", "focus:ring-green-500"],
|
|
998
|
-
danger: ["bg-red-600", "text-white", "hover:bg-red-700", "focus:ring-red-500"],
|
|
999
|
-
outline: ["border", "border-blue-600", "text-blue-600", "hover:bg-blue-50", "focus:ring-blue-500"],
|
|
1000
|
-
ghost: ["text-gray-700", "hover:bg-gray-100", "focus:ring-gray-500"]
|
|
1001
|
-
};
|
|
1002
|
-
const allClasses = [
|
|
1003
|
-
...base,
|
|
1004
|
-
...sizes[size] ?? sizes.md ?? [],
|
|
1005
|
-
...styles[style] ?? styles.primary ?? []
|
|
1006
|
-
];
|
|
1007
|
-
return allClasses.join(" ");
|
|
1008
|
-
}
|
|
1009
1181
|
|
|
1010
1182
|
// src/extensions/embed.ts
|
|
1011
1183
|
var EmbedExtension = {
|
|
@@ -1083,8 +1255,7 @@ function renderYouTubeEmbed(url, options, classes) {
|
|
|
1083
1255
|
params.set("rel", "0");
|
|
1084
1256
|
params.set("modestbranding", "1");
|
|
1085
1257
|
const embedUrl = `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
|
|
1086
|
-
return
|
|
1087
|
-
<div class="${classes}">
|
|
1258
|
+
return `<div class="${classes}">
|
|
1088
1259
|
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
|
|
1089
1260
|
<iframe
|
|
1090
1261
|
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
|
|
@@ -1112,8 +1283,7 @@ function renderCodePenEmbed(url, options, classes) {
|
|
|
1112
1283
|
"editable": "true"
|
|
1113
1284
|
});
|
|
1114
1285
|
const embedUrl = `https://codepen.io/${user}/embed/${penId}?${embedParams.toString()}`;
|
|
1115
|
-
return
|
|
1116
|
-
<div class="${classes}">
|
|
1286
|
+
return `<div class="${classes}">
|
|
1117
1287
|
<iframe
|
|
1118
1288
|
height="${height}"
|
|
1119
1289
|
style="width: 100%; border: 0;"
|
|
@@ -1137,8 +1307,7 @@ function renderVimeoEmbed(url, options, classes) {
|
|
|
1137
1307
|
if (options.mute === "1") params.set("muted", "1");
|
|
1138
1308
|
if (options.loop === "1") params.set("loop", "1");
|
|
1139
1309
|
const embedUrl = `https://player.vimeo.com/video/${videoId}?${params.toString()}`;
|
|
1140
|
-
return
|
|
1141
|
-
<div class="${classes}">
|
|
1310
|
+
return `<div class="${classes}">
|
|
1142
1311
|
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
|
|
1143
1312
|
<iframe
|
|
1144
1313
|
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
|
|
@@ -1154,8 +1323,7 @@ function renderVimeoEmbed(url, options, classes) {
|
|
|
1154
1323
|
function renderSpotifyEmbed(url, options, classes) {
|
|
1155
1324
|
const embedUrl = url.replace("open.spotify.com", "open.spotify.com/embed");
|
|
1156
1325
|
const height = options.height || "380";
|
|
1157
|
-
return
|
|
1158
|
-
<div class="${classes}">
|
|
1326
|
+
return `<div class="${classes}">
|
|
1159
1327
|
<iframe
|
|
1160
1328
|
style="border-radius: 12px;"
|
|
1161
1329
|
src="${embedUrl}"
|
|
@@ -1178,8 +1346,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
|
|
|
1178
1346
|
if (!embedUrl.includes("?")) {
|
|
1179
1347
|
embedUrl += `?view=${view}`;
|
|
1180
1348
|
}
|
|
1181
|
-
return
|
|
1182
|
-
<div class="${classes}">
|
|
1349
|
+
return `<div class="${classes}">
|
|
1183
1350
|
<iframe
|
|
1184
1351
|
src="${embedUrl}"
|
|
1185
1352
|
style="width: 100%; height: ${height}px; border: 0; border-radius: 4px; overflow: hidden;"
|
|
@@ -1192,8 +1359,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
|
|
|
1192
1359
|
function renderFigmaEmbed(url, options, classes) {
|
|
1193
1360
|
const embedUrl = `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
|
|
1194
1361
|
const height = options.height || "450";
|
|
1195
|
-
return
|
|
1196
|
-
<div class="${classes}">
|
|
1362
|
+
return `<div class="${classes}">
|
|
1197
1363
|
<iframe
|
|
1198
1364
|
style="border: none;"
|
|
1199
1365
|
width="100%"
|
|
@@ -1204,8 +1370,7 @@ function renderFigmaEmbed(url, options, classes) {
|
|
|
1204
1370
|
</div>`;
|
|
1205
1371
|
}
|
|
1206
1372
|
function renderTwitterEmbed(url, _options, classes) {
|
|
1207
|
-
return
|
|
1208
|
-
<div class="${classes}">
|
|
1373
|
+
return `<div class="${classes}">
|
|
1209
1374
|
<div class="p-4">
|
|
1210
1375
|
<div class="flex items-center gap-3 mb-3">
|
|
1211
1376
|
<svg class="w-6 h-6 fill-current text-blue-500" viewBox="0 0 24 24">
|
|
@@ -1233,8 +1398,7 @@ function renderGitHubEmbed(url, _options, classes) {
|
|
|
1233
1398
|
if (!owner || !repo) {
|
|
1234
1399
|
return createErrorEmbed("Invalid GitHub URL", url, classes);
|
|
1235
1400
|
}
|
|
1236
|
-
return
|
|
1237
|
-
<div class="${classes}">
|
|
1401
|
+
return `<div class="${classes}">
|
|
1238
1402
|
<div class="p-4">
|
|
1239
1403
|
<div class="flex items-center gap-3 mb-3">
|
|
1240
1404
|
<svg class="w-6 h-6 fill-current" viewBox="0 0 24 24">
|
|
@@ -1257,8 +1421,7 @@ function renderGitHubEmbed(url, _options, classes) {
|
|
|
1257
1421
|
}
|
|
1258
1422
|
function renderGenericEmbed(url, _options, classes) {
|
|
1259
1423
|
const domain = extractDomain(url);
|
|
1260
|
-
return
|
|
1261
|
-
<div class="${classes}">
|
|
1424
|
+
return `<div class="${classes}">
|
|
1262
1425
|
<div class="p-4">
|
|
1263
1426
|
<div class="flex items-center gap-3 mb-3">
|
|
1264
1427
|
<div class="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
|
|
@@ -1282,8 +1445,7 @@ function renderGenericEmbed(url, _options, classes) {
|
|
|
1282
1445
|
</div>`;
|
|
1283
1446
|
}
|
|
1284
1447
|
function createErrorEmbed(error, url, classes) {
|
|
1285
|
-
return
|
|
1286
|
-
<div class="${classes}">
|
|
1448
|
+
return `<div class="${classes}">
|
|
1287
1449
|
<div class="p-4 text-destructive">
|
|
1288
1450
|
<div class="font-medium flex items-center gap-2">
|
|
1289
1451
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -1317,19 +1479,54 @@ var ChangerawrMarkdown = class {
|
|
|
1317
1479
|
this.extensions = /* @__PURE__ */ new Map();
|
|
1318
1480
|
this.parser = new MarkdownParser(config?.parser);
|
|
1319
1481
|
this.renderer = new MarkdownRenderer(config?.renderer);
|
|
1320
|
-
this.
|
|
1482
|
+
this.registerCoreExtensions();
|
|
1483
|
+
this.registerFeatureExtensions();
|
|
1321
1484
|
if (config?.extensions) {
|
|
1322
1485
|
config.extensions.forEach((extension) => {
|
|
1323
1486
|
this.registerExtension(extension);
|
|
1324
1487
|
});
|
|
1325
1488
|
}
|
|
1326
|
-
this.parser.setupDefaultRulesIfEmpty();
|
|
1327
1489
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1490
|
+
registerFeatureExtensions() {
|
|
1491
|
+
this.registerExtension(AlertExtension);
|
|
1492
|
+
this.registerExtension(ButtonExtension);
|
|
1493
|
+
this.registerExtension(EmbedExtension);
|
|
1494
|
+
}
|
|
1495
|
+
registerCoreExtensions() {
|
|
1496
|
+
CoreExtensions.forEach((extension) => {
|
|
1497
|
+
this.registerExtension(extension);
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1331
1500
|
registerExtension(extension) {
|
|
1332
1501
|
try {
|
|
1502
|
+
if (!extension.name || typeof extension.name !== "string") {
|
|
1503
|
+
throw new Error("Extension must have a valid name");
|
|
1504
|
+
}
|
|
1505
|
+
if (!Array.isArray(extension.parseRules)) {
|
|
1506
|
+
throw new Error("Extension must have parseRules array");
|
|
1507
|
+
}
|
|
1508
|
+
if (!Array.isArray(extension.renderRules)) {
|
|
1509
|
+
throw new Error("Extension must have renderRules array");
|
|
1510
|
+
}
|
|
1511
|
+
extension.parseRules.forEach((rule, index) => {
|
|
1512
|
+
if (!rule.name || typeof rule.name !== "string") {
|
|
1513
|
+
throw new Error(`Parse rule ${index} must have a valid name`);
|
|
1514
|
+
}
|
|
1515
|
+
if (!rule.pattern || !(rule.pattern instanceof RegExp)) {
|
|
1516
|
+
throw new Error(`Parse rule ${index} must have a valid RegExp pattern`);
|
|
1517
|
+
}
|
|
1518
|
+
if (!rule.render || typeof rule.render !== "function") {
|
|
1519
|
+
throw new Error(`Parse rule ${index} must have a valid render function`);
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
extension.renderRules.forEach((rule, index) => {
|
|
1523
|
+
if (!rule.type || typeof rule.type !== "string") {
|
|
1524
|
+
throw new Error(`Render rule ${index} must have a valid type`);
|
|
1525
|
+
}
|
|
1526
|
+
if (!rule.render || typeof rule.render !== "function") {
|
|
1527
|
+
throw new Error(`Render rule ${index} must have a valid render function`);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1333
1530
|
this.extensions.set(extension.name, extension);
|
|
1334
1531
|
extension.parseRules.forEach((rule) => {
|
|
1335
1532
|
this.parser.addRule(rule);
|
|
@@ -1350,9 +1547,6 @@ var ChangerawrMarkdown = class {
|
|
|
1350
1547
|
};
|
|
1351
1548
|
}
|
|
1352
1549
|
}
|
|
1353
|
-
/**
|
|
1354
|
-
* Unregister an extension
|
|
1355
|
-
*/
|
|
1356
1550
|
unregisterExtension(name) {
|
|
1357
1551
|
const extension = this.extensions.get(name);
|
|
1358
1552
|
if (!extension) {
|
|
@@ -1366,46 +1560,25 @@ var ChangerawrMarkdown = class {
|
|
|
1366
1560
|
return false;
|
|
1367
1561
|
}
|
|
1368
1562
|
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Parse markdown content into tokens
|
|
1371
|
-
*/
|
|
1372
1563
|
parse(markdown2) {
|
|
1373
1564
|
return this.parser.parse(markdown2);
|
|
1374
1565
|
}
|
|
1375
|
-
/**
|
|
1376
|
-
* Render tokens to HTML
|
|
1377
|
-
*/
|
|
1378
1566
|
render(tokens) {
|
|
1379
1567
|
return this.renderer.render(tokens);
|
|
1380
1568
|
}
|
|
1381
|
-
/**
|
|
1382
|
-
* Parse and render markdown to HTML in one step
|
|
1383
|
-
*/
|
|
1384
1569
|
toHtml(markdown2) {
|
|
1385
1570
|
const tokens = this.parse(markdown2);
|
|
1386
1571
|
return this.render(tokens);
|
|
1387
1572
|
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Get list of registered extensions
|
|
1390
|
-
*/
|
|
1391
1573
|
getExtensions() {
|
|
1392
1574
|
return Array.from(this.extensions.keys());
|
|
1393
1575
|
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Check if extension is registered
|
|
1396
|
-
*/
|
|
1397
1576
|
hasExtension(name) {
|
|
1398
1577
|
return this.extensions.has(name);
|
|
1399
1578
|
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Get parser warnings
|
|
1402
|
-
*/
|
|
1403
1579
|
getWarnings() {
|
|
1404
1580
|
return [...this.parser.getWarnings(), ...this.renderer.getWarnings()];
|
|
1405
1581
|
}
|
|
1406
|
-
/**
|
|
1407
|
-
* Get debug information from last render
|
|
1408
|
-
*/
|
|
1409
1582
|
getDebugInfo() {
|
|
1410
1583
|
return {
|
|
1411
1584
|
warnings: this.getWarnings(),
|
|
@@ -1415,9 +1588,6 @@ var ChangerawrMarkdown = class {
|
|
|
1415
1588
|
iterationCount: 0
|
|
1416
1589
|
};
|
|
1417
1590
|
}
|
|
1418
|
-
/**
|
|
1419
|
-
* Get performance metrics for the last operation
|
|
1420
|
-
*/
|
|
1421
1591
|
getPerformanceMetrics() {
|
|
1422
1592
|
return {
|
|
1423
1593
|
parseTime: 0,
|
|
@@ -1426,28 +1596,29 @@ var ChangerawrMarkdown = class {
|
|
|
1426
1596
|
tokenCount: 0
|
|
1427
1597
|
};
|
|
1428
1598
|
}
|
|
1429
|
-
/**
|
|
1430
|
-
* Register built-in extensions
|
|
1431
|
-
*/
|
|
1432
|
-
registerBuiltInExtensions() {
|
|
1433
|
-
this.registerExtension(AlertExtension);
|
|
1434
|
-
this.registerExtension(ButtonExtension);
|
|
1435
|
-
this.registerExtension(EmbedExtension);
|
|
1436
|
-
}
|
|
1437
|
-
/**
|
|
1438
|
-
* Rebuild parser and renderer with current extensions
|
|
1439
|
-
*/
|
|
1440
1599
|
rebuildParserAndRenderer() {
|
|
1441
1600
|
const parserConfig = this.parser.getConfig();
|
|
1442
1601
|
const rendererConfig = this.renderer.getConfig();
|
|
1443
1602
|
this.parser = new MarkdownParser(parserConfig);
|
|
1444
1603
|
this.renderer = new MarkdownRenderer(rendererConfig);
|
|
1445
1604
|
const extensionsToRegister = Array.from(this.extensions.values());
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1605
|
+
const featureExtensions = extensionsToRegister.filter(
|
|
1606
|
+
(ext) => ["alert", "button", "embed"].includes(ext.name)
|
|
1607
|
+
);
|
|
1608
|
+
const coreExtensions = extensionsToRegister.filter(
|
|
1609
|
+
(ext) => ["text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(ext.name)
|
|
1610
|
+
);
|
|
1611
|
+
const customExtensions = extensionsToRegister.filter(
|
|
1612
|
+
(ext) => !["alert", "button", "embed", "text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(ext.name)
|
|
1613
|
+
);
|
|
1614
|
+
[...featureExtensions, ...coreExtensions, ...customExtensions].forEach((extension) => {
|
|
1615
|
+
extension.parseRules.forEach((rule) => {
|
|
1616
|
+
this.parser.addRule(rule);
|
|
1617
|
+
});
|
|
1618
|
+
extension.renderRules.forEach((rule) => {
|
|
1619
|
+
this.renderer.addRule(rule);
|
|
1620
|
+
});
|
|
1449
1621
|
});
|
|
1450
|
-
this.parser.setupDefaultRulesIfEmpty();
|
|
1451
1622
|
}
|
|
1452
1623
|
};
|
|
1453
1624
|
var markdown = new ChangerawrMarkdown();
|