@changerawr/markdown 1.0.5 → 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 +128 -10
- package/dist/index.d.mts +63 -47
- package/dist/index.d.ts +63 -47
- package/dist/index.js +677 -447
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +661 -447
- 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 -431
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +604 -431
- package/dist/react/index.mjs.map +1 -1
- package/dist/standalone.browser.js +610 -435
- 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
|
@@ -9,39 +9,56 @@ var MarkdownParser = class {
|
|
|
9
9
|
constructor(config) {
|
|
10
10
|
this.rules = [];
|
|
11
11
|
this.warnings = [];
|
|
12
|
-
this.config =
|
|
12
|
+
this.config = {
|
|
13
|
+
debugMode: false,
|
|
14
|
+
maxIterations: 1e4,
|
|
15
|
+
validateMarkdown: false,
|
|
16
|
+
...config
|
|
17
|
+
};
|
|
13
18
|
}
|
|
14
19
|
addRule(rule) {
|
|
15
20
|
this.rules.push(rule);
|
|
16
21
|
this.rules.sort((a, b) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
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;
|
|
23
38
|
return a.name.localeCompare(b.name);
|
|
24
39
|
});
|
|
25
40
|
}
|
|
26
|
-
setupDefaultRulesIfEmpty() {
|
|
27
|
-
const hasDefaultRules = this.rules.some(
|
|
28
|
-
(rule) => !["alert", "button", "embed"].includes(rule.name)
|
|
29
|
-
);
|
|
30
|
-
if (!hasDefaultRules) {
|
|
31
|
-
this.setupDefaultRules();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
41
|
hasRule(name) {
|
|
35
42
|
return this.rules.some((rule) => rule.name === name);
|
|
36
43
|
}
|
|
37
44
|
parse(markdown2) {
|
|
38
45
|
this.warnings = [];
|
|
39
|
-
|
|
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
|
+
}
|
|
40
57
|
const processedMarkdown = this.preprocessMarkdown(markdown2);
|
|
41
58
|
const tokens = [];
|
|
42
59
|
let remaining = processedMarkdown;
|
|
43
60
|
let iterationCount = 0;
|
|
44
|
-
const maxIterations = this.config.maxIterations ||
|
|
61
|
+
const maxIterations = this.config.maxIterations || 1e4;
|
|
45
62
|
while (remaining.length > 0 && iterationCount < maxIterations) {
|
|
46
63
|
iterationCount++;
|
|
47
64
|
let matched = false;
|
|
@@ -130,9 +147,6 @@ var MarkdownParser = class {
|
|
|
130
147
|
clearWarnings() {
|
|
131
148
|
this.warnings = [];
|
|
132
149
|
}
|
|
133
|
-
getIterationCount() {
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
150
|
preprocessMarkdown(markdown2) {
|
|
137
151
|
if (this.config.validateMarkdown) {
|
|
138
152
|
this.validateMarkdown(markdown2);
|
|
@@ -187,161 +201,6 @@ var MarkdownParser = class {
|
|
|
187
201
|
}
|
|
188
202
|
return processed;
|
|
189
203
|
}
|
|
190
|
-
setupDefaultRules() {
|
|
191
|
-
this.addRule({
|
|
192
|
-
name: "heading",
|
|
193
|
-
pattern: /^(#{1,6})\s+(.+)$/m,
|
|
194
|
-
render: (match) => ({
|
|
195
|
-
type: "heading",
|
|
196
|
-
content: match[2]?.trim() || "",
|
|
197
|
-
raw: match[0] || "",
|
|
198
|
-
attributes: {
|
|
199
|
-
level: String(match[1]?.length || 1)
|
|
200
|
-
}
|
|
201
|
-
})
|
|
202
|
-
});
|
|
203
|
-
this.addRule({
|
|
204
|
-
name: "codeblock",
|
|
205
|
-
pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
|
|
206
|
-
render: (match) => ({
|
|
207
|
-
type: "codeblock",
|
|
208
|
-
content: match[2] || "",
|
|
209
|
-
raw: match[0] || "",
|
|
210
|
-
attributes: {
|
|
211
|
-
language: match[1] || "text"
|
|
212
|
-
}
|
|
213
|
-
})
|
|
214
|
-
});
|
|
215
|
-
this.addRule({
|
|
216
|
-
name: "hard-break-backslash",
|
|
217
|
-
pattern: /\\\s*\n/,
|
|
218
|
-
render: (match) => ({
|
|
219
|
-
type: "line-break",
|
|
220
|
-
content: "",
|
|
221
|
-
raw: match[0] || ""
|
|
222
|
-
})
|
|
223
|
-
});
|
|
224
|
-
this.addRule({
|
|
225
|
-
name: "hard-break-spaces",
|
|
226
|
-
pattern: / +\n/,
|
|
227
|
-
render: (match) => ({
|
|
228
|
-
type: "line-break",
|
|
229
|
-
content: "",
|
|
230
|
-
raw: match[0] || ""
|
|
231
|
-
})
|
|
232
|
-
});
|
|
233
|
-
this.addRule({
|
|
234
|
-
name: "paragraph-break",
|
|
235
|
-
pattern: /\n\s*\n/,
|
|
236
|
-
render: (match) => ({
|
|
237
|
-
type: "paragraph-break",
|
|
238
|
-
content: "",
|
|
239
|
-
raw: match[0] || ""
|
|
240
|
-
})
|
|
241
|
-
});
|
|
242
|
-
this.addRule({
|
|
243
|
-
name: "bold",
|
|
244
|
-
pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
|
|
245
|
-
render: (match) => ({
|
|
246
|
-
type: "bold",
|
|
247
|
-
content: match[1] || "",
|
|
248
|
-
raw: match[0] || ""
|
|
249
|
-
})
|
|
250
|
-
});
|
|
251
|
-
this.addRule({
|
|
252
|
-
name: "italic",
|
|
253
|
-
pattern: /\*((?:(?!\*).)+)\*/,
|
|
254
|
-
render: (match) => ({
|
|
255
|
-
type: "italic",
|
|
256
|
-
content: match[1] || "",
|
|
257
|
-
raw: match[0] || ""
|
|
258
|
-
})
|
|
259
|
-
});
|
|
260
|
-
this.addRule({
|
|
261
|
-
name: "code",
|
|
262
|
-
pattern: /`([^`]+)`/,
|
|
263
|
-
render: (match) => ({
|
|
264
|
-
type: "code",
|
|
265
|
-
content: match[1] || "",
|
|
266
|
-
raw: match[0] || ""
|
|
267
|
-
})
|
|
268
|
-
});
|
|
269
|
-
this.addRule({
|
|
270
|
-
name: "image",
|
|
271
|
-
pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
|
|
272
|
-
render: (match) => ({
|
|
273
|
-
type: "image",
|
|
274
|
-
content: match[1] || "",
|
|
275
|
-
raw: match[0] || "",
|
|
276
|
-
attributes: {
|
|
277
|
-
alt: match[1] || "",
|
|
278
|
-
src: match[2] || "",
|
|
279
|
-
title: match[3] || ""
|
|
280
|
-
}
|
|
281
|
-
})
|
|
282
|
-
});
|
|
283
|
-
this.addRule({
|
|
284
|
-
name: "link",
|
|
285
|
-
pattern: /\[([^\]]+)\]\(([^)]+)\)/,
|
|
286
|
-
render: (match) => ({
|
|
287
|
-
type: "link",
|
|
288
|
-
content: match[1] || "",
|
|
289
|
-
raw: match[0] || "",
|
|
290
|
-
attributes: {
|
|
291
|
-
href: match[2] || ""
|
|
292
|
-
}
|
|
293
|
-
})
|
|
294
|
-
});
|
|
295
|
-
this.addRule({
|
|
296
|
-
name: "task-list",
|
|
297
|
-
pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
|
|
298
|
-
render: (match) => ({
|
|
299
|
-
type: "task-item",
|
|
300
|
-
content: match[3] || "",
|
|
301
|
-
raw: match[0] || "",
|
|
302
|
-
attributes: {
|
|
303
|
-
checked: String((match[2] || "").toLowerCase() === "x")
|
|
304
|
-
}
|
|
305
|
-
})
|
|
306
|
-
});
|
|
307
|
-
this.addRule({
|
|
308
|
-
name: "list",
|
|
309
|
-
pattern: /^(\s*)[-*+]\s+(.+)$/m,
|
|
310
|
-
render: (match) => ({
|
|
311
|
-
type: "list-item",
|
|
312
|
-
content: match[2] || "",
|
|
313
|
-
raw: match[0] || ""
|
|
314
|
-
})
|
|
315
|
-
});
|
|
316
|
-
this.addRule({
|
|
317
|
-
name: "blockquote",
|
|
318
|
-
pattern: /^>\s+(.+)$/m,
|
|
319
|
-
render: (match) => ({
|
|
320
|
-
type: "blockquote",
|
|
321
|
-
content: match[1] || "",
|
|
322
|
-
raw: match[0] || ""
|
|
323
|
-
})
|
|
324
|
-
});
|
|
325
|
-
this.addRule({
|
|
326
|
-
name: "hr",
|
|
327
|
-
pattern: /^---$/m,
|
|
328
|
-
render: (match) => ({
|
|
329
|
-
type: "hr",
|
|
330
|
-
content: "",
|
|
331
|
-
raw: match[0] || ""
|
|
332
|
-
})
|
|
333
|
-
});
|
|
334
|
-
this.addRule({
|
|
335
|
-
name: "soft-break",
|
|
336
|
-
pattern: /\n/,
|
|
337
|
-
render: (match) => ({
|
|
338
|
-
type: "soft-break",
|
|
339
|
-
content: " ",
|
|
340
|
-
// Convert to space for inline text
|
|
341
|
-
raw: match[0] || ""
|
|
342
|
-
})
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
204
|
};
|
|
346
205
|
|
|
347
206
|
// src/utils.ts
|
|
@@ -580,7 +439,6 @@ var MarkdownRenderer = class {
|
|
|
580
439
|
debugMode: false,
|
|
581
440
|
...config
|
|
582
441
|
};
|
|
583
|
-
this.setupDefaultRules();
|
|
584
442
|
}
|
|
585
443
|
addRule(rule) {
|
|
586
444
|
this.rules.set(rule.type, rule);
|
|
@@ -590,28 +448,20 @@ var MarkdownRenderer = class {
|
|
|
590
448
|
}
|
|
591
449
|
render(tokens) {
|
|
592
450
|
this.warnings = [];
|
|
593
|
-
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));
|
|
594
459
|
const combinedHtml = htmlParts.join("");
|
|
595
460
|
if (this.config.sanitize && !this.config.allowUnsafeHtml) {
|
|
596
461
|
return sanitizeHtml(combinedHtml);
|
|
597
462
|
}
|
|
598
463
|
return combinedHtml;
|
|
599
464
|
}
|
|
600
|
-
getWarnings() {
|
|
601
|
-
return [...this.warnings];
|
|
602
|
-
}
|
|
603
|
-
getConfig() {
|
|
604
|
-
return { ...this.config };
|
|
605
|
-
}
|
|
606
|
-
updateConfig(config) {
|
|
607
|
-
this.config = { ...this.config, ...config };
|
|
608
|
-
}
|
|
609
|
-
setDebugMode(enabled) {
|
|
610
|
-
this.config.debugMode = enabled;
|
|
611
|
-
}
|
|
612
|
-
clearWarnings() {
|
|
613
|
-
this.warnings = [];
|
|
614
|
-
}
|
|
615
465
|
renderToken(token) {
|
|
616
466
|
const rule = this.rules.get(token.type);
|
|
617
467
|
if (rule) {
|
|
@@ -623,9 +473,6 @@ var MarkdownRenderer = class {
|
|
|
623
473
|
return this.createErrorBlock(`Render error for ${token.type}: ${errorMessage}`);
|
|
624
474
|
}
|
|
625
475
|
}
|
|
626
|
-
if (token.type === "text") {
|
|
627
|
-
return escapeHtml(token.content || token.raw || "");
|
|
628
|
-
}
|
|
629
476
|
if (this.config.debugMode) {
|
|
630
477
|
return this.createDebugBlock(token);
|
|
631
478
|
}
|
|
@@ -648,20 +495,239 @@ var MarkdownRenderer = class {
|
|
|
648
495
|
<strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
|
|
649
496
|
</div>`;
|
|
650
497
|
}
|
|
651
|
-
return `<div class="bg-yellow-100 border border-yellow-300 text-yellow-800 p-2 rounded text-sm mb-2">
|
|
652
|
-
<strong>Unknown token type:</strong> ${escapeHtml(token.type)}<br>
|
|
653
|
-
<strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
|
|
654
|
-
</div>`;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
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
|
+
{
|
|
658
723
|
type: "heading",
|
|
659
724
|
render: (token) => {
|
|
660
725
|
const level = parseInt(token.attributes?.level || "1");
|
|
661
726
|
const text = token.content;
|
|
662
727
|
const id = generateId(text);
|
|
663
728
|
const escapedContent = escapeHtml(text);
|
|
664
|
-
|
|
729
|
+
const format = token.attributes?.format || "html";
|
|
730
|
+
if (format === "html") {
|
|
665
731
|
return `<h${level} id="${id}">${escapedContent}</h${level}>`;
|
|
666
732
|
}
|
|
667
733
|
let headingClasses = "group relative flex items-center gap-2";
|
|
@@ -688,61 +754,108 @@ var MarkdownRenderer = class {
|
|
|
688
754
|
return `<h${level} id="${id}" class="${headingClasses}">
|
|
689
755
|
${escapedContent}
|
|
690
756
|
<a href="#${id}" class="opacity-0 group-hover:opacity-100 text-muted-foreground transition-opacity">
|
|
691
|
-
<svg width="16" height="16" viewBox="0 0
|
|
692
|
-
|
|
693
|
-
|
|
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>
|
|
694
761
|
</a>
|
|
695
762
|
</h${level}>`;
|
|
696
763
|
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
|
|
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",
|
|
700
785
|
render: (token) => {
|
|
701
|
-
const
|
|
702
|
-
if (
|
|
703
|
-
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;">';
|
|
704
789
|
}
|
|
705
|
-
return
|
|
790
|
+
return '<hr class="my-6 border-t border-border">';
|
|
706
791
|
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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] || ""
|
|
714
811
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
812
|
+
})
|
|
813
|
+
}
|
|
814
|
+
],
|
|
815
|
+
renderRules: [
|
|
816
|
+
{
|
|
817
|
+
type: "image",
|
|
720
818
|
render: (token) => {
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
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" />`;
|
|
724
826
|
}
|
|
725
|
-
return `<
|
|
827
|
+
return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} class="max-w-full h-auto rounded-lg my-4" loading="lazy" />`;
|
|
726
828
|
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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] || ""
|
|
735
846
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
847
|
+
})
|
|
848
|
+
}
|
|
849
|
+
],
|
|
850
|
+
renderRules: [
|
|
851
|
+
{
|
|
740
852
|
type: "link",
|
|
741
853
|
render: (token) => {
|
|
742
854
|
const href = token.attributes?.href || "#";
|
|
743
855
|
const escapedHref = escapeHtml(href);
|
|
744
856
|
const escapedText = escapeHtml(token.content);
|
|
745
|
-
|
|
857
|
+
const format = token.attributes?.format || "html";
|
|
858
|
+
if (format === "html") {
|
|
746
859
|
return `<a href="${escapedHref}" target="_blank" rel="noopener noreferrer">${escapedText}</a>`;
|
|
747
860
|
}
|
|
748
861
|
return `<a href="${escapedHref}" class="text-primary hover:underline inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
|
|
@@ -754,47 +867,55 @@ var MarkdownRenderer = class {
|
|
|
754
867
|
</svg>
|
|
755
868
|
</a>`;
|
|
756
869
|
}
|
|
757
|
-
}
|
|
758
|
-
|
|
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
|
+
{
|
|
759
890
|
type: "list-item",
|
|
760
891
|
render: (token) => `<li>${escapeHtml(token.content)}</li>`
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return escapeHtml(token.content);
|
|
777
|
-
}
|
|
778
|
-
});
|
|
779
|
-
this.addRule({
|
|
780
|
-
type: "paragraph",
|
|
781
|
-
render: (token) => {
|
|
782
|
-
if (!token.content) return "";
|
|
783
|
-
const content = token.content.trim();
|
|
784
|
-
if (!content) return "";
|
|
785
|
-
const processedContent = content.includes("<br>") ? content : escapeHtml(content);
|
|
786
|
-
if (this.config.format === "html") {
|
|
787
|
-
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")
|
|
788
907
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
908
|
+
})
|
|
909
|
+
}
|
|
910
|
+
],
|
|
911
|
+
renderRules: [
|
|
912
|
+
{
|
|
793
913
|
type: "task-item",
|
|
794
914
|
render: (token) => {
|
|
795
915
|
const isChecked = token.attributes?.checked === "true";
|
|
796
916
|
const escapedContent = escapeHtml(token.content);
|
|
797
|
-
|
|
917
|
+
const format = token.attributes?.format || "html";
|
|
918
|
+
if (format === "html") {
|
|
798
919
|
return `<div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
|
|
799
920
|
<input type="checkbox" ${isChecked ? "checked" : ""} disabled style="margin: 0;" />
|
|
800
921
|
<span${isChecked ? ' style="text-decoration: line-through; color: #6b7280;"' : ""}>${escapedContent}</span>
|
|
@@ -806,44 +927,65 @@ var MarkdownRenderer = class {
|
|
|
806
927
|
<span${isChecked ? ' class="line-through text-muted-foreground"' : ""}>${escapedContent}</span>
|
|
807
928
|
</div>`;
|
|
808
929
|
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
|
|
930
|
+
}
|
|
931
|
+
]
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
// src/extensions/core/paragraph.ts
|
|
935
|
+
var ParagraphExtension = {
|
|
936
|
+
name: "paragraph",
|
|
937
|
+
parseRules: [],
|
|
938
|
+
renderRules: [
|
|
939
|
+
{
|
|
940
|
+
type: "paragraph",
|
|
812
941
|
render: (token) => {
|
|
813
|
-
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
|
|
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>`;
|
|
819
949
|
}
|
|
820
|
-
return `<
|
|
950
|
+
return `<p class="leading-7 mb-4">${processedContent}</p>`;
|
|
821
951
|
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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);
|
|
830
966
|
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
type: "line-break",
|
|
834
|
-
render: () => "<br>"
|
|
835
|
-
});
|
|
836
|
-
this.addRule({
|
|
837
|
-
type: "paragraph-break",
|
|
838
|
-
render: () => "</p><p>"
|
|
839
|
-
});
|
|
840
|
-
this.addRule({
|
|
841
|
-
type: "soft-break",
|
|
842
|
-
render: (token) => token.content || " "
|
|
843
|
-
});
|
|
844
|
-
}
|
|
967
|
+
}
|
|
968
|
+
]
|
|
845
969
|
};
|
|
846
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
|
+
|
|
847
989
|
// src/extensions/alert.ts
|
|
848
990
|
var AlertExtension = {
|
|
849
991
|
name: "alert",
|
|
@@ -923,26 +1065,19 @@ var ButtonExtension = {
|
|
|
923
1065
|
name: "button",
|
|
924
1066
|
pattern: /\[button:([^\]]+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
|
|
925
1067
|
render: (match) => {
|
|
926
|
-
const
|
|
927
|
-
const href = match[2] || "";
|
|
928
|
-
const optionsString = match[3] || "";
|
|
929
|
-
const options = optionsString.split(",").map((opt) => opt.trim()).filter(Boolean);
|
|
930
|
-
const styleOptions = ["default", "primary", "secondary", "success", "danger", "outline", "ghost"];
|
|
931
|
-
const style = options.find((opt) => styleOptions.includes(opt)) || "primary";
|
|
932
|
-
const sizeOptions = ["sm", "md", "lg"];
|
|
933
|
-
const size = options.find((opt) => sizeOptions.includes(opt)) || "md";
|
|
934
|
-
const disabled = options.includes("disabled");
|
|
935
|
-
const target = options.includes("self") ? "_self" : "_blank";
|
|
1068
|
+
const options = match[3] ? match[3].split(",").map((opt) => opt.trim()) : [];
|
|
936
1069
|
return {
|
|
937
1070
|
type: "button",
|
|
938
|
-
content:
|
|
1071
|
+
content: match[1] || "",
|
|
939
1072
|
raw: match[0] || "",
|
|
940
1073
|
attributes: {
|
|
941
|
-
href,
|
|
942
|
-
style
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
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"
|
|
946
1081
|
}
|
|
947
1082
|
};
|
|
948
1083
|
}
|
|
@@ -957,53 +1092,92 @@ var ButtonExtension = {
|
|
|
957
1092
|
const size = token.attributes?.size || "md";
|
|
958
1093
|
const disabled = token.attributes?.disabled === "true";
|
|
959
1094
|
const target = token.attributes?.target || "_blank";
|
|
960
|
-
const
|
|
961
|
-
|
|
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();
|
|
962
1169
|
const targetAttr = target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : target === "_self" ? ' target="_self"' : "";
|
|
963
1170
|
const disabledAttr = disabled ? ' aria-disabled="true" tabindex="-1"' : "";
|
|
964
|
-
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>` : "";
|
|
965
1174
|
return `<a href="${href}" class="${classes}"${targetAttr}${disabledAttr}>
|
|
966
|
-
|
|
967
|
-
|
|
1175
|
+
<span class="relative z-10">${token.content}</span>${externalIcon}
|
|
1176
|
+
</a>`;
|
|
968
1177
|
}
|
|
969
1178
|
}
|
|
970
1179
|
]
|
|
971
1180
|
};
|
|
972
|
-
function buildButtonClasses(style, size) {
|
|
973
|
-
const base = [
|
|
974
|
-
"inline-flex",
|
|
975
|
-
"items-center",
|
|
976
|
-
"justify-center",
|
|
977
|
-
"font-medium",
|
|
978
|
-
"rounded-lg",
|
|
979
|
-
"transition-colors",
|
|
980
|
-
"focus:outline-none",
|
|
981
|
-
"focus:ring-2",
|
|
982
|
-
"focus:ring-offset-2",
|
|
983
|
-
"disabled:opacity-50",
|
|
984
|
-
"disabled:cursor-not-allowed"
|
|
985
|
-
];
|
|
986
|
-
const sizes = {
|
|
987
|
-
sm: ["px-3", "py-1.5", "text-sm"],
|
|
988
|
-
md: ["px-4", "py-2", "text-base"],
|
|
989
|
-
lg: ["px-6", "py-3", "text-lg"]
|
|
990
|
-
};
|
|
991
|
-
const styles = {
|
|
992
|
-
default: ["bg-slate-600", "text-white", "hover:bg-slate-700", "focus:ring-slate-500"],
|
|
993
|
-
primary: ["bg-blue-600", "text-white", "hover:bg-blue-700", "focus:ring-blue-500"],
|
|
994
|
-
secondary: ["bg-gray-600", "text-white", "hover:bg-gray-700", "focus:ring-gray-500"],
|
|
995
|
-
success: ["bg-green-600", "text-white", "hover:bg-green-700", "focus:ring-green-500"],
|
|
996
|
-
danger: ["bg-red-600", "text-white", "hover:bg-red-700", "focus:ring-red-500"],
|
|
997
|
-
outline: ["border", "border-blue-600", "text-blue-600", "hover:bg-blue-50", "focus:ring-blue-500"],
|
|
998
|
-
ghost: ["text-gray-700", "hover:bg-gray-100", "focus:ring-gray-500"]
|
|
999
|
-
};
|
|
1000
|
-
const allClasses = [
|
|
1001
|
-
...base,
|
|
1002
|
-
...sizes[size] ?? sizes.md ?? [],
|
|
1003
|
-
...styles[style] ?? styles.primary ?? []
|
|
1004
|
-
];
|
|
1005
|
-
return allClasses.join(" ");
|
|
1006
|
-
}
|
|
1007
1181
|
|
|
1008
1182
|
// src/extensions/embed.ts
|
|
1009
1183
|
var EmbedExtension = {
|
|
@@ -1081,8 +1255,7 @@ function renderYouTubeEmbed(url, options, classes) {
|
|
|
1081
1255
|
params.set("rel", "0");
|
|
1082
1256
|
params.set("modestbranding", "1");
|
|
1083
1257
|
const embedUrl = `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
|
|
1084
|
-
return
|
|
1085
|
-
<div class="${classes}">
|
|
1258
|
+
return `<div class="${classes}">
|
|
1086
1259
|
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
|
|
1087
1260
|
<iframe
|
|
1088
1261
|
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
|
|
@@ -1110,8 +1283,7 @@ function renderCodePenEmbed(url, options, classes) {
|
|
|
1110
1283
|
"editable": "true"
|
|
1111
1284
|
});
|
|
1112
1285
|
const embedUrl = `https://codepen.io/${user}/embed/${penId}?${embedParams.toString()}`;
|
|
1113
|
-
return
|
|
1114
|
-
<div class="${classes}">
|
|
1286
|
+
return `<div class="${classes}">
|
|
1115
1287
|
<iframe
|
|
1116
1288
|
height="${height}"
|
|
1117
1289
|
style="width: 100%; border: 0;"
|
|
@@ -1135,8 +1307,7 @@ function renderVimeoEmbed(url, options, classes) {
|
|
|
1135
1307
|
if (options.mute === "1") params.set("muted", "1");
|
|
1136
1308
|
if (options.loop === "1") params.set("loop", "1");
|
|
1137
1309
|
const embedUrl = `https://player.vimeo.com/video/${videoId}?${params.toString()}`;
|
|
1138
|
-
return
|
|
1139
|
-
<div class="${classes}">
|
|
1310
|
+
return `<div class="${classes}">
|
|
1140
1311
|
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
|
|
1141
1312
|
<iframe
|
|
1142
1313
|
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
|
|
@@ -1152,8 +1323,7 @@ function renderVimeoEmbed(url, options, classes) {
|
|
|
1152
1323
|
function renderSpotifyEmbed(url, options, classes) {
|
|
1153
1324
|
const embedUrl = url.replace("open.spotify.com", "open.spotify.com/embed");
|
|
1154
1325
|
const height = options.height || "380";
|
|
1155
|
-
return
|
|
1156
|
-
<div class="${classes}">
|
|
1326
|
+
return `<div class="${classes}">
|
|
1157
1327
|
<iframe
|
|
1158
1328
|
style="border-radius: 12px;"
|
|
1159
1329
|
src="${embedUrl}"
|
|
@@ -1176,8 +1346,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
|
|
|
1176
1346
|
if (!embedUrl.includes("?")) {
|
|
1177
1347
|
embedUrl += `?view=${view}`;
|
|
1178
1348
|
}
|
|
1179
|
-
return
|
|
1180
|
-
<div class="${classes}">
|
|
1349
|
+
return `<div class="${classes}">
|
|
1181
1350
|
<iframe
|
|
1182
1351
|
src="${embedUrl}"
|
|
1183
1352
|
style="width: 100%; height: ${height}px; border: 0; border-radius: 4px; overflow: hidden;"
|
|
@@ -1190,8 +1359,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
|
|
|
1190
1359
|
function renderFigmaEmbed(url, options, classes) {
|
|
1191
1360
|
const embedUrl = `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
|
|
1192
1361
|
const height = options.height || "450";
|
|
1193
|
-
return
|
|
1194
|
-
<div class="${classes}">
|
|
1362
|
+
return `<div class="${classes}">
|
|
1195
1363
|
<iframe
|
|
1196
1364
|
style="border: none;"
|
|
1197
1365
|
width="100%"
|
|
@@ -1202,8 +1370,7 @@ function renderFigmaEmbed(url, options, classes) {
|
|
|
1202
1370
|
</div>`;
|
|
1203
1371
|
}
|
|
1204
1372
|
function renderTwitterEmbed(url, _options, classes) {
|
|
1205
|
-
return
|
|
1206
|
-
<div class="${classes}">
|
|
1373
|
+
return `<div class="${classes}">
|
|
1207
1374
|
<div class="p-4">
|
|
1208
1375
|
<div class="flex items-center gap-3 mb-3">
|
|
1209
1376
|
<svg class="w-6 h-6 fill-current text-blue-500" viewBox="0 0 24 24">
|
|
@@ -1231,8 +1398,7 @@ function renderGitHubEmbed(url, _options, classes) {
|
|
|
1231
1398
|
if (!owner || !repo) {
|
|
1232
1399
|
return createErrorEmbed("Invalid GitHub URL", url, classes);
|
|
1233
1400
|
}
|
|
1234
|
-
return
|
|
1235
|
-
<div class="${classes}">
|
|
1401
|
+
return `<div class="${classes}">
|
|
1236
1402
|
<div class="p-4">
|
|
1237
1403
|
<div class="flex items-center gap-3 mb-3">
|
|
1238
1404
|
<svg class="w-6 h-6 fill-current" viewBox="0 0 24 24">
|
|
@@ -1255,8 +1421,7 @@ function renderGitHubEmbed(url, _options, classes) {
|
|
|
1255
1421
|
}
|
|
1256
1422
|
function renderGenericEmbed(url, _options, classes) {
|
|
1257
1423
|
const domain = extractDomain(url);
|
|
1258
|
-
return
|
|
1259
|
-
<div class="${classes}">
|
|
1424
|
+
return `<div class="${classes}">
|
|
1260
1425
|
<div class="p-4">
|
|
1261
1426
|
<div class="flex items-center gap-3 mb-3">
|
|
1262
1427
|
<div class="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
|
|
@@ -1280,8 +1445,7 @@ function renderGenericEmbed(url, _options, classes) {
|
|
|
1280
1445
|
</div>`;
|
|
1281
1446
|
}
|
|
1282
1447
|
function createErrorEmbed(error, url, classes) {
|
|
1283
|
-
return
|
|
1284
|
-
<div class="${classes}">
|
|
1448
|
+
return `<div class="${classes}">
|
|
1285
1449
|
<div class="p-4 text-destructive">
|
|
1286
1450
|
<div class="font-medium flex items-center gap-2">
|
|
1287
1451
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -1315,19 +1479,54 @@ var ChangerawrMarkdown = class {
|
|
|
1315
1479
|
this.extensions = /* @__PURE__ */ new Map();
|
|
1316
1480
|
this.parser = new MarkdownParser(config?.parser);
|
|
1317
1481
|
this.renderer = new MarkdownRenderer(config?.renderer);
|
|
1318
|
-
this.
|
|
1482
|
+
this.registerCoreExtensions();
|
|
1483
|
+
this.registerFeatureExtensions();
|
|
1319
1484
|
if (config?.extensions) {
|
|
1320
1485
|
config.extensions.forEach((extension) => {
|
|
1321
1486
|
this.registerExtension(extension);
|
|
1322
1487
|
});
|
|
1323
1488
|
}
|
|
1324
|
-
this.parser.setupDefaultRulesIfEmpty();
|
|
1325
1489
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
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
|
+
}
|
|
1329
1500
|
registerExtension(extension) {
|
|
1330
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
|
+
});
|
|
1331
1530
|
this.extensions.set(extension.name, extension);
|
|
1332
1531
|
extension.parseRules.forEach((rule) => {
|
|
1333
1532
|
this.parser.addRule(rule);
|
|
@@ -1348,9 +1547,6 @@ var ChangerawrMarkdown = class {
|
|
|
1348
1547
|
};
|
|
1349
1548
|
}
|
|
1350
1549
|
}
|
|
1351
|
-
/**
|
|
1352
|
-
* Unregister an extension
|
|
1353
|
-
*/
|
|
1354
1550
|
unregisterExtension(name) {
|
|
1355
1551
|
const extension = this.extensions.get(name);
|
|
1356
1552
|
if (!extension) {
|
|
@@ -1364,46 +1560,25 @@ var ChangerawrMarkdown = class {
|
|
|
1364
1560
|
return false;
|
|
1365
1561
|
}
|
|
1366
1562
|
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Parse markdown content into tokens
|
|
1369
|
-
*/
|
|
1370
1563
|
parse(markdown2) {
|
|
1371
1564
|
return this.parser.parse(markdown2);
|
|
1372
1565
|
}
|
|
1373
|
-
/**
|
|
1374
|
-
* Render tokens to HTML
|
|
1375
|
-
*/
|
|
1376
1566
|
render(tokens) {
|
|
1377
1567
|
return this.renderer.render(tokens);
|
|
1378
1568
|
}
|
|
1379
|
-
/**
|
|
1380
|
-
* Parse and render markdown to HTML in one step
|
|
1381
|
-
*/
|
|
1382
1569
|
toHtml(markdown2) {
|
|
1383
1570
|
const tokens = this.parse(markdown2);
|
|
1384
1571
|
return this.render(tokens);
|
|
1385
1572
|
}
|
|
1386
|
-
/**
|
|
1387
|
-
* Get list of registered extensions
|
|
1388
|
-
*/
|
|
1389
1573
|
getExtensions() {
|
|
1390
1574
|
return Array.from(this.extensions.keys());
|
|
1391
1575
|
}
|
|
1392
|
-
/**
|
|
1393
|
-
* Check if extension is registered
|
|
1394
|
-
*/
|
|
1395
1576
|
hasExtension(name) {
|
|
1396
1577
|
return this.extensions.has(name);
|
|
1397
1578
|
}
|
|
1398
|
-
/**
|
|
1399
|
-
* Get parser warnings
|
|
1400
|
-
*/
|
|
1401
1579
|
getWarnings() {
|
|
1402
1580
|
return [...this.parser.getWarnings(), ...this.renderer.getWarnings()];
|
|
1403
1581
|
}
|
|
1404
|
-
/**
|
|
1405
|
-
* Get debug information from last render
|
|
1406
|
-
*/
|
|
1407
1582
|
getDebugInfo() {
|
|
1408
1583
|
return {
|
|
1409
1584
|
warnings: this.getWarnings(),
|
|
@@ -1413,9 +1588,6 @@ var ChangerawrMarkdown = class {
|
|
|
1413
1588
|
iterationCount: 0
|
|
1414
1589
|
};
|
|
1415
1590
|
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Get performance metrics for the last operation
|
|
1418
|
-
*/
|
|
1419
1591
|
getPerformanceMetrics() {
|
|
1420
1592
|
return {
|
|
1421
1593
|
parseTime: 0,
|
|
@@ -1424,28 +1596,29 @@ var ChangerawrMarkdown = class {
|
|
|
1424
1596
|
tokenCount: 0
|
|
1425
1597
|
};
|
|
1426
1598
|
}
|
|
1427
|
-
/**
|
|
1428
|
-
* Register built-in extensions
|
|
1429
|
-
*/
|
|
1430
|
-
registerBuiltInExtensions() {
|
|
1431
|
-
this.registerExtension(AlertExtension);
|
|
1432
|
-
this.registerExtension(ButtonExtension);
|
|
1433
|
-
this.registerExtension(EmbedExtension);
|
|
1434
|
-
}
|
|
1435
|
-
/**
|
|
1436
|
-
* Rebuild parser and renderer with current extensions
|
|
1437
|
-
*/
|
|
1438
1599
|
rebuildParserAndRenderer() {
|
|
1439
1600
|
const parserConfig = this.parser.getConfig();
|
|
1440
1601
|
const rendererConfig = this.renderer.getConfig();
|
|
1441
1602
|
this.parser = new MarkdownParser(parserConfig);
|
|
1442
1603
|
this.renderer = new MarkdownRenderer(rendererConfig);
|
|
1443
1604
|
const extensionsToRegister = Array.from(this.extensions.values());
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
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
|
+
});
|
|
1447
1621
|
});
|
|
1448
|
-
this.parser.setupDefaultRulesIfEmpty();
|
|
1449
1622
|
}
|
|
1450
1623
|
};
|
|
1451
1624
|
var markdown = new ChangerawrMarkdown();
|