@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/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- "use client";
2
1
  "use strict";
3
2
  var __create = Object.create;
4
3
  var __defProp = Object.defineProperty;
@@ -32,17 +31,33 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
31
  var index_exports = {};
33
32
  __export(index_exports, {
34
33
  AlertExtension: () => AlertExtension,
34
+ BlockquoteExtension: () => BlockquoteExtension,
35
+ BoldExtension: () => BoldExtension,
35
36
  ButtonExtension: () => ButtonExtension,
36
37
  ChangerawrMarkdown: () => ChangerawrMarkdown,
38
+ CodeBlockExtension: () => CodeBlockExtension,
39
+ CoreExtensions: () => CoreExtensions,
37
40
  EmbedExtension: () => EmbedExtension,
41
+ HeadingExtension: () => HeadingExtension,
42
+ HorizontalRuleExtension: () => HorizontalRuleExtension,
43
+ ImageExtension: () => ImageExtension,
44
+ InlineCodeExtension: () => InlineCodeExtension,
45
+ ItalicExtension: () => ItalicExtension,
46
+ LineBreakExtension: () => LineBreakExtension,
47
+ LinkExtension: () => LinkExtension,
48
+ ListExtension: () => ListExtension,
38
49
  Logger: () => Logger,
39
50
  MarkdownParser: () => MarkdownParser,
40
51
  MarkdownRenderer: () => MarkdownRenderer,
52
+ ParagraphExtension: () => ParagraphExtension,
41
53
  PerformanceTimer: () => PerformanceTimer,
54
+ TaskListExtension: () => TaskListExtension,
55
+ TextExtension: () => TextExtension,
42
56
  astToJSONString: () => astToJSONString,
43
57
  astToTokens: () => astToTokens,
44
58
  basicSanitize: () => basicSanitize,
45
59
  compareTokens: () => compareTokens,
60
+ createCoreOnlyEngine: () => createCoreOnlyEngine,
46
61
  createCumEngine: () => createCumEngine,
47
62
  createCustomEngine: () => createCustomEngine,
48
63
  createDebugEngine: () => createDebugEngine,
@@ -96,39 +111,56 @@ var MarkdownParser = class {
96
111
  constructor(config) {
97
112
  this.rules = [];
98
113
  this.warnings = [];
99
- this.config = config || {};
114
+ this.config = {
115
+ debugMode: false,
116
+ maxIterations: 1e4,
117
+ validateMarkdown: false,
118
+ ...config
119
+ };
100
120
  }
101
121
  addRule(rule) {
102
122
  this.rules.push(rule);
103
123
  this.rules.sort((a, b) => {
104
- if (a.name.includes("alert") || a.name.includes("embed") || a.name.includes("button")) return -1;
105
- if (b.name.includes("alert") || b.name.includes("embed") || b.name.includes("button")) return 1;
106
- if (a.name === "task-list") return -1;
107
- if (b.name === "task-list") return 1;
108
- if (a.name === "list" && b.name === "task-list") return 1;
109
- if (b.name === "list" && a.name === "task-list") return -1;
124
+ const aFeatureExtension = ["alert", "button", "embed"].includes(a.name);
125
+ const bFeatureExtension = ["alert", "button", "embed"].includes(b.name);
126
+ if (aFeatureExtension && !bFeatureExtension) return -1;
127
+ if (!aFeatureExtension && bFeatureExtension) return 1;
128
+ const aCoreExtension = ["text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(a.name);
129
+ const bCoreExtension = ["text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(b.name);
130
+ if (aCoreExtension && !bCoreExtension) return -1;
131
+ if (!aCoreExtension && bCoreExtension) return 1;
132
+ if (a.name === "image" && b.name === "link") return -1;
133
+ if (a.name === "link" && b.name === "image") return 1;
134
+ if (a.name === "task-item" && b.name === "list-item") return -1;
135
+ if (a.name === "list-item" && b.name === "task-item") return 1;
136
+ if (a.name === "codeblock" && b.name === "code") return -1;
137
+ if (a.name === "code" && b.name === "codeblock") return 1;
138
+ if (a.name === "bold" && b.name === "italic") return -1;
139
+ if (a.name === "italic" && b.name === "bold") return 1;
110
140
  return a.name.localeCompare(b.name);
111
141
  });
112
142
  }
113
- setupDefaultRulesIfEmpty() {
114
- const hasDefaultRules = this.rules.some(
115
- (rule) => !["alert", "button", "embed"].includes(rule.name)
116
- );
117
- if (!hasDefaultRules) {
118
- this.setupDefaultRules();
119
- }
120
- }
121
143
  hasRule(name) {
122
144
  return this.rules.some((rule) => rule.name === name);
123
145
  }
124
146
  parse(markdown3) {
125
147
  this.warnings = [];
126
- this.setupDefaultRulesIfEmpty();
148
+ if (!markdown3.trim()) {
149
+ return [];
150
+ }
151
+ if (this.rules.length === 0) {
152
+ this.warnings.push("No parse rules registered - consider using CoreExtensions");
153
+ return [{
154
+ type: "text",
155
+ content: markdown3,
156
+ raw: markdown3
157
+ }];
158
+ }
127
159
  const processedMarkdown = this.preprocessMarkdown(markdown3);
128
160
  const tokens = [];
129
161
  let remaining = processedMarkdown;
130
162
  let iterationCount = 0;
131
- const maxIterations = this.config.maxIterations || markdown3.length * 2;
163
+ const maxIterations = this.config.maxIterations || 1e4;
132
164
  while (remaining.length > 0 && iterationCount < maxIterations) {
133
165
  iterationCount++;
134
166
  let matched = false;
@@ -217,9 +249,6 @@ var MarkdownParser = class {
217
249
  clearWarnings() {
218
250
  this.warnings = [];
219
251
  }
220
- getIterationCount() {
221
- return 0;
222
- }
223
252
  preprocessMarkdown(markdown3) {
224
253
  if (this.config.validateMarkdown) {
225
254
  this.validateMarkdown(markdown3);
@@ -274,161 +303,6 @@ var MarkdownParser = class {
274
303
  }
275
304
  return processed;
276
305
  }
277
- setupDefaultRules() {
278
- this.addRule({
279
- name: "heading",
280
- pattern: /^(#{1,6})\s+(.+)$/m,
281
- render: (match) => ({
282
- type: "heading",
283
- content: match[2]?.trim() || "",
284
- raw: match[0] || "",
285
- attributes: {
286
- level: String(match[1]?.length || 1)
287
- }
288
- })
289
- });
290
- this.addRule({
291
- name: "codeblock",
292
- pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
293
- render: (match) => ({
294
- type: "codeblock",
295
- content: match[2] || "",
296
- raw: match[0] || "",
297
- attributes: {
298
- language: match[1] || "text"
299
- }
300
- })
301
- });
302
- this.addRule({
303
- name: "hard-break-backslash",
304
- pattern: /\\\s*\n/,
305
- render: (match) => ({
306
- type: "line-break",
307
- content: "",
308
- raw: match[0] || ""
309
- })
310
- });
311
- this.addRule({
312
- name: "hard-break-spaces",
313
- pattern: / +\n/,
314
- render: (match) => ({
315
- type: "line-break",
316
- content: "",
317
- raw: match[0] || ""
318
- })
319
- });
320
- this.addRule({
321
- name: "paragraph-break",
322
- pattern: /\n\s*\n/,
323
- render: (match) => ({
324
- type: "paragraph-break",
325
- content: "",
326
- raw: match[0] || ""
327
- })
328
- });
329
- this.addRule({
330
- name: "bold",
331
- pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
332
- render: (match) => ({
333
- type: "bold",
334
- content: match[1] || "",
335
- raw: match[0] || ""
336
- })
337
- });
338
- this.addRule({
339
- name: "italic",
340
- pattern: /\*((?:(?!\*).)+)\*/,
341
- render: (match) => ({
342
- type: "italic",
343
- content: match[1] || "",
344
- raw: match[0] || ""
345
- })
346
- });
347
- this.addRule({
348
- name: "code",
349
- pattern: /`([^`]+)`/,
350
- render: (match) => ({
351
- type: "code",
352
- content: match[1] || "",
353
- raw: match[0] || ""
354
- })
355
- });
356
- this.addRule({
357
- name: "image",
358
- pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
359
- render: (match) => ({
360
- type: "image",
361
- content: match[1] || "",
362
- raw: match[0] || "",
363
- attributes: {
364
- alt: match[1] || "",
365
- src: match[2] || "",
366
- title: match[3] || ""
367
- }
368
- })
369
- });
370
- this.addRule({
371
- name: "link",
372
- pattern: /\[([^\]]+)\]\(([^)]+)\)/,
373
- render: (match) => ({
374
- type: "link",
375
- content: match[1] || "",
376
- raw: match[0] || "",
377
- attributes: {
378
- href: match[2] || ""
379
- }
380
- })
381
- });
382
- this.addRule({
383
- name: "task-list",
384
- pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
385
- render: (match) => ({
386
- type: "task-item",
387
- content: match[3] || "",
388
- raw: match[0] || "",
389
- attributes: {
390
- checked: String((match[2] || "").toLowerCase() === "x")
391
- }
392
- })
393
- });
394
- this.addRule({
395
- name: "list",
396
- pattern: /^(\s*)[-*+]\s+(.+)$/m,
397
- render: (match) => ({
398
- type: "list-item",
399
- content: match[2] || "",
400
- raw: match[0] || ""
401
- })
402
- });
403
- this.addRule({
404
- name: "blockquote",
405
- pattern: /^>\s+(.+)$/m,
406
- render: (match) => ({
407
- type: "blockquote",
408
- content: match[1] || "",
409
- raw: match[0] || ""
410
- })
411
- });
412
- this.addRule({
413
- name: "hr",
414
- pattern: /^---$/m,
415
- render: (match) => ({
416
- type: "hr",
417
- content: "",
418
- raw: match[0] || ""
419
- })
420
- });
421
- this.addRule({
422
- name: "soft-break",
423
- pattern: /\n/,
424
- render: (match) => ({
425
- type: "soft-break",
426
- content: " ",
427
- // Convert to space for inline text
428
- raw: match[0] || ""
429
- })
430
- });
431
- }
432
306
  };
433
307
 
434
308
  // src/utils.ts
@@ -747,7 +621,6 @@ var MarkdownRenderer = class {
747
621
  debugMode: false,
748
622
  ...config
749
623
  };
750
- this.setupDefaultRules();
751
624
  }
752
625
  addRule(rule) {
753
626
  this.rules.set(rule.type, rule);
@@ -757,28 +630,20 @@ var MarkdownRenderer = class {
757
630
  }
758
631
  render(tokens) {
759
632
  this.warnings = [];
760
- const htmlParts = tokens.map((token) => this.renderToken(token));
633
+ const tokensWithFormat = tokens.map((token) => ({
634
+ ...token,
635
+ attributes: {
636
+ ...token.attributes,
637
+ format: this.config.format
638
+ }
639
+ }));
640
+ const htmlParts = tokensWithFormat.map((token) => this.renderToken(token));
761
641
  const combinedHtml = htmlParts.join("");
762
642
  if (this.config.sanitize && !this.config.allowUnsafeHtml) {
763
643
  return sanitizeHtml(combinedHtml);
764
644
  }
765
645
  return combinedHtml;
766
646
  }
767
- getWarnings() {
768
- return [...this.warnings];
769
- }
770
- getConfig() {
771
- return { ...this.config };
772
- }
773
- updateConfig(config) {
774
- this.config = { ...this.config, ...config };
775
- }
776
- setDebugMode(enabled) {
777
- this.config.debugMode = enabled;
778
- }
779
- clearWarnings() {
780
- this.warnings = [];
781
- }
782
647
  renderToken(token) {
783
648
  const rule = this.rules.get(token.type);
784
649
  if (rule) {
@@ -790,9 +655,6 @@ var MarkdownRenderer = class {
790
655
  return this.createErrorBlock(`Render error for ${token.type}: ${errorMessage}`);
791
656
  }
792
657
  }
793
- if (token.type === "text") {
794
- return escapeHtml(token.content || token.raw || "");
795
- }
796
658
  if (this.config.debugMode) {
797
659
  return this.createDebugBlock(token);
798
660
  }
@@ -820,15 +682,234 @@ var MarkdownRenderer = class {
820
682
  <strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
821
683
  </div>`;
822
684
  }
823
- setupDefaultRules() {
824
- this.addRule({
685
+ getWarnings() {
686
+ return [...this.warnings];
687
+ }
688
+ getConfig() {
689
+ return { ...this.config };
690
+ }
691
+ updateConfig(config) {
692
+ this.config = { ...this.config, ...config };
693
+ }
694
+ setDebugMode(enabled) {
695
+ this.config.debugMode = enabled;
696
+ }
697
+ clearWarnings() {
698
+ this.warnings = [];
699
+ }
700
+ };
701
+
702
+ // src/extensions/core/blockquote.ts
703
+ var BlockquoteExtension = {
704
+ name: "blockquote",
705
+ parseRules: [
706
+ {
707
+ name: "blockquote",
708
+ pattern: /^>\s+(.+)$/m,
709
+ render: (match) => ({
710
+ type: "blockquote",
711
+ content: match[1] || "",
712
+ raw: match[0] || ""
713
+ })
714
+ }
715
+ ],
716
+ renderRules: [
717
+ {
718
+ type: "blockquote",
719
+ render: (token) => {
720
+ const content = escapeHtml(token.content);
721
+ const format = token.attributes?.format || "html";
722
+ if (format === "html") {
723
+ return `<blockquote style="border-left: 2px solid #d1d5db; padding: 8px 0 8px 16px; margin: 16px 0; font-style: italic; color: #6b7280;">${content}</blockquote>`;
724
+ }
725
+ return `<blockquote class="pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4">${content}</blockquote>`;
726
+ }
727
+ }
728
+ ]
729
+ };
730
+
731
+ // src/extensions/core/breaks.ts
732
+ var LineBreakExtension = {
733
+ name: "line-break",
734
+ parseRules: [
735
+ {
736
+ name: "hard-break-backslash",
737
+ pattern: /\\\s*\n/,
738
+ render: (match) => ({
739
+ type: "line-break",
740
+ content: "",
741
+ raw: match[0] || ""
742
+ })
743
+ },
744
+ {
745
+ name: "hard-break-spaces",
746
+ pattern: / +\n/,
747
+ render: (match) => ({
748
+ type: "line-break",
749
+ content: "",
750
+ raw: match[0] || ""
751
+ })
752
+ }
753
+ ],
754
+ renderRules: [
755
+ {
756
+ type: "line-break",
757
+ render: () => "<br>"
758
+ },
759
+ {
760
+ type: "paragraph-break",
761
+ render: () => "</p><p>"
762
+ },
763
+ {
764
+ type: "soft-break",
765
+ render: (token) => token.content || " "
766
+ }
767
+ ]
768
+ };
769
+
770
+ // src/extensions/core/code.ts
771
+ var InlineCodeExtension = {
772
+ name: "inline-code",
773
+ parseRules: [
774
+ {
775
+ name: "code",
776
+ pattern: /`([^`]+)`/,
777
+ render: (match) => ({
778
+ type: "code",
779
+ content: match[1] || "",
780
+ raw: match[0] || ""
781
+ })
782
+ }
783
+ ],
784
+ renderRules: [
785
+ {
786
+ type: "code",
787
+ render: (token) => {
788
+ const content = escapeHtml(token.content);
789
+ const format = token.attributes?.format;
790
+ if (format === "html") {
791
+ return `<code style="background-color: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 0.875rem;">${content}</code>`;
792
+ }
793
+ return `<code class="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">${content}</code>`;
794
+ }
795
+ }
796
+ ]
797
+ };
798
+ var CodeBlockExtension = {
799
+ name: "codeblock",
800
+ parseRules: [
801
+ {
802
+ name: "codeblock",
803
+ pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
804
+ render: (match) => ({
805
+ type: "codeblock",
806
+ content: match[2] || "",
807
+ raw: match[0] || "",
808
+ attributes: {
809
+ language: match[1] || "text"
810
+ }
811
+ })
812
+ }
813
+ ],
814
+ renderRules: [
815
+ {
816
+ type: "codeblock",
817
+ render: (token) => {
818
+ const language = token.attributes?.language || "text";
819
+ const escapedCode = escapeHtml(token.content);
820
+ const format = token.attributes?.format || "html";
821
+ if (format === "html") {
822
+ 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>`;
823
+ }
824
+ return `<pre class="bg-muted p-4 rounded-md overflow-x-auto my-4"><code class="language-${escapeHtml(language)}">${escapedCode}</code></pre>`;
825
+ }
826
+ }
827
+ ]
828
+ };
829
+
830
+ // src/extensions/core/emphasis.ts
831
+ var BoldExtension = {
832
+ name: "bold",
833
+ parseRules: [
834
+ {
835
+ name: "bold",
836
+ pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
837
+ render: (match) => ({
838
+ type: "bold",
839
+ content: match[1] || "",
840
+ raw: match[0] || ""
841
+ })
842
+ }
843
+ ],
844
+ renderRules: [
845
+ {
846
+ type: "bold",
847
+ render: (token) => {
848
+ const content = escapeHtml(token.content);
849
+ const format = token.attributes?.format;
850
+ if (format === "html") {
851
+ return `<strong>${content}</strong>`;
852
+ }
853
+ return `<strong class="font-bold">${content}</strong>`;
854
+ }
855
+ }
856
+ ]
857
+ };
858
+ var ItalicExtension = {
859
+ name: "italic",
860
+ parseRules: [
861
+ {
862
+ name: "italic",
863
+ pattern: /\*((?:(?!\*).)+)\*/,
864
+ render: (match) => ({
865
+ type: "italic",
866
+ content: match[1] || "",
867
+ raw: match[0] || ""
868
+ })
869
+ }
870
+ ],
871
+ renderRules: [
872
+ {
873
+ type: "italic",
874
+ render: (token) => {
875
+ const content = escapeHtml(token.content);
876
+ const format = token.attributes?.format;
877
+ if (format === "html") {
878
+ return `<em>${content}</em>`;
879
+ }
880
+ return `<em class="italic">${content}</em>`;
881
+ }
882
+ }
883
+ ]
884
+ };
885
+
886
+ // src/extensions/core/heading.ts
887
+ var HeadingExtension = {
888
+ name: "heading",
889
+ parseRules: [
890
+ {
891
+ name: "heading",
892
+ pattern: /^(#{1,6})\s+(.+)$/m,
893
+ render: (match) => ({
894
+ type: "heading",
895
+ content: match[2]?.trim() || "",
896
+ raw: match[0] || "",
897
+ attributes: {
898
+ level: String(match[1]?.length || 1)
899
+ }
900
+ })
901
+ }
902
+ ],
903
+ renderRules: [
904
+ {
825
905
  type: "heading",
826
906
  render: (token) => {
827
907
  const level = parseInt(token.attributes?.level || "1");
828
908
  const text = token.content;
829
909
  const id = generateId(text);
830
910
  const escapedContent = escapeHtml(text);
831
- if (this.config.format === "html") {
911
+ const format = token.attributes?.format || "html";
912
+ if (format === "html") {
832
913
  return `<h${level} id="${id}">${escapedContent}</h${level}>`;
833
914
  }
834
915
  let headingClasses = "group relative flex items-center gap-2";
@@ -855,61 +936,108 @@ var MarkdownRenderer = class {
855
936
  return `<h${level} id="${id}" class="${headingClasses}">
856
937
  ${escapedContent}
857
938
  <a href="#${id}" class="opacity-0 group-hover:opacity-100 text-muted-foreground transition-opacity">
858
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
859
- <path d="M7.5 4H5.75A3.75 3.75 0 002 7.75v.5a3.75 3.75 0 003.75 3.75h1.5m-1.5-4h3m1.5-4h1.75A3.75 3.75 0 0114 7.75v.5a3.75 3.75 0 01-3.75 3.75H8.5"/>
860
- </svg>
939
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
940
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
941
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
942
+ </svg>
861
943
  </a>
862
944
  </h${level}>`;
863
945
  }
864
- });
865
- this.addRule({
866
- type: "bold",
867
- render: (token) => {
868
- const content = escapeHtml(token.content);
869
- if (this.config.format === "html") {
870
- return `<strong>${content}</strong>`;
871
- }
872
- return `<strong class="font-bold">${content}</strong>`;
873
- }
874
- });
875
- this.addRule({
876
- type: "italic",
877
- render: (token) => {
878
- const content = escapeHtml(token.content);
879
- if (this.config.format === "html") {
880
- return `<em>${content}</em>`;
881
- }
882
- return `<em class="italic">${content}</em>`;
883
- }
884
- });
885
- this.addRule({
886
- type: "code",
946
+ }
947
+ ]
948
+ };
949
+
950
+ // src/extensions/core/hr.ts
951
+ var HorizontalRuleExtension = {
952
+ name: "hr",
953
+ parseRules: [
954
+ {
955
+ name: "hr",
956
+ pattern: /^---$/m,
957
+ render: (match) => ({
958
+ type: "hr",
959
+ content: "",
960
+ raw: match[0] || ""
961
+ })
962
+ }
963
+ ],
964
+ renderRules: [
965
+ {
966
+ type: "hr",
887
967
  render: (token) => {
888
- const content = escapeHtml(token.content);
889
- if (this.config.format === "html") {
890
- return `<code style="background-color: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 0.875rem;">${content}</code>`;
968
+ const format = token.attributes?.format;
969
+ if (format === "html") {
970
+ return '<hr style="margin: 24px 0; border: none; border-top: 1px solid #d1d5db;">';
891
971
  }
892
- return `<code class="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">${content}</code>`;
972
+ return '<hr class="my-6 border-t border-border">';
893
973
  }
894
- });
895
- this.addRule({
896
- type: "codeblock",
974
+ }
975
+ ]
976
+ };
977
+
978
+ // src/extensions/core/image.ts
979
+ var ImageExtension = {
980
+ name: "image",
981
+ parseRules: [
982
+ {
983
+ name: "image",
984
+ pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
985
+ render: (match) => ({
986
+ type: "image",
987
+ content: match[1] || "",
988
+ raw: match[0] || "",
989
+ attributes: {
990
+ alt: match[1] || "",
991
+ src: match[2] || "",
992
+ title: match[3] || ""
993
+ }
994
+ })
995
+ }
996
+ ],
997
+ renderRules: [
998
+ {
999
+ type: "image",
897
1000
  render: (token) => {
898
- const language = token.attributes?.language || "text";
899
- const escapedCode = escapeHtml(token.content);
900
- if (this.config.format === "html") {
901
- return `<pre style="background-color: #f3f4f6; padding: 16px; border-radius: 6px; overflow-x: auto; margin: 16px 0;"><code class="language-${escapeHtml(language)}">${escapedCode}</code></pre>`;
1001
+ const src = token.attributes?.src || "";
1002
+ const alt = token.attributes?.alt || "";
1003
+ const title = token.attributes?.title || "";
1004
+ const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
1005
+ const format = token.attributes?.format || "html";
1006
+ if (format === "html") {
1007
+ return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} style="max-width: 100%; height: auto; border-radius: 8px; margin: 16px 0;" loading="lazy" />`;
902
1008
  }
903
- return `<pre class="bg-muted p-4 rounded-md overflow-x-auto my-4"><code class="language-${escapeHtml(language)}">${escapedCode}</code></pre>`;
1009
+ return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} class="max-w-full h-auto rounded-lg my-4" loading="lazy" />`;
904
1010
  }
905
- });
906
- this.addRule({
1011
+ }
1012
+ ]
1013
+ };
1014
+
1015
+ // src/extensions/core/links.ts
1016
+ var LinkExtension = {
1017
+ name: "link",
1018
+ parseRules: [
1019
+ {
1020
+ name: "link",
1021
+ pattern: /\[(?!(?:button|embed):)([^\]]+)\]\(([^)]+)\)/,
1022
+ render: (match) => ({
1023
+ type: "link",
1024
+ content: match[1] || "",
1025
+ raw: match[0] || "",
1026
+ attributes: {
1027
+ href: match[2] || ""
1028
+ }
1029
+ })
1030
+ }
1031
+ ],
1032
+ renderRules: [
1033
+ {
907
1034
  type: "link",
908
1035
  render: (token) => {
909
1036
  const href = token.attributes?.href || "#";
910
1037
  const escapedHref = escapeHtml(href);
911
1038
  const escapedText = escapeHtml(token.content);
912
- if (this.config.format === "html") {
1039
+ const format = token.attributes?.format || "html";
1040
+ if (format === "html") {
913
1041
  return `<a href="${escapedHref}" target="_blank" rel="noopener noreferrer">${escapedText}</a>`;
914
1042
  }
915
1043
  return `<a href="${escapedHref}" class="text-primary hover:underline inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
@@ -921,47 +1049,55 @@ var MarkdownRenderer = class {
921
1049
  </svg>
922
1050
  </a>`;
923
1051
  }
924
- });
925
- this.addRule({
1052
+ }
1053
+ ]
1054
+ };
1055
+
1056
+ // src/extensions/core/lists.ts
1057
+ var ListExtension = {
1058
+ name: "list",
1059
+ parseRules: [
1060
+ {
1061
+ name: "list-item",
1062
+ pattern: /^(\s*)[-*+]\s+(.+)$/m,
1063
+ render: (match) => ({
1064
+ type: "list-item",
1065
+ content: match[2] || "",
1066
+ raw: match[0] || ""
1067
+ })
1068
+ }
1069
+ ],
1070
+ renderRules: [
1071
+ {
926
1072
  type: "list-item",
927
1073
  render: (token) => `<li>${escapeHtml(token.content)}</li>`
928
- });
929
- this.addRule({
930
- type: "blockquote",
931
- render: (token) => {
932
- const content = escapeHtml(token.content);
933
- if (this.config.format === "html") {
934
- return `<blockquote style="border-left: 2px solid #d1d5db; padding: 8px 0 8px 16px; margin: 16px 0; font-style: italic; color: #6b7280;">${content}</blockquote>`;
935
- }
936
- return `<blockquote class="pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4">${content}</blockquote>`;
937
- }
938
- });
939
- this.addRule({
940
- type: "text",
941
- render: (token) => {
942
- if (!token.content) return "";
943
- return escapeHtml(token.content);
944
- }
945
- });
946
- this.addRule({
947
- type: "paragraph",
948
- render: (token) => {
949
- if (!token.content) return "";
950
- const content = token.content.trim();
951
- if (!content) return "";
952
- const processedContent = content.includes("<br>") ? content : escapeHtml(content);
953
- if (this.config.format === "html") {
954
- return `<p style="line-height: 1.75; margin-bottom: 16px;">${processedContent}</p>`;
1074
+ }
1075
+ ]
1076
+ };
1077
+ var TaskListExtension = {
1078
+ name: "task-list",
1079
+ parseRules: [
1080
+ {
1081
+ name: "task-item",
1082
+ pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
1083
+ render: (match) => ({
1084
+ type: "task-item",
1085
+ content: match[3] || "",
1086
+ raw: match[0] || "",
1087
+ attributes: {
1088
+ checked: String((match[2] || "").toLowerCase() === "x")
955
1089
  }
956
- return `<p class="leading-7 mb-4">${processedContent}</p>`;
957
- }
958
- });
959
- this.addRule({
1090
+ })
1091
+ }
1092
+ ],
1093
+ renderRules: [
1094
+ {
960
1095
  type: "task-item",
961
1096
  render: (token) => {
962
1097
  const isChecked = token.attributes?.checked === "true";
963
1098
  const escapedContent = escapeHtml(token.content);
964
- if (this.config.format === "html") {
1099
+ const format = token.attributes?.format || "html";
1100
+ if (format === "html") {
965
1101
  return `<div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
966
1102
  <input type="checkbox" ${isChecked ? "checked" : ""} disabled style="margin: 0;" />
967
1103
  <span${isChecked ? ' style="text-decoration: line-through; color: #6b7280;"' : ""}>${escapedContent}</span>
@@ -973,44 +1109,65 @@ var MarkdownRenderer = class {
973
1109
  <span${isChecked ? ' class="line-through text-muted-foreground"' : ""}>${escapedContent}</span>
974
1110
  </div>`;
975
1111
  }
976
- });
977
- this.addRule({
978
- type: "image",
1112
+ }
1113
+ ]
1114
+ };
1115
+
1116
+ // src/extensions/core/paragraph.ts
1117
+ var ParagraphExtension = {
1118
+ name: "paragraph",
1119
+ parseRules: [],
1120
+ renderRules: [
1121
+ {
1122
+ type: "paragraph",
979
1123
  render: (token) => {
980
- const src = token.attributes?.src || "";
981
- const alt = token.attributes?.alt || "";
982
- const title = token.attributes?.title || "";
983
- const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
984
- if (this.config.format === "html") {
985
- return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} style="max-width: 100%; height: auto; border-radius: 8px; margin: 16px 0;" loading="lazy" />`;
1124
+ if (!token.content) return "";
1125
+ const content = token.content.trim();
1126
+ if (!content) return "";
1127
+ const processedContent = content.includes("<br>") ? content : escapeHtml(content);
1128
+ const format = token.attributes?.format || "html";
1129
+ if (format === "html") {
1130
+ return `<p style="line-height: 1.75; margin-bottom: 16px;">${processedContent}</p>`;
986
1131
  }
987
- return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} class="max-w-full h-auto rounded-lg my-4" loading="lazy" />`;
1132
+ return `<p class="leading-7 mb-4">${processedContent}</p>`;
988
1133
  }
989
- });
990
- this.addRule({
991
- type: "hr",
992
- render: () => {
993
- if (this.config.format === "html") {
994
- return '<hr style="margin: 24px 0; border: none; border-top: 1px solid #d1d5db;">';
995
- }
996
- return '<hr class="my-6 border-t border-border">';
1134
+ }
1135
+ ]
1136
+ };
1137
+
1138
+ // src/extensions/core/text.ts
1139
+ var TextExtension = {
1140
+ name: "text",
1141
+ parseRules: [],
1142
+ renderRules: [
1143
+ {
1144
+ type: "text",
1145
+ render: (token) => {
1146
+ if (!token.content) return "";
1147
+ return escapeHtml(token.content);
997
1148
  }
998
- });
999
- this.addRule({
1000
- type: "line-break",
1001
- render: () => "<br>"
1002
- });
1003
- this.addRule({
1004
- type: "paragraph-break",
1005
- render: () => "</p><p>"
1006
- });
1007
- this.addRule({
1008
- type: "soft-break",
1009
- render: (token) => token.content || " "
1010
- });
1011
- }
1149
+ }
1150
+ ]
1012
1151
  };
1013
1152
 
1153
+ // src/extensions/core/index.ts
1154
+ var CoreExtensions = [
1155
+ TextExtension,
1156
+ HeadingExtension,
1157
+ BoldExtension,
1158
+ ItalicExtension,
1159
+ InlineCodeExtension,
1160
+ CodeBlockExtension,
1161
+ LinkExtension,
1162
+ ImageExtension,
1163
+ ListExtension,
1164
+ TaskListExtension,
1165
+ BlockquoteExtension,
1166
+ HorizontalRuleExtension,
1167
+ ParagraphExtension,
1168
+ LineBreakExtension
1169
+ ];
1170
+
1014
1171
  // src/extensions/alert.ts
1015
1172
  var AlertExtension = {
1016
1173
  name: "alert",
@@ -1090,26 +1247,19 @@ var ButtonExtension = {
1090
1247
  name: "button",
1091
1248
  pattern: /\[button:([^\]]+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
1092
1249
  render: (match) => {
1093
- const text = match[1] || "";
1094
- const href = match[2] || "";
1095
- const optionsString = match[3] || "";
1096
- const options = optionsString.split(",").map((opt) => opt.trim()).filter(Boolean);
1097
- const styleOptions = ["default", "primary", "secondary", "success", "danger", "outline", "ghost"];
1098
- const style = options.find((opt) => styleOptions.includes(opt)) || "primary";
1099
- const sizeOptions = ["sm", "md", "lg"];
1100
- const size = options.find((opt) => sizeOptions.includes(opt)) || "md";
1101
- const disabled = options.includes("disabled");
1102
- const target = options.includes("self") ? "_self" : "_blank";
1250
+ const options = match[3] ? match[3].split(",").map((opt) => opt.trim()) : [];
1103
1251
  return {
1104
1252
  type: "button",
1105
- content: text,
1253
+ content: match[1] || "",
1106
1254
  raw: match[0] || "",
1107
1255
  attributes: {
1108
- href,
1109
- style,
1110
- size,
1111
- disabled: disabled.toString(),
1112
- target
1256
+ href: match[2] || "",
1257
+ style: options.find(
1258
+ (opt) => ["default", "primary", "secondary", "success", "danger", "outline", "ghost"].includes(opt)
1259
+ ) || "primary",
1260
+ size: options.find((opt) => ["sm", "md", "lg"].includes(opt)) || "md",
1261
+ disabled: String(options.includes("disabled")),
1262
+ target: options.includes("self") ? "_self" : "_blank"
1113
1263
  }
1114
1264
  };
1115
1265
  }
@@ -1124,53 +1274,92 @@ var ButtonExtension = {
1124
1274
  const size = token.attributes?.size || "md";
1125
1275
  const disabled = token.attributes?.disabled === "true";
1126
1276
  const target = token.attributes?.target || "_blank";
1127
- const text = token.content;
1128
- const classes = buildButtonClasses(style, size);
1277
+ const baseClasses = `
1278
+ inline-flex items-center justify-center font-medium rounded-lg
1279
+ transition-all duration-200 ease-out
1280
+ focus:outline-none focus:ring-2 focus:ring-offset-2
1281
+ disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none
1282
+ transform hover:scale-[1.02] active:scale-[0.98]
1283
+ shadow-sm hover:shadow-md active:shadow-sm
1284
+ border border-transparent
1285
+ relative overflow-hidden
1286
+ before:absolute before:inset-0 before:rounded-lg
1287
+ before:bg-gradient-to-br before:from-white/20 before:to-transparent
1288
+ before:opacity-0 hover:before:opacity-100 before:transition-opacity before:duration-200
1289
+ `.replace(/\s+/g, " ").trim();
1290
+ const sizeClasses = {
1291
+ sm: "px-3 py-1.5 text-sm gap-1.5",
1292
+ md: "px-4 py-2 text-base gap-2",
1293
+ lg: "px-6 py-3 text-lg gap-2.5"
1294
+ };
1295
+ const styleClasses = {
1296
+ default: `
1297
+ bg-slate-600 text-white border-slate-500
1298
+ hover:bg-slate-700 hover:border-slate-400
1299
+ focus:ring-slate-500
1300
+ shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
1301
+ hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
1302
+ `,
1303
+ primary: `
1304
+ bg-blue-600 text-white border-blue-500
1305
+ hover:bg-blue-700 hover:border-blue-400
1306
+ focus:ring-blue-500
1307
+ shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
1308
+ hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
1309
+ `,
1310
+ secondary: `
1311
+ bg-gray-600 text-white border-gray-500
1312
+ hover:bg-gray-700 hover:border-gray-400
1313
+ focus:ring-gray-500
1314
+ shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
1315
+ hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
1316
+ `,
1317
+ success: `
1318
+ bg-green-600 text-white border-green-500
1319
+ hover:bg-green-700 hover:border-green-400
1320
+ focus:ring-green-500
1321
+ shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
1322
+ hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
1323
+ `,
1324
+ danger: `
1325
+ bg-red-600 text-white border-red-500
1326
+ hover:bg-red-700 hover:border-red-400
1327
+ focus:ring-red-500
1328
+ shadow-[0_1px_0_0_rgba(255,255,255,0.1)_inset,0_1px_2px_0_rgba(0,0,0,0.1)]
1329
+ hover:shadow-[0_1px_0_0_rgba(255,255,255,0.15)_inset,0_2px_4px_0_rgba(0,0,0,0.15)]
1330
+ `,
1331
+ outline: `
1332
+ bg-transparent text-blue-600 border-blue-600
1333
+ hover:bg-blue-50 hover:border-blue-700 hover:text-blue-700
1334
+ focus:ring-blue-500
1335
+ shadow-[0_0_0_1px_rgba(59,130,246,0.5)_inset]
1336
+ hover:shadow-[0_0_0_1px_rgba(29,78,216,0.6)_inset,0_1px_2px_0_rgba(0,0,0,0.05)]
1337
+ `,
1338
+ ghost: `
1339
+ bg-transparent text-gray-700 border-transparent
1340
+ hover:bg-gray-100 hover:text-gray-900
1341
+ focus:ring-gray-500
1342
+ shadow-none
1343
+ hover:shadow-[0_1px_2px_0_rgba(0,0,0,0.05)]
1344
+ `
1345
+ };
1346
+ const classes = `
1347
+ ${baseClasses}
1348
+ ${sizeClasses[size] || sizeClasses.md}
1349
+ ${styleClasses[style] || styleClasses.primary}
1350
+ `.replace(/\s+/g, " ").trim();
1129
1351
  const targetAttr = target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : target === "_self" ? ' target="_self"' : "";
1130
1352
  const disabledAttr = disabled ? ' aria-disabled="true" tabindex="-1"' : "";
1131
- const externalIcon = target === "_blank" && !disabled ? '<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>' : "";
1353
+ const externalIcon = target === "_blank" && !disabled ? `<svg class="w-4 h-4 ml-1 opacity-75" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1354
+ <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"/>
1355
+ </svg>` : "";
1132
1356
  return `<a href="${href}" class="${classes}"${targetAttr}${disabledAttr}>
1133
- ${text}${externalIcon}
1134
- </a>`;
1357
+ <span class="relative z-10">${token.content}</span>${externalIcon}
1358
+ </a>`;
1135
1359
  }
1136
1360
  }
1137
1361
  ]
1138
1362
  };
1139
- function buildButtonClasses(style, size) {
1140
- const base = [
1141
- "inline-flex",
1142
- "items-center",
1143
- "justify-center",
1144
- "font-medium",
1145
- "rounded-lg",
1146
- "transition-colors",
1147
- "focus:outline-none",
1148
- "focus:ring-2",
1149
- "focus:ring-offset-2",
1150
- "disabled:opacity-50",
1151
- "disabled:cursor-not-allowed"
1152
- ];
1153
- const sizes = {
1154
- sm: ["px-3", "py-1.5", "text-sm"],
1155
- md: ["px-4", "py-2", "text-base"],
1156
- lg: ["px-6", "py-3", "text-lg"]
1157
- };
1158
- const styles = {
1159
- default: ["bg-slate-600", "text-white", "hover:bg-slate-700", "focus:ring-slate-500"],
1160
- primary: ["bg-blue-600", "text-white", "hover:bg-blue-700", "focus:ring-blue-500"],
1161
- secondary: ["bg-gray-600", "text-white", "hover:bg-gray-700", "focus:ring-gray-500"],
1162
- success: ["bg-green-600", "text-white", "hover:bg-green-700", "focus:ring-green-500"],
1163
- danger: ["bg-red-600", "text-white", "hover:bg-red-700", "focus:ring-red-500"],
1164
- outline: ["border", "border-blue-600", "text-blue-600", "hover:bg-blue-50", "focus:ring-blue-500"],
1165
- ghost: ["text-gray-700", "hover:bg-gray-100", "focus:ring-gray-500"]
1166
- };
1167
- const allClasses = [
1168
- ...base,
1169
- ...sizes[size] ?? sizes.md ?? [],
1170
- ...styles[style] ?? styles.primary ?? []
1171
- ];
1172
- return allClasses.join(" ");
1173
- }
1174
1363
 
1175
1364
  // src/extensions/embed.ts
1176
1365
  var EmbedExtension = {
@@ -1248,8 +1437,7 @@ function renderYouTubeEmbed(url, options, classes) {
1248
1437
  params.set("rel", "0");
1249
1438
  params.set("modestbranding", "1");
1250
1439
  const embedUrl = `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
1251
- return `
1252
- <div class="${classes}">
1440
+ return `<div class="${classes}">
1253
1441
  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
1254
1442
  <iframe
1255
1443
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
@@ -1277,8 +1465,7 @@ function renderCodePenEmbed(url, options, classes) {
1277
1465
  "editable": "true"
1278
1466
  });
1279
1467
  const embedUrl = `https://codepen.io/${user}/embed/${penId}?${embedParams.toString()}`;
1280
- return `
1281
- <div class="${classes}">
1468
+ return `<div class="${classes}">
1282
1469
  <iframe
1283
1470
  height="${height}"
1284
1471
  style="width: 100%; border: 0;"
@@ -1302,8 +1489,7 @@ function renderVimeoEmbed(url, options, classes) {
1302
1489
  if (options.mute === "1") params.set("muted", "1");
1303
1490
  if (options.loop === "1") params.set("loop", "1");
1304
1491
  const embedUrl = `https://player.vimeo.com/video/${videoId}?${params.toString()}`;
1305
- return `
1306
- <div class="${classes}">
1492
+ return `<div class="${classes}">
1307
1493
  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
1308
1494
  <iframe
1309
1495
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
@@ -1319,8 +1505,7 @@ function renderVimeoEmbed(url, options, classes) {
1319
1505
  function renderSpotifyEmbed(url, options, classes) {
1320
1506
  const embedUrl = url.replace("open.spotify.com", "open.spotify.com/embed");
1321
1507
  const height = options.height || "380";
1322
- return `
1323
- <div class="${classes}">
1508
+ return `<div class="${classes}">
1324
1509
  <iframe
1325
1510
  style="border-radius: 12px;"
1326
1511
  src="${embedUrl}"
@@ -1343,8 +1528,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
1343
1528
  if (!embedUrl.includes("?")) {
1344
1529
  embedUrl += `?view=${view}`;
1345
1530
  }
1346
- return `
1347
- <div class="${classes}">
1531
+ return `<div class="${classes}">
1348
1532
  <iframe
1349
1533
  src="${embedUrl}"
1350
1534
  style="width: 100%; height: ${height}px; border: 0; border-radius: 4px; overflow: hidden;"
@@ -1357,8 +1541,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
1357
1541
  function renderFigmaEmbed(url, options, classes) {
1358
1542
  const embedUrl = `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
1359
1543
  const height = options.height || "450";
1360
- return `
1361
- <div class="${classes}">
1544
+ return `<div class="${classes}">
1362
1545
  <iframe
1363
1546
  style="border: none;"
1364
1547
  width="100%"
@@ -1369,8 +1552,7 @@ function renderFigmaEmbed(url, options, classes) {
1369
1552
  </div>`;
1370
1553
  }
1371
1554
  function renderTwitterEmbed(url, _options, classes) {
1372
- return `
1373
- <div class="${classes}">
1555
+ return `<div class="${classes}">
1374
1556
  <div class="p-4">
1375
1557
  <div class="flex items-center gap-3 mb-3">
1376
1558
  <svg class="w-6 h-6 fill-current text-blue-500" viewBox="0 0 24 24">
@@ -1398,8 +1580,7 @@ function renderGitHubEmbed(url, _options, classes) {
1398
1580
  if (!owner || !repo) {
1399
1581
  return createErrorEmbed("Invalid GitHub URL", url, classes);
1400
1582
  }
1401
- return `
1402
- <div class="${classes}">
1583
+ return `<div class="${classes}">
1403
1584
  <div class="p-4">
1404
1585
  <div class="flex items-center gap-3 mb-3">
1405
1586
  <svg class="w-6 h-6 fill-current" viewBox="0 0 24 24">
@@ -1422,8 +1603,7 @@ function renderGitHubEmbed(url, _options, classes) {
1422
1603
  }
1423
1604
  function renderGenericEmbed(url, _options, classes) {
1424
1605
  const domain = extractDomain(url);
1425
- return `
1426
- <div class="${classes}">
1606
+ return `<div class="${classes}">
1427
1607
  <div class="p-4">
1428
1608
  <div class="flex items-center gap-3 mb-3">
1429
1609
  <div class="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
@@ -1447,8 +1627,7 @@ function renderGenericEmbed(url, _options, classes) {
1447
1627
  </div>`;
1448
1628
  }
1449
1629
  function createErrorEmbed(error, url, classes) {
1450
- return `
1451
- <div class="${classes}">
1630
+ return `<div class="${classes}">
1452
1631
  <div class="p-4 text-destructive">
1453
1632
  <div class="font-medium flex items-center gap-2">
1454
1633
  <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -1482,19 +1661,54 @@ var ChangerawrMarkdown = class {
1482
1661
  this.extensions = /* @__PURE__ */ new Map();
1483
1662
  this.parser = new MarkdownParser(config?.parser);
1484
1663
  this.renderer = new MarkdownRenderer(config?.renderer);
1485
- this.registerBuiltInExtensions();
1664
+ this.registerCoreExtensions();
1665
+ this.registerFeatureExtensions();
1486
1666
  if (config?.extensions) {
1487
1667
  config.extensions.forEach((extension) => {
1488
1668
  this.registerExtension(extension);
1489
1669
  });
1490
1670
  }
1491
- this.parser.setupDefaultRulesIfEmpty();
1492
1671
  }
1493
- /**
1494
- * Register a custom extension
1495
- */
1672
+ registerFeatureExtensions() {
1673
+ this.registerExtension(AlertExtension);
1674
+ this.registerExtension(ButtonExtension);
1675
+ this.registerExtension(EmbedExtension);
1676
+ }
1677
+ registerCoreExtensions() {
1678
+ CoreExtensions.forEach((extension) => {
1679
+ this.registerExtension(extension);
1680
+ });
1681
+ }
1496
1682
  registerExtension(extension) {
1497
1683
  try {
1684
+ if (!extension.name || typeof extension.name !== "string") {
1685
+ throw new Error("Extension must have a valid name");
1686
+ }
1687
+ if (!Array.isArray(extension.parseRules)) {
1688
+ throw new Error("Extension must have parseRules array");
1689
+ }
1690
+ if (!Array.isArray(extension.renderRules)) {
1691
+ throw new Error("Extension must have renderRules array");
1692
+ }
1693
+ extension.parseRules.forEach((rule, index) => {
1694
+ if (!rule.name || typeof rule.name !== "string") {
1695
+ throw new Error(`Parse rule ${index} must have a valid name`);
1696
+ }
1697
+ if (!rule.pattern || !(rule.pattern instanceof RegExp)) {
1698
+ throw new Error(`Parse rule ${index} must have a valid RegExp pattern`);
1699
+ }
1700
+ if (!rule.render || typeof rule.render !== "function") {
1701
+ throw new Error(`Parse rule ${index} must have a valid render function`);
1702
+ }
1703
+ });
1704
+ extension.renderRules.forEach((rule, index) => {
1705
+ if (!rule.type || typeof rule.type !== "string") {
1706
+ throw new Error(`Render rule ${index} must have a valid type`);
1707
+ }
1708
+ if (!rule.render || typeof rule.render !== "function") {
1709
+ throw new Error(`Render rule ${index} must have a valid render function`);
1710
+ }
1711
+ });
1498
1712
  this.extensions.set(extension.name, extension);
1499
1713
  extension.parseRules.forEach((rule) => {
1500
1714
  this.parser.addRule(rule);
@@ -1515,9 +1729,6 @@ var ChangerawrMarkdown = class {
1515
1729
  };
1516
1730
  }
1517
1731
  }
1518
- /**
1519
- * Unregister an extension
1520
- */
1521
1732
  unregisterExtension(name) {
1522
1733
  const extension = this.extensions.get(name);
1523
1734
  if (!extension) {
@@ -1531,46 +1742,25 @@ var ChangerawrMarkdown = class {
1531
1742
  return false;
1532
1743
  }
1533
1744
  }
1534
- /**
1535
- * Parse markdown content into tokens
1536
- */
1537
1745
  parse(markdown3) {
1538
1746
  return this.parser.parse(markdown3);
1539
1747
  }
1540
- /**
1541
- * Render tokens to HTML
1542
- */
1543
1748
  render(tokens) {
1544
1749
  return this.renderer.render(tokens);
1545
1750
  }
1546
- /**
1547
- * Parse and render markdown to HTML in one step
1548
- */
1549
1751
  toHtml(markdown3) {
1550
1752
  const tokens = this.parse(markdown3);
1551
1753
  return this.render(tokens);
1552
1754
  }
1553
- /**
1554
- * Get list of registered extensions
1555
- */
1556
1755
  getExtensions() {
1557
1756
  return Array.from(this.extensions.keys());
1558
1757
  }
1559
- /**
1560
- * Check if extension is registered
1561
- */
1562
1758
  hasExtension(name) {
1563
1759
  return this.extensions.has(name);
1564
1760
  }
1565
- /**
1566
- * Get parser warnings
1567
- */
1568
1761
  getWarnings() {
1569
1762
  return [...this.parser.getWarnings(), ...this.renderer.getWarnings()];
1570
1763
  }
1571
- /**
1572
- * Get debug information from last render
1573
- */
1574
1764
  getDebugInfo() {
1575
1765
  return {
1576
1766
  warnings: this.getWarnings(),
@@ -1580,9 +1770,6 @@ var ChangerawrMarkdown = class {
1580
1770
  iterationCount: 0
1581
1771
  };
1582
1772
  }
1583
- /**
1584
- * Get performance metrics for the last operation
1585
- */
1586
1773
  getPerformanceMetrics() {
1587
1774
  return {
1588
1775
  parseTime: 0,
@@ -1591,28 +1778,29 @@ var ChangerawrMarkdown = class {
1591
1778
  tokenCount: 0
1592
1779
  };
1593
1780
  }
1594
- /**
1595
- * Register built-in extensions
1596
- */
1597
- registerBuiltInExtensions() {
1598
- this.registerExtension(AlertExtension);
1599
- this.registerExtension(ButtonExtension);
1600
- this.registerExtension(EmbedExtension);
1601
- }
1602
- /**
1603
- * Rebuild parser and renderer with current extensions
1604
- */
1605
1781
  rebuildParserAndRenderer() {
1606
1782
  const parserConfig = this.parser.getConfig();
1607
1783
  const rendererConfig = this.renderer.getConfig();
1608
1784
  this.parser = new MarkdownParser(parserConfig);
1609
1785
  this.renderer = new MarkdownRenderer(rendererConfig);
1610
1786
  const extensionsToRegister = Array.from(this.extensions.values());
1611
- this.extensions.clear();
1612
- extensionsToRegister.forEach((extension) => {
1613
- this.registerExtension(extension);
1787
+ const featureExtensions = extensionsToRegister.filter(
1788
+ (ext) => ["alert", "button", "embed"].includes(ext.name)
1789
+ );
1790
+ const coreExtensions = extensionsToRegister.filter(
1791
+ (ext) => ["text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(ext.name)
1792
+ );
1793
+ const customExtensions = extensionsToRegister.filter(
1794
+ (ext) => !["alert", "button", "embed", "text", "heading", "bold", "italic", "code", "codeblock", "link", "image", "list", "task-list", "blockquote", "hr", "paragraph", "line-break"].includes(ext.name)
1795
+ );
1796
+ [...featureExtensions, ...coreExtensions, ...customExtensions].forEach((extension) => {
1797
+ extension.parseRules.forEach((rule) => {
1798
+ this.parser.addRule(rule);
1799
+ });
1800
+ extension.renderRules.forEach((rule) => {
1801
+ this.renderer.addRule(rule);
1802
+ });
1614
1803
  });
1615
- this.parser.setupDefaultRulesIfEmpty();
1616
1804
  }
1617
1805
  };
1618
1806
  var markdown = new ChangerawrMarkdown();
@@ -2042,8 +2230,7 @@ function createHTMLEngine(config) {
2042
2230
  renderer: {
2043
2231
  format: "html",
2044
2232
  sanitize: true,
2045
- allowUnsafeHtml: false,
2046
- ...config?.parser
2233
+ allowUnsafeHtml: false
2047
2234
  }
2048
2235
  });
2049
2236
  }
@@ -2053,8 +2240,7 @@ function createTailwindEngine(config) {
2053
2240
  renderer: {
2054
2241
  format: "tailwind",
2055
2242
  sanitize: true,
2056
- allowUnsafeHtml: false,
2057
- ...config?.parser
2243
+ allowUnsafeHtml: false
2058
2244
  }
2059
2245
  });
2060
2246
  }
@@ -2073,21 +2259,18 @@ function createDebugEngine(config) {
2073
2259
  }
2074
2260
  });
2075
2261
  }
2076
- function createMinimalEngine(config) {
2077
- const engine = new ChangerawrMarkdown({
2078
- ...config,
2079
- extensions: []
2080
- // Override to prevent built-in extensions
2081
- });
2082
- if (engine.hasExtension("alert")) {
2083
- engine.unregisterExtension("alert");
2084
- }
2085
- if (engine.hasExtension("button")) {
2086
- engine.unregisterExtension("button");
2087
- }
2088
- if (engine.hasExtension("embed")) {
2089
- engine.unregisterExtension("embed");
2090
- }
2262
+ function createMinimalEngine(extensions = []) {
2263
+ const engine = new ChangerawrMarkdown();
2264
+ const defaultExtensions = engine.getExtensions();
2265
+ defaultExtensions.forEach((ext) => engine.unregisterExtension(ext));
2266
+ extensions.forEach((ext) => engine.registerExtension(ext));
2267
+ return engine;
2268
+ }
2269
+ function createCoreOnlyEngine(config) {
2270
+ const engine = new ChangerawrMarkdown(config);
2271
+ engine.unregisterExtension("alert");
2272
+ engine.unregisterExtension("button");
2273
+ engine.unregisterExtension("embed");
2091
2274
  return engine;
2092
2275
  }
2093
2276
  function createCustomEngine(extensions, config) {
@@ -2111,6 +2294,7 @@ var markdown2 = {
2111
2294
  createTailwindEngine,
2112
2295
  createDebugEngine,
2113
2296
  createMinimalEngine,
2297
+ createCoreOnlyEngine,
2114
2298
  createCustomEngine,
2115
2299
  // Standalone functions
2116
2300
  renderCum,
@@ -2119,6 +2303,24 @@ var markdown2 = {
2119
2303
  ChangerawrMarkdown,
2120
2304
  // Extensions
2121
2305
  extensions: {
2306
+ // All core extensions as a bundle
2307
+ core: CoreExtensions,
2308
+ // Individual core extensions
2309
+ Text: TextExtension,
2310
+ Heading: HeadingExtension,
2311
+ Bold: BoldExtension,
2312
+ Italic: ItalicExtension,
2313
+ InlineCode: InlineCodeExtension,
2314
+ CodeBlock: CodeBlockExtension,
2315
+ Link: LinkExtension,
2316
+ Image: ImageExtension,
2317
+ List: ListExtension,
2318
+ TaskList: TaskListExtension,
2319
+ Blockquote: BlockquoteExtension,
2320
+ HorizontalRule: HorizontalRuleExtension,
2321
+ Paragraph: ParagraphExtension,
2322
+ LineBreak: LineBreakExtension,
2323
+ // Feature extensions
2122
2324
  Alert: AlertExtension,
2123
2325
  Button: ButtonExtension,
2124
2326
  Embed: EmbedExtension
@@ -2179,6 +2381,14 @@ var presets = {
2179
2381
  sanitize: true
2180
2382
  }
2181
2383
  },
2384
+ /**
2385
+ * Core-only preset with just markdown basics
2386
+ */
2387
+ coreOnly: {
2388
+ renderer: {
2389
+ format: "tailwind"
2390
+ }
2391
+ },
2182
2392
  /**
2183
2393
  * Performance preset with minimal processing
2184
2394
  */
@@ -2195,6 +2405,9 @@ var presets = {
2195
2405
  };
2196
2406
  function createEngineWithPreset(presetName, additionalConfig) {
2197
2407
  const preset = presets[presetName];
2408
+ if (presetName === "coreOnly") {
2409
+ return createCoreOnlyEngine(additionalConfig);
2410
+ }
2198
2411
  return new ChangerawrMarkdown({
2199
2412
  ...preset,
2200
2413
  ...additionalConfig
@@ -2203,17 +2416,33 @@ function createEngineWithPreset(presetName, additionalConfig) {
2203
2416
  // Annotate the CommonJS export names for ESM import in node:
2204
2417
  0 && (module.exports = {
2205
2418
  AlertExtension,
2419
+ BlockquoteExtension,
2420
+ BoldExtension,
2206
2421
  ButtonExtension,
2207
2422
  ChangerawrMarkdown,
2423
+ CodeBlockExtension,
2424
+ CoreExtensions,
2208
2425
  EmbedExtension,
2426
+ HeadingExtension,
2427
+ HorizontalRuleExtension,
2428
+ ImageExtension,
2429
+ InlineCodeExtension,
2430
+ ItalicExtension,
2431
+ LineBreakExtension,
2432
+ LinkExtension,
2433
+ ListExtension,
2209
2434
  Logger,
2210
2435
  MarkdownParser,
2211
2436
  MarkdownRenderer,
2437
+ ParagraphExtension,
2212
2438
  PerformanceTimer,
2439
+ TaskListExtension,
2440
+ TextExtension,
2213
2441
  astToJSONString,
2214
2442
  astToTokens,
2215
2443
  basicSanitize,
2216
2444
  compareTokens,
2445
+ createCoreOnlyEngine,
2217
2446
  createCumEngine,
2218
2447
  createCustomEngine,
2219
2448
  createDebugEngine,