@changerawr/markdown 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -31,17 +31,33 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AlertExtension: () => AlertExtension,
34
+ BlockquoteExtension: () => BlockquoteExtension,
35
+ BoldExtension: () => BoldExtension,
34
36
  ButtonExtension: () => ButtonExtension,
35
37
  ChangerawrMarkdown: () => ChangerawrMarkdown,
38
+ CodeBlockExtension: () => CodeBlockExtension,
39
+ CoreExtensions: () => CoreExtensions,
36
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,
37
49
  Logger: () => Logger,
38
50
  MarkdownParser: () => MarkdownParser,
39
51
  MarkdownRenderer: () => MarkdownRenderer,
52
+ ParagraphExtension: () => ParagraphExtension,
40
53
  PerformanceTimer: () => PerformanceTimer,
54
+ TaskListExtension: () => TaskListExtension,
55
+ TextExtension: () => TextExtension,
41
56
  astToJSONString: () => astToJSONString,
42
57
  astToTokens: () => astToTokens,
43
58
  basicSanitize: () => basicSanitize,
44
59
  compareTokens: () => compareTokens,
60
+ createCoreOnlyEngine: () => createCoreOnlyEngine,
45
61
  createCumEngine: () => createCumEngine,
46
62
  createCustomEngine: () => createCustomEngine,
47
63
  createDebugEngine: () => createDebugEngine,
@@ -95,39 +111,56 @@ var MarkdownParser = class {
95
111
  constructor(config) {
96
112
  this.rules = [];
97
113
  this.warnings = [];
98
- this.config = config || {};
114
+ this.config = {
115
+ debugMode: false,
116
+ maxIterations: 1e4,
117
+ validateMarkdown: false,
118
+ ...config
119
+ };
99
120
  }
100
121
  addRule(rule) {
101
122
  this.rules.push(rule);
102
123
  this.rules.sort((a, b) => {
103
- if (a.name.includes("alert") || a.name.includes("embed") || a.name.includes("button")) return -1;
104
- if (b.name.includes("alert") || b.name.includes("embed") || b.name.includes("button")) return 1;
105
- if (a.name === "task-list") return -1;
106
- if (b.name === "task-list") return 1;
107
- if (a.name === "list" && b.name === "task-list") return 1;
108
- 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;
109
140
  return a.name.localeCompare(b.name);
110
141
  });
111
142
  }
112
- setupDefaultRulesIfEmpty() {
113
- const hasDefaultRules = this.rules.some(
114
- (rule) => !["alert", "button", "embed"].includes(rule.name)
115
- );
116
- if (!hasDefaultRules) {
117
- this.setupDefaultRules();
118
- }
119
- }
120
143
  hasRule(name) {
121
144
  return this.rules.some((rule) => rule.name === name);
122
145
  }
123
146
  parse(markdown3) {
124
147
  this.warnings = [];
125
- 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
+ }
126
159
  const processedMarkdown = this.preprocessMarkdown(markdown3);
127
160
  const tokens = [];
128
161
  let remaining = processedMarkdown;
129
162
  let iterationCount = 0;
130
- const maxIterations = this.config.maxIterations || markdown3.length * 2;
163
+ const maxIterations = this.config.maxIterations || 1e4;
131
164
  while (remaining.length > 0 && iterationCount < maxIterations) {
132
165
  iterationCount++;
133
166
  let matched = false;
@@ -216,9 +249,6 @@ var MarkdownParser = class {
216
249
  clearWarnings() {
217
250
  this.warnings = [];
218
251
  }
219
- getIterationCount() {
220
- return 0;
221
- }
222
252
  preprocessMarkdown(markdown3) {
223
253
  if (this.config.validateMarkdown) {
224
254
  this.validateMarkdown(markdown3);
@@ -273,161 +303,6 @@ var MarkdownParser = class {
273
303
  }
274
304
  return processed;
275
305
  }
276
- setupDefaultRules() {
277
- this.addRule({
278
- name: "heading",
279
- pattern: /^(#{1,6})\s+(.+)$/m,
280
- render: (match) => ({
281
- type: "heading",
282
- content: match[2]?.trim() || "",
283
- raw: match[0] || "",
284
- attributes: {
285
- level: String(match[1]?.length || 1)
286
- }
287
- })
288
- });
289
- this.addRule({
290
- name: "codeblock",
291
- pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
292
- render: (match) => ({
293
- type: "codeblock",
294
- content: match[2] || "",
295
- raw: match[0] || "",
296
- attributes: {
297
- language: match[1] || "text"
298
- }
299
- })
300
- });
301
- this.addRule({
302
- name: "hard-break-backslash",
303
- pattern: /\\\s*\n/,
304
- render: (match) => ({
305
- type: "line-break",
306
- content: "",
307
- raw: match[0] || ""
308
- })
309
- });
310
- this.addRule({
311
- name: "hard-break-spaces",
312
- pattern: / +\n/,
313
- render: (match) => ({
314
- type: "line-break",
315
- content: "",
316
- raw: match[0] || ""
317
- })
318
- });
319
- this.addRule({
320
- name: "paragraph-break",
321
- pattern: /\n\s*\n/,
322
- render: (match) => ({
323
- type: "paragraph-break",
324
- content: "",
325
- raw: match[0] || ""
326
- })
327
- });
328
- this.addRule({
329
- name: "bold",
330
- pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
331
- render: (match) => ({
332
- type: "bold",
333
- content: match[1] || "",
334
- raw: match[0] || ""
335
- })
336
- });
337
- this.addRule({
338
- name: "italic",
339
- pattern: /\*((?:(?!\*).)+)\*/,
340
- render: (match) => ({
341
- type: "italic",
342
- content: match[1] || "",
343
- raw: match[0] || ""
344
- })
345
- });
346
- this.addRule({
347
- name: "code",
348
- pattern: /`([^`]+)`/,
349
- render: (match) => ({
350
- type: "code",
351
- content: match[1] || "",
352
- raw: match[0] || ""
353
- })
354
- });
355
- this.addRule({
356
- name: "image",
357
- pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
358
- render: (match) => ({
359
- type: "image",
360
- content: match[1] || "",
361
- raw: match[0] || "",
362
- attributes: {
363
- alt: match[1] || "",
364
- src: match[2] || "",
365
- title: match[3] || ""
366
- }
367
- })
368
- });
369
- this.addRule({
370
- name: "link",
371
- pattern: /\[([^\]]+)\]\(([^)]+)\)/,
372
- render: (match) => ({
373
- type: "link",
374
- content: match[1] || "",
375
- raw: match[0] || "",
376
- attributes: {
377
- href: match[2] || ""
378
- }
379
- })
380
- });
381
- this.addRule({
382
- name: "task-list",
383
- pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
384
- render: (match) => ({
385
- type: "task-item",
386
- content: match[3] || "",
387
- raw: match[0] || "",
388
- attributes: {
389
- checked: String((match[2] || "").toLowerCase() === "x")
390
- }
391
- })
392
- });
393
- this.addRule({
394
- name: "list",
395
- pattern: /^(\s*)[-*+]\s+(.+)$/m,
396
- render: (match) => ({
397
- type: "list-item",
398
- content: match[2] || "",
399
- raw: match[0] || ""
400
- })
401
- });
402
- this.addRule({
403
- name: "blockquote",
404
- pattern: /^>\s+(.+)$/m,
405
- render: (match) => ({
406
- type: "blockquote",
407
- content: match[1] || "",
408
- raw: match[0] || ""
409
- })
410
- });
411
- this.addRule({
412
- name: "hr",
413
- pattern: /^---$/m,
414
- render: (match) => ({
415
- type: "hr",
416
- content: "",
417
- raw: match[0] || ""
418
- })
419
- });
420
- this.addRule({
421
- name: "soft-break",
422
- pattern: /\n/,
423
- render: (match) => ({
424
- type: "soft-break",
425
- content: " ",
426
- // Convert to space for inline text
427
- raw: match[0] || ""
428
- })
429
- });
430
- }
431
306
  };
432
307
 
433
308
  // src/utils.ts
@@ -746,7 +621,6 @@ var MarkdownRenderer = class {
746
621
  debugMode: false,
747
622
  ...config
748
623
  };
749
- this.setupDefaultRules();
750
624
  }
751
625
  addRule(rule) {
752
626
  this.rules.set(rule.type, rule);
@@ -756,28 +630,20 @@ var MarkdownRenderer = class {
756
630
  }
757
631
  render(tokens) {
758
632
  this.warnings = [];
759
- 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));
760
641
  const combinedHtml = htmlParts.join("");
761
642
  if (this.config.sanitize && !this.config.allowUnsafeHtml) {
762
643
  return sanitizeHtml(combinedHtml);
763
644
  }
764
645
  return combinedHtml;
765
646
  }
766
- getWarnings() {
767
- return [...this.warnings];
768
- }
769
- getConfig() {
770
- return { ...this.config };
771
- }
772
- updateConfig(config) {
773
- this.config = { ...this.config, ...config };
774
- }
775
- setDebugMode(enabled) {
776
- this.config.debugMode = enabled;
777
- }
778
- clearWarnings() {
779
- this.warnings = [];
780
- }
781
647
  renderToken(token) {
782
648
  const rule = this.rules.get(token.type);
783
649
  if (rule) {
@@ -789,9 +655,6 @@ var MarkdownRenderer = class {
789
655
  return this.createErrorBlock(`Render error for ${token.type}: ${errorMessage}`);
790
656
  }
791
657
  }
792
- if (token.type === "text") {
793
- return escapeHtml(token.content || token.raw || "");
794
- }
795
658
  if (this.config.debugMode) {
796
659
  return this.createDebugBlock(token);
797
660
  }
@@ -819,15 +682,234 @@ var MarkdownRenderer = class {
819
682
  <strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
820
683
  </div>`;
821
684
  }
822
- setupDefaultRules() {
823
- 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
+ {
824
905
  type: "heading",
825
906
  render: (token) => {
826
907
  const level = parseInt(token.attributes?.level || "1");
827
908
  const text = token.content;
828
909
  const id = generateId(text);
829
910
  const escapedContent = escapeHtml(text);
830
- if (this.config.format === "html") {
911
+ const format = token.attributes?.format || "html";
912
+ if (format === "html") {
831
913
  return `<h${level} id="${id}">${escapedContent}</h${level}>`;
832
914
  }
833
915
  let headingClasses = "group relative flex items-center gap-2";
@@ -854,61 +936,108 @@ var MarkdownRenderer = class {
854
936
  return `<h${level} id="${id}" class="${headingClasses}">
855
937
  ${escapedContent}
856
938
  <a href="#${id}" class="opacity-0 group-hover:opacity-100 text-muted-foreground transition-opacity">
857
- <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
858
- <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"/>
859
- </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>
860
943
  </a>
861
944
  </h${level}>`;
862
945
  }
863
- });
864
- this.addRule({
865
- type: "bold",
866
- render: (token) => {
867
- const content = escapeHtml(token.content);
868
- if (this.config.format === "html") {
869
- return `<strong>${content}</strong>`;
870
- }
871
- return `<strong class="font-bold">${content}</strong>`;
872
- }
873
- });
874
- this.addRule({
875
- type: "italic",
876
- render: (token) => {
877
- const content = escapeHtml(token.content);
878
- if (this.config.format === "html") {
879
- return `<em>${content}</em>`;
880
- }
881
- return `<em class="italic">${content}</em>`;
882
- }
883
- });
884
- this.addRule({
885
- 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",
886
967
  render: (token) => {
887
- const content = escapeHtml(token.content);
888
- if (this.config.format === "html") {
889
- 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;">';
890
971
  }
891
- 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">';
892
973
  }
893
- });
894
- this.addRule({
895
- 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",
896
1000
  render: (token) => {
897
- const language = token.attributes?.language || "text";
898
- const escapedCode = escapeHtml(token.content);
899
- if (this.config.format === "html") {
900
- 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" />`;
901
1008
  }
902
- 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" />`;
903
1010
  }
904
- });
905
- 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
+ {
906
1034
  type: "link",
907
1035
  render: (token) => {
908
1036
  const href = token.attributes?.href || "#";
909
1037
  const escapedHref = escapeHtml(href);
910
1038
  const escapedText = escapeHtml(token.content);
911
- if (this.config.format === "html") {
1039
+ const format = token.attributes?.format || "html";
1040
+ if (format === "html") {
912
1041
  return `<a href="${escapedHref}" target="_blank" rel="noopener noreferrer">${escapedText}</a>`;
913
1042
  }
914
1043
  return `<a href="${escapedHref}" class="text-primary hover:underline inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
@@ -920,47 +1049,55 @@ var MarkdownRenderer = class {
920
1049
  </svg>
921
1050
  </a>`;
922
1051
  }
923
- });
924
- 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
+ {
925
1072
  type: "list-item",
926
1073
  render: (token) => `<li>${escapeHtml(token.content)}</li>`
927
- });
928
- this.addRule({
929
- type: "blockquote",
930
- render: (token) => {
931
- const content = escapeHtml(token.content);
932
- if (this.config.format === "html") {
933
- return `<blockquote style="border-left: 2px solid #d1d5db; padding: 8px 0 8px 16px; margin: 16px 0; font-style: italic; color: #6b7280;">${content}</blockquote>`;
934
- }
935
- return `<blockquote class="pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4">${content}</blockquote>`;
936
- }
937
- });
938
- this.addRule({
939
- type: "text",
940
- render: (token) => {
941
- if (!token.content) return "";
942
- return escapeHtml(token.content);
943
- }
944
- });
945
- this.addRule({
946
- type: "paragraph",
947
- render: (token) => {
948
- if (!token.content) return "";
949
- const content = token.content.trim();
950
- if (!content) return "";
951
- const processedContent = content.includes("<br>") ? content : escapeHtml(content);
952
- if (this.config.format === "html") {
953
- 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")
954
1089
  }
955
- return `<p class="leading-7 mb-4">${processedContent}</p>`;
956
- }
957
- });
958
- this.addRule({
1090
+ })
1091
+ }
1092
+ ],
1093
+ renderRules: [
1094
+ {
959
1095
  type: "task-item",
960
1096
  render: (token) => {
961
1097
  const isChecked = token.attributes?.checked === "true";
962
1098
  const escapedContent = escapeHtml(token.content);
963
- if (this.config.format === "html") {
1099
+ const format = token.attributes?.format || "html";
1100
+ if (format === "html") {
964
1101
  return `<div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
965
1102
  <input type="checkbox" ${isChecked ? "checked" : ""} disabled style="margin: 0;" />
966
1103
  <span${isChecked ? ' style="text-decoration: line-through; color: #6b7280;"' : ""}>${escapedContent}</span>
@@ -972,44 +1109,65 @@ var MarkdownRenderer = class {
972
1109
  <span${isChecked ? ' class="line-through text-muted-foreground"' : ""}>${escapedContent}</span>
973
1110
  </div>`;
974
1111
  }
975
- });
976
- this.addRule({
977
- 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",
978
1123
  render: (token) => {
979
- const src = token.attributes?.src || "";
980
- const alt = token.attributes?.alt || "";
981
- const title = token.attributes?.title || "";
982
- const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
983
- if (this.config.format === "html") {
984
- 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>`;
985
1131
  }
986
- 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>`;
987
1133
  }
988
- });
989
- this.addRule({
990
- type: "hr",
991
- render: () => {
992
- if (this.config.format === "html") {
993
- return '<hr style="margin: 24px 0; border: none; border-top: 1px solid #d1d5db;">';
994
- }
995
- 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);
996
1148
  }
997
- });
998
- this.addRule({
999
- type: "line-break",
1000
- render: () => "<br>"
1001
- });
1002
- this.addRule({
1003
- type: "paragraph-break",
1004
- render: () => "</p><p>"
1005
- });
1006
- this.addRule({
1007
- type: "soft-break",
1008
- render: (token) => token.content || " "
1009
- });
1010
- }
1149
+ }
1150
+ ]
1011
1151
  };
1012
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
+
1013
1171
  // src/extensions/alert.ts
1014
1172
  var AlertExtension = {
1015
1173
  name: "alert",
@@ -1089,26 +1247,19 @@ var ButtonExtension = {
1089
1247
  name: "button",
1090
1248
  pattern: /\[button:([^\]]+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
1091
1249
  render: (match) => {
1092
- const text = match[1] || "";
1093
- const href = match[2] || "";
1094
- const optionsString = match[3] || "";
1095
- const options = optionsString.split(",").map((opt) => opt.trim()).filter(Boolean);
1096
- const styleOptions = ["default", "primary", "secondary", "success", "danger", "outline", "ghost"];
1097
- const style = options.find((opt) => styleOptions.includes(opt)) || "primary";
1098
- const sizeOptions = ["sm", "md", "lg"];
1099
- const size = options.find((opt) => sizeOptions.includes(opt)) || "md";
1100
- const disabled = options.includes("disabled");
1101
- const target = options.includes("self") ? "_self" : "_blank";
1250
+ const options = match[3] ? match[3].split(",").map((opt) => opt.trim()) : [];
1102
1251
  return {
1103
1252
  type: "button",
1104
- content: text,
1253
+ content: match[1] || "",
1105
1254
  raw: match[0] || "",
1106
1255
  attributes: {
1107
- href,
1108
- style,
1109
- size,
1110
- disabled: disabled.toString(),
1111
- 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"
1112
1263
  }
1113
1264
  };
1114
1265
  }
@@ -1123,53 +1274,92 @@ var ButtonExtension = {
1123
1274
  const size = token.attributes?.size || "md";
1124
1275
  const disabled = token.attributes?.disabled === "true";
1125
1276
  const target = token.attributes?.target || "_blank";
1126
- const text = token.content;
1127
- 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();
1128
1351
  const targetAttr = target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : target === "_self" ? ' target="_self"' : "";
1129
1352
  const disabledAttr = disabled ? ' aria-disabled="true" tabindex="-1"' : "";
1130
- 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>` : "";
1131
1356
  return `<a href="${href}" class="${classes}"${targetAttr}${disabledAttr}>
1132
- ${text}${externalIcon}
1133
- </a>`;
1357
+ <span class="relative z-10">${token.content}</span>${externalIcon}
1358
+ </a>`;
1134
1359
  }
1135
1360
  }
1136
1361
  ]
1137
1362
  };
1138
- function buildButtonClasses(style, size) {
1139
- const base = [
1140
- "inline-flex",
1141
- "items-center",
1142
- "justify-center",
1143
- "font-medium",
1144
- "rounded-lg",
1145
- "transition-colors",
1146
- "focus:outline-none",
1147
- "focus:ring-2",
1148
- "focus:ring-offset-2",
1149
- "disabled:opacity-50",
1150
- "disabled:cursor-not-allowed"
1151
- ];
1152
- const sizes = {
1153
- sm: ["px-3", "py-1.5", "text-sm"],
1154
- md: ["px-4", "py-2", "text-base"],
1155
- lg: ["px-6", "py-3", "text-lg"]
1156
- };
1157
- const styles = {
1158
- default: ["bg-slate-600", "text-white", "hover:bg-slate-700", "focus:ring-slate-500"],
1159
- primary: ["bg-blue-600", "text-white", "hover:bg-blue-700", "focus:ring-blue-500"],
1160
- secondary: ["bg-gray-600", "text-white", "hover:bg-gray-700", "focus:ring-gray-500"],
1161
- success: ["bg-green-600", "text-white", "hover:bg-green-700", "focus:ring-green-500"],
1162
- danger: ["bg-red-600", "text-white", "hover:bg-red-700", "focus:ring-red-500"],
1163
- outline: ["border", "border-blue-600", "text-blue-600", "hover:bg-blue-50", "focus:ring-blue-500"],
1164
- ghost: ["text-gray-700", "hover:bg-gray-100", "focus:ring-gray-500"]
1165
- };
1166
- const allClasses = [
1167
- ...base,
1168
- ...sizes[size] ?? sizes.md ?? [],
1169
- ...styles[style] ?? styles.primary ?? []
1170
- ];
1171
- return allClasses.join(" ");
1172
- }
1173
1363
 
1174
1364
  // src/extensions/embed.ts
1175
1365
  var EmbedExtension = {
@@ -1247,8 +1437,7 @@ function renderYouTubeEmbed(url, options, classes) {
1247
1437
  params.set("rel", "0");
1248
1438
  params.set("modestbranding", "1");
1249
1439
  const embedUrl = `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
1250
- return `
1251
- <div class="${classes}">
1440
+ return `<div class="${classes}">
1252
1441
  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
1253
1442
  <iframe
1254
1443
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
@@ -1276,8 +1465,7 @@ function renderCodePenEmbed(url, options, classes) {
1276
1465
  "editable": "true"
1277
1466
  });
1278
1467
  const embedUrl = `https://codepen.io/${user}/embed/${penId}?${embedParams.toString()}`;
1279
- return `
1280
- <div class="${classes}">
1468
+ return `<div class="${classes}">
1281
1469
  <iframe
1282
1470
  height="${height}"
1283
1471
  style="width: 100%; border: 0;"
@@ -1301,8 +1489,7 @@ function renderVimeoEmbed(url, options, classes) {
1301
1489
  if (options.mute === "1") params.set("muted", "1");
1302
1490
  if (options.loop === "1") params.set("loop", "1");
1303
1491
  const embedUrl = `https://player.vimeo.com/video/${videoId}?${params.toString()}`;
1304
- return `
1305
- <div class="${classes}">
1492
+ return `<div class="${classes}">
1306
1493
  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
1307
1494
  <iframe
1308
1495
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
@@ -1318,8 +1505,7 @@ function renderVimeoEmbed(url, options, classes) {
1318
1505
  function renderSpotifyEmbed(url, options, classes) {
1319
1506
  const embedUrl = url.replace("open.spotify.com", "open.spotify.com/embed");
1320
1507
  const height = options.height || "380";
1321
- return `
1322
- <div class="${classes}">
1508
+ return `<div class="${classes}">
1323
1509
  <iframe
1324
1510
  style="border-radius: 12px;"
1325
1511
  src="${embedUrl}"
@@ -1342,8 +1528,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
1342
1528
  if (!embedUrl.includes("?")) {
1343
1529
  embedUrl += `?view=${view}`;
1344
1530
  }
1345
- return `
1346
- <div class="${classes}">
1531
+ return `<div class="${classes}">
1347
1532
  <iframe
1348
1533
  src="${embedUrl}"
1349
1534
  style="width: 100%; height: ${height}px; border: 0; border-radius: 4px; overflow: hidden;"
@@ -1356,8 +1541,7 @@ function renderCodeSandboxEmbed(url, options, classes) {
1356
1541
  function renderFigmaEmbed(url, options, classes) {
1357
1542
  const embedUrl = `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
1358
1543
  const height = options.height || "450";
1359
- return `
1360
- <div class="${classes}">
1544
+ return `<div class="${classes}">
1361
1545
  <iframe
1362
1546
  style="border: none;"
1363
1547
  width="100%"
@@ -1368,8 +1552,7 @@ function renderFigmaEmbed(url, options, classes) {
1368
1552
  </div>`;
1369
1553
  }
1370
1554
  function renderTwitterEmbed(url, _options, classes) {
1371
- return `
1372
- <div class="${classes}">
1555
+ return `<div class="${classes}">
1373
1556
  <div class="p-4">
1374
1557
  <div class="flex items-center gap-3 mb-3">
1375
1558
  <svg class="w-6 h-6 fill-current text-blue-500" viewBox="0 0 24 24">
@@ -1397,8 +1580,7 @@ function renderGitHubEmbed(url, _options, classes) {
1397
1580
  if (!owner || !repo) {
1398
1581
  return createErrorEmbed("Invalid GitHub URL", url, classes);
1399
1582
  }
1400
- return `
1401
- <div class="${classes}">
1583
+ return `<div class="${classes}">
1402
1584
  <div class="p-4">
1403
1585
  <div class="flex items-center gap-3 mb-3">
1404
1586
  <svg class="w-6 h-6 fill-current" viewBox="0 0 24 24">
@@ -1421,8 +1603,7 @@ function renderGitHubEmbed(url, _options, classes) {
1421
1603
  }
1422
1604
  function renderGenericEmbed(url, _options, classes) {
1423
1605
  const domain = extractDomain(url);
1424
- return `
1425
- <div class="${classes}">
1606
+ return `<div class="${classes}">
1426
1607
  <div class="p-4">
1427
1608
  <div class="flex items-center gap-3 mb-3">
1428
1609
  <div class="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
@@ -1446,8 +1627,7 @@ function renderGenericEmbed(url, _options, classes) {
1446
1627
  </div>`;
1447
1628
  }
1448
1629
  function createErrorEmbed(error, url, classes) {
1449
- return `
1450
- <div class="${classes}">
1630
+ return `<div class="${classes}">
1451
1631
  <div class="p-4 text-destructive">
1452
1632
  <div class="font-medium flex items-center gap-2">
1453
1633
  <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -1481,19 +1661,54 @@ var ChangerawrMarkdown = class {
1481
1661
  this.extensions = /* @__PURE__ */ new Map();
1482
1662
  this.parser = new MarkdownParser(config?.parser);
1483
1663
  this.renderer = new MarkdownRenderer(config?.renderer);
1484
- this.registerBuiltInExtensions();
1664
+ this.registerCoreExtensions();
1665
+ this.registerFeatureExtensions();
1485
1666
  if (config?.extensions) {
1486
1667
  config.extensions.forEach((extension) => {
1487
1668
  this.registerExtension(extension);
1488
1669
  });
1489
1670
  }
1490
- this.parser.setupDefaultRulesIfEmpty();
1491
1671
  }
1492
- /**
1493
- * Register a custom extension
1494
- */
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
+ }
1495
1682
  registerExtension(extension) {
1496
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
+ });
1497
1712
  this.extensions.set(extension.name, extension);
1498
1713
  extension.parseRules.forEach((rule) => {
1499
1714
  this.parser.addRule(rule);
@@ -1514,9 +1729,6 @@ var ChangerawrMarkdown = class {
1514
1729
  };
1515
1730
  }
1516
1731
  }
1517
- /**
1518
- * Unregister an extension
1519
- */
1520
1732
  unregisterExtension(name) {
1521
1733
  const extension = this.extensions.get(name);
1522
1734
  if (!extension) {
@@ -1530,46 +1742,25 @@ var ChangerawrMarkdown = class {
1530
1742
  return false;
1531
1743
  }
1532
1744
  }
1533
- /**
1534
- * Parse markdown content into tokens
1535
- */
1536
1745
  parse(markdown3) {
1537
1746
  return this.parser.parse(markdown3);
1538
1747
  }
1539
- /**
1540
- * Render tokens to HTML
1541
- */
1542
1748
  render(tokens) {
1543
1749
  return this.renderer.render(tokens);
1544
1750
  }
1545
- /**
1546
- * Parse and render markdown to HTML in one step
1547
- */
1548
1751
  toHtml(markdown3) {
1549
1752
  const tokens = this.parse(markdown3);
1550
1753
  return this.render(tokens);
1551
1754
  }
1552
- /**
1553
- * Get list of registered extensions
1554
- */
1555
1755
  getExtensions() {
1556
1756
  return Array.from(this.extensions.keys());
1557
1757
  }
1558
- /**
1559
- * Check if extension is registered
1560
- */
1561
1758
  hasExtension(name) {
1562
1759
  return this.extensions.has(name);
1563
1760
  }
1564
- /**
1565
- * Get parser warnings
1566
- */
1567
1761
  getWarnings() {
1568
1762
  return [...this.parser.getWarnings(), ...this.renderer.getWarnings()];
1569
1763
  }
1570
- /**
1571
- * Get debug information from last render
1572
- */
1573
1764
  getDebugInfo() {
1574
1765
  return {
1575
1766
  warnings: this.getWarnings(),
@@ -1579,9 +1770,6 @@ var ChangerawrMarkdown = class {
1579
1770
  iterationCount: 0
1580
1771
  };
1581
1772
  }
1582
- /**
1583
- * Get performance metrics for the last operation
1584
- */
1585
1773
  getPerformanceMetrics() {
1586
1774
  return {
1587
1775
  parseTime: 0,
@@ -1590,28 +1778,29 @@ var ChangerawrMarkdown = class {
1590
1778
  tokenCount: 0
1591
1779
  };
1592
1780
  }
1593
- /**
1594
- * Register built-in extensions
1595
- */
1596
- registerBuiltInExtensions() {
1597
- this.registerExtension(AlertExtension);
1598
- this.registerExtension(ButtonExtension);
1599
- this.registerExtension(EmbedExtension);
1600
- }
1601
- /**
1602
- * Rebuild parser and renderer with current extensions
1603
- */
1604
1781
  rebuildParserAndRenderer() {
1605
1782
  const parserConfig = this.parser.getConfig();
1606
1783
  const rendererConfig = this.renderer.getConfig();
1607
1784
  this.parser = new MarkdownParser(parserConfig);
1608
1785
  this.renderer = new MarkdownRenderer(rendererConfig);
1609
1786
  const extensionsToRegister = Array.from(this.extensions.values());
1610
- this.extensions.clear();
1611
- extensionsToRegister.forEach((extension) => {
1612
- 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
+ });
1613
1803
  });
1614
- this.parser.setupDefaultRulesIfEmpty();
1615
1804
  }
1616
1805
  };
1617
1806
  var markdown = new ChangerawrMarkdown();
@@ -2041,8 +2230,7 @@ function createHTMLEngine(config) {
2041
2230
  renderer: {
2042
2231
  format: "html",
2043
2232
  sanitize: true,
2044
- allowUnsafeHtml: false,
2045
- ...config?.parser
2233
+ allowUnsafeHtml: false
2046
2234
  }
2047
2235
  });
2048
2236
  }
@@ -2052,8 +2240,7 @@ function createTailwindEngine(config) {
2052
2240
  renderer: {
2053
2241
  format: "tailwind",
2054
2242
  sanitize: true,
2055
- allowUnsafeHtml: false,
2056
- ...config?.parser
2243
+ allowUnsafeHtml: false
2057
2244
  }
2058
2245
  });
2059
2246
  }
@@ -2072,21 +2259,18 @@ function createDebugEngine(config) {
2072
2259
  }
2073
2260
  });
2074
2261
  }
2075
- function createMinimalEngine(config) {
2076
- const engine = new ChangerawrMarkdown({
2077
- ...config,
2078
- extensions: []
2079
- // Override to prevent built-in extensions
2080
- });
2081
- if (engine.hasExtension("alert")) {
2082
- engine.unregisterExtension("alert");
2083
- }
2084
- if (engine.hasExtension("button")) {
2085
- engine.unregisterExtension("button");
2086
- }
2087
- if (engine.hasExtension("embed")) {
2088
- engine.unregisterExtension("embed");
2089
- }
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");
2090
2274
  return engine;
2091
2275
  }
2092
2276
  function createCustomEngine(extensions, config) {
@@ -2110,6 +2294,7 @@ var markdown2 = {
2110
2294
  createTailwindEngine,
2111
2295
  createDebugEngine,
2112
2296
  createMinimalEngine,
2297
+ createCoreOnlyEngine,
2113
2298
  createCustomEngine,
2114
2299
  // Standalone functions
2115
2300
  renderCum,
@@ -2118,6 +2303,24 @@ var markdown2 = {
2118
2303
  ChangerawrMarkdown,
2119
2304
  // Extensions
2120
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
2121
2324
  Alert: AlertExtension,
2122
2325
  Button: ButtonExtension,
2123
2326
  Embed: EmbedExtension
@@ -2178,6 +2381,14 @@ var presets = {
2178
2381
  sanitize: true
2179
2382
  }
2180
2383
  },
2384
+ /**
2385
+ * Core-only preset with just markdown basics
2386
+ */
2387
+ coreOnly: {
2388
+ renderer: {
2389
+ format: "tailwind"
2390
+ }
2391
+ },
2181
2392
  /**
2182
2393
  * Performance preset with minimal processing
2183
2394
  */
@@ -2194,6 +2405,9 @@ var presets = {
2194
2405
  };
2195
2406
  function createEngineWithPreset(presetName, additionalConfig) {
2196
2407
  const preset = presets[presetName];
2408
+ if (presetName === "coreOnly") {
2409
+ return createCoreOnlyEngine(additionalConfig);
2410
+ }
2197
2411
  return new ChangerawrMarkdown({
2198
2412
  ...preset,
2199
2413
  ...additionalConfig
@@ -2202,17 +2416,33 @@ function createEngineWithPreset(presetName, additionalConfig) {
2202
2416
  // Annotate the CommonJS export names for ESM import in node:
2203
2417
  0 && (module.exports = {
2204
2418
  AlertExtension,
2419
+ BlockquoteExtension,
2420
+ BoldExtension,
2205
2421
  ButtonExtension,
2206
2422
  ChangerawrMarkdown,
2423
+ CodeBlockExtension,
2424
+ CoreExtensions,
2207
2425
  EmbedExtension,
2426
+ HeadingExtension,
2427
+ HorizontalRuleExtension,
2428
+ ImageExtension,
2429
+ InlineCodeExtension,
2430
+ ItalicExtension,
2431
+ LineBreakExtension,
2432
+ LinkExtension,
2433
+ ListExtension,
2208
2434
  Logger,
2209
2435
  MarkdownParser,
2210
2436
  MarkdownRenderer,
2437
+ ParagraphExtension,
2211
2438
  PerformanceTimer,
2439
+ TaskListExtension,
2440
+ TextExtension,
2212
2441
  astToJSONString,
2213
2442
  astToTokens,
2214
2443
  basicSanitize,
2215
2444
  compareTokens,
2445
+ createCoreOnlyEngine,
2216
2446
  createCumEngine,
2217
2447
  createCustomEngine,
2218
2448
  createDebugEngine,