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