@changerawr/markdown 1.0.0 → 1.0.1

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.
@@ -1,117 +1,1246 @@
1
- "use strict";var j=Object.create;var w=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var q=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var G=(r,e)=>{for(var t in e)w(r,t,{get:e[t],enumerable:!0})},D=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of W(e))!F.call(r,s)&&s!==t&&w(r,s,{get:()=>e[s],enumerable:!(n=N(e,s))||n.enumerable});return r};var V=(r,e,t)=>(t=r!=null?j(q(r)):{},D(e||!r||!r.__esModule?w(t,"default",{value:r,enumerable:!0}):t,r)),Y=r=>D(w({},"__esModule",{value:!0}),r);var ue={};G(ue,{ChangerawrMarkdown:()=>m,createCumEngine:()=>T,default:()=>ce,parseCum:()=>y,parseMarkdown:()=>E,renderCum:()=>h,renderCumToHtml:()=>C,renderCumToJson:()=>P,renderCumToTailwind:()=>A,renderMarkdown:()=>M});module.exports=Y(ue);var b=class{constructor(e){this.rules=[];this.warnings=[];this.config=e||{}}addRule(e){this.rules.push(e),this.rules.sort((t,n)=>t.name.includes("alert")||t.name.includes("embed")||t.name.includes("button")?-1:n.name.includes("alert")||n.name.includes("embed")||n.name.includes("button")?1:t.name==="task-list"?-1:n.name==="task-list"||t.name==="list"&&n.name==="task-list"?1:n.name==="list"&&t.name==="task-list"?-1:t.name.localeCompare(n.name))}setupDefaultRulesIfEmpty(){this.rules.some(t=>!["alert","button","embed"].includes(t.name))||this.setupDefaultRules()}hasRule(e){return this.rules.some(t=>t.name===e)}parse(e){this.warnings=[],this.setupDefaultRulesIfEmpty();let t=this.preprocessMarkdown(e),n=[],s=t,i=0,a=this.config.maxIterations||e.length*2;for(;s.length>0&&i<a;){i++;let f=!1,c=null;for(let l of this.rules)try{let d=new RegExp(l.pattern.source,l.pattern.flags.replace("g","")),p=s.match(d);if(p&&p.index!==void 0){let u=p.index===0?1e3:1e3-p.index;(!c||u>c.priority||u===c.priority&&p.index<(c.match.index||0))&&(c={rule:l,match:p,priority:u})}}catch(d){this.config.debugMode&&console.warn(`Error in rule "${l.name}":`,d)}if(c&&c.match.index!==void 0){let{rule:l,match:d}=c,p=d.index;if(p>0){let u=s.slice(0,p);n.push({type:"text",content:u,raw:u}),s=s.slice(p);continue}try{let u=l.render(d);n.push({...u,raw:d[0]||""}),s=s.slice(d[0]?.length||0),f=!0}catch(u){let H=u instanceof Error?u.message:String(u);this.warnings.push(`Failed to render ${l.name}: ${H}`);let k=s[0];k&&(n.push({type:"text",content:k,raw:k}),s=s.slice(1))}}if(!f){let l=s[0];l&&(n.push({type:"text",content:l,raw:l}),s=s.slice(1))}}return i>=a&&this.warnings.push("Parser hit maximum iterations - possible infinite loop detected"),this.postProcessTokens(n)}getWarnings(){return[...this.warnings]}getConfig(){return{...this.config}}updateConfig(e){this.config={...this.config,...e}}setDebugMode(e){this.config.debugMode=e}clearWarnings(){this.warnings=[]}getIterationCount(){return 0}preprocessMarkdown(e){return this.config.validateMarkdown&&this.validateMarkdown(e),e.replace(/\r\n/g,`
2
- `).replace(/\r/g,`
3
- `)}validateMarkdown(e){let t=e.match(/\*\*/g);t&&t.length%2!==0&&this.warnings.push("Unclosed bold markers (**) detected - some bold formatting may not work");let n=e.match(/(?<!\*)\*(?!\*)/g);n&&n.length%2!==0&&this.warnings.push("Unclosed italic markers (*) detected - some italic formatting may not work");let s=e.match(/```/g);s&&s.length%2!==0&&this.warnings.push("Unclosed code blocks (```) detected - some code formatting may not work");let i=e.match(/`/g);i&&i.length%2!==0&&this.warnings.push("Unclosed inline code markers (`) detected - some code formatting may not work")}postProcessTokens(e){let t=[],n=0;for(;n<e.length;){let s=e[n];if(s&&s.type==="text"){let i=s.content||s.raw||"",a=n+1;for(;a<e.length&&e[a]&&e[a].type==="text";){let g=e[a];i+=g.content||g.raw||"",a++}(i.trim().length>0||i.includes(`
4
- `))&&t.push({type:"text",content:i,raw:i}),n=a}else s&&t.push(s),n++}return t}setupDefaultRules(){this.addRule({name:"heading",pattern:/^(#{1,6})\s+(.+)$/m,render:e=>({type:"heading",content:e[2]?.trim()||"",raw:e[0]||"",attributes:{level:String(e[1]?.length||1)}})}),this.addRule({name:"codeblock",pattern:/```(\w+)?\s*\n([\s\S]*?)\n```/,render:e=>({type:"codeblock",content:e[2]||"",raw:e[0]||"",attributes:{language:e[1]||"text"}})}),this.addRule({name:"hard-break-backslash",pattern:/\\\s*\n/,render:e=>({type:"line-break",content:"",raw:e[0]||""})}),this.addRule({name:"hard-break-spaces",pattern:/ +\n/,render:e=>({type:"line-break",content:"",raw:e[0]||""})}),this.addRule({name:"paragraph-break",pattern:/\n\s*\n/,render:e=>({type:"paragraph-break",content:"",raw:e[0]||""})}),this.addRule({name:"bold",pattern:/\*\*((?:(?!\*\*).)+)\*\*/,render:e=>({type:"bold",content:e[1]||"",raw:e[0]||""})}),this.addRule({name:"italic",pattern:/\*((?:(?!\*).)+)\*/,render:e=>({type:"italic",content:e[1]||"",raw:e[0]||""})}),this.addRule({name:"code",pattern:/`([^`]+)`/,render:e=>({type:"code",content:e[1]||"",raw:e[0]||""})}),this.addRule({name:"image",pattern:/!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,render:e=>({type:"image",content:e[1]||"",raw:e[0]||"",attributes:{alt:e[1]||"",src:e[2]||"",title:e[3]||""}})}),this.addRule({name:"link",pattern:/\[([^\]]+)\]\(([^)]+)\)/,render:e=>({type:"link",content:e[1]||"",raw:e[0]||"",attributes:{href:e[2]||""}})}),this.addRule({name:"task-list",pattern:/^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,render:e=>({type:"task-item",content:e[3]||"",raw:e[0]||"",attributes:{checked:String((e[2]||"").toLowerCase()==="x")}})}),this.addRule({name:"list",pattern:/^(\s*)[-*+]\s+(.+)$/m,render:e=>({type:"list-item",content:e[2]||"",raw:e[0]||""})}),this.addRule({name:"blockquote",pattern:/^>\s+(.+)$/m,render:e=>({type:"blockquote",content:e[1]||"",raw:e[0]||""})}),this.addRule({name:"hr",pattern:/^---$/m,render:e=>({type:"hr",content:"",raw:e[0]||""})}),this.addRule({name:"soft-break",pattern:/\n/,render:e=>({type:"soft-break",content:" ",raw:e[0]||""})})}};var $={sanitize:r=>r};typeof window<"u"&&import("dompurify").then(r=>{$=r.default}).catch(r=>{console.error("Failed to load DOMPurify",r)});var J=["h1","h2","h3","h4","h5","h6","p","br","strong","em","del","ins","a","img","ul","ol","li","blockquote","pre","code","table","thead","tbody","tr","th","td","div","span","sup","sub","hr","input","iframe","embed","object","param","video","audio","source","svg","path","polyline","line","circle","rect","g","defs","use","form","fieldset","legend","label","select","option","textarea","button"],X=["href","title","alt","src","class","id","target","rel","type","checked","disabled","loading","width","height","style","role","frameborder","allowfullscreen","allow","sandbox","scrolling","allowtransparency","name","seamless","srcdoc","data-*","viewBox","fill","stroke","stroke-width","stroke-linecap","stroke-linejoin","d","points","x1","y1","x2","y2","cx","cy","r","rx","ry","autoplay","controls","loop","muted","preload","poster","value","placeholder","required","readonly","maxlength","minlength","max","min","step","pattern","autocomplete","autofocus"];function o(r){let e={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return r.replace(/[&<>"']/g,t=>e[t]||t)}function _(r){return r.toLowerCase().replace(/[^\w\s-]/g,"").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").trim()}function I(r){try{if(r.includes("codepen.io/")||r.includes("youtube.com/embed/"))return r;if(typeof $?.sanitize=="function"){let e=$.sanitize(r,{ALLOWED_TAGS:J,ALLOWED_ATTR:X,ALLOW_DATA_ATTR:!0,ALLOW_UNKNOWN_PROTOCOLS:!1,SAFE_FOR_TEMPLATES:!1,WHOLE_DOCUMENT:!1,RETURN_DOM:!1,RETURN_DOM_FRAGMENT:!1,FORCE_BODY:!1,SANITIZE_DOM:!1,SANITIZE_NAMED_PROPS:!1,FORBID_ATTR:["onload","onerror","onclick","onmouseover","onmouseout","onfocus","onblur"],ADD_TAGS:["iframe","embed","object","param"],ADD_ATTR:["allow","allowfullscreen","frameborder","scrolling","allowtransparency","sandbox","loading","style","title","name","seamless","srcdoc"],ALLOWED_URI_REGEXP:/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|xxx):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i});return e.length<r.length*.7?R(r):e}return R(r)}catch(e){return console.error("Sanitization failed:",e),R(r)}}function R(r){return r.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"").replace(/on\w+\s*=\s*"[^"]*"/gi,"").replace(/on\w+\s*=\s*'[^']*'/gi,"").replace(/javascript:/gi,"")}function S(r){try{return new URL(r).hostname}catch{return r}}function U(r){let e={};return r&&r.split(",").forEach(t=>{let[n,s]=t.split(":").map(i=>i.trim());n&&s&&(e[n]=s)}),e}var x=class{constructor(e){this.rules=new Map;this.warnings=[];this.config={format:"tailwind",sanitize:!0,allowUnsafeHtml:!1,debugMode:!1,...e},this.setupDefaultRules()}addRule(e){this.rules.set(e.type,e)}hasRule(e){return this.rules.has(e)}render(e){this.warnings=[];let n=e.map(s=>this.renderToken(s)).join("");return this.config.sanitize&&!this.config.allowUnsafeHtml?I(n):n}getWarnings(){return[...this.warnings]}getConfig(){return{...this.config}}updateConfig(e){this.config={...this.config,...e}}setDebugMode(e){this.config.debugMode=e}clearWarnings(){this.warnings=[]}renderToken(e){let t=this.rules.get(e.type);if(t)try{return t.render(e)}catch(n){let s=n instanceof Error?n.message:String(n);return this.warnings.push(`Render error for ${e.type}: ${s}`),this.createErrorBlock(`Render error for ${e.type}: ${s}`)}return e.type==="text"?o(e.content||e.raw||""):this.config.debugMode?this.createDebugBlock(e):o(e.content||e.raw||"")}createErrorBlock(e){return this.config.format==="html"?`<div style="background-color: #fee; border: 1px solid #fcc; color: #c66; padding: 8px; border-radius: 4px; margin: 8px 0; font-size: 14px;">
5
- <strong>Render Error:</strong> ${o(e)}
6
- </div>`:`<div class="bg-red-100 border border-red-300 text-red-800 p-2 rounded text-sm mb-2">
7
- <strong>Render Error:</strong> ${o(e)}
8
- </div>`}createDebugBlock(e){return this.config.format==="html"?`<div style="background-color: #fffbf0; border: 1px solid #fed; color: #b8860b; padding: 8px; border-radius: 4px; margin: 8px 0; font-size: 14px;">
9
- <strong>Unknown token type:</strong> ${o(e.type)}<br>
10
- <strong>Content:</strong> ${o(e.content||e.raw||"")}
11
- </div>`:`<div class="bg-yellow-100 border border-yellow-300 text-yellow-800 p-2 rounded text-sm mb-2">
12
- <strong>Unknown token type:</strong> ${o(e.type)}<br>
13
- <strong>Content:</strong> ${o(e.content||e.raw||"")}
14
- </div>`}setupDefaultRules(){this.addRule({type:"heading",render:e=>{let t=parseInt(e.attributes?.level||"1"),n=e.content,s=_(n),i=o(n);if(this.config.format==="html")return`<h${t} id="${s}">${i}</h${t}>`;let a="group relative flex items-center gap-2";switch(t){case 1:a+=" text-3xl font-bold mt-8 mb-4";break;case 2:a+=" text-2xl font-semibold mt-6 mb-3";break;case 3:a+=" text-xl font-medium mt-5 mb-3";break;case 4:a+=" text-lg font-medium mt-4 mb-2";break;case 5:a+=" text-base font-medium mt-3 mb-2";break;case 6:a+=" text-sm font-medium mt-3 mb-2";break}return`<h${t} id="${s}" class="${a}">
15
- ${i}
16
- <a href="#${s}" class="opacity-0 group-hover:opacity-100 text-muted-foreground transition-opacity">
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/standalone.ts
31
+ var standalone_exports = {};
32
+ __export(standalone_exports, {
33
+ ChangerawrMarkdown: () => ChangerawrMarkdown,
34
+ createCumEngine: () => createCumEngine,
35
+ default: () => standalone_default,
36
+ parseCum: () => parseCum,
37
+ renderCum: () => renderCum,
38
+ renderCumToHtml: () => renderCumToHtml,
39
+ renderCumToJson: () => renderCumToJson,
40
+ renderCumToTailwind: () => renderCumToTailwind
41
+ });
42
+ module.exports = __toCommonJS(standalone_exports);
43
+
44
+ // src/parser.ts
45
+ var MarkdownParser = class {
46
+ constructor(config) {
47
+ this.rules = [];
48
+ this.warnings = [];
49
+ this.config = config || {};
50
+ }
51
+ addRule(rule) {
52
+ this.rules.push(rule);
53
+ this.rules.sort((a, b) => {
54
+ if (a.name.includes("alert") || a.name.includes("embed") || a.name.includes("button")) return -1;
55
+ if (b.name.includes("alert") || b.name.includes("embed") || b.name.includes("button")) return 1;
56
+ if (a.name === "task-list") return -1;
57
+ if (b.name === "task-list") return 1;
58
+ if (a.name === "list" && b.name === "task-list") return 1;
59
+ if (b.name === "list" && a.name === "task-list") return -1;
60
+ return a.name.localeCompare(b.name);
61
+ });
62
+ }
63
+ setupDefaultRulesIfEmpty() {
64
+ const hasDefaultRules = this.rules.some(
65
+ (rule) => !["alert", "button", "embed"].includes(rule.name)
66
+ );
67
+ if (!hasDefaultRules) {
68
+ this.setupDefaultRules();
69
+ }
70
+ }
71
+ hasRule(name) {
72
+ return this.rules.some((rule) => rule.name === name);
73
+ }
74
+ parse(markdown2) {
75
+ this.warnings = [];
76
+ this.setupDefaultRulesIfEmpty();
77
+ const processedMarkdown = this.preprocessMarkdown(markdown2);
78
+ const tokens = [];
79
+ let remaining = processedMarkdown;
80
+ let iterationCount = 0;
81
+ const maxIterations = this.config.maxIterations || markdown2.length * 2;
82
+ while (remaining.length > 0 && iterationCount < maxIterations) {
83
+ iterationCount++;
84
+ let matched = false;
85
+ let bestMatch = null;
86
+ for (const rule of this.rules) {
87
+ try {
88
+ const pattern = new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", ""));
89
+ const match = remaining.match(pattern);
90
+ if (match && match.index !== void 0) {
91
+ const priority = match.index === 0 ? 1e3 : 1e3 - match.index;
92
+ if (!bestMatch || priority > bestMatch.priority || priority === bestMatch.priority && match.index < (bestMatch.match.index || 0)) {
93
+ bestMatch = { rule, match, priority };
94
+ }
95
+ }
96
+ } catch (error) {
97
+ if (this.config.debugMode) {
98
+ console.warn(`Error in rule "${rule.name}":`, error);
99
+ }
100
+ }
101
+ }
102
+ if (bestMatch && bestMatch.match.index !== void 0) {
103
+ const { rule, match } = bestMatch;
104
+ const matchIndex = match.index;
105
+ if (matchIndex > 0) {
106
+ const textBefore = remaining.slice(0, matchIndex);
107
+ tokens.push({
108
+ type: "text",
109
+ content: textBefore,
110
+ raw: textBefore
111
+ });
112
+ remaining = remaining.slice(matchIndex);
113
+ continue;
114
+ }
115
+ try {
116
+ const token = rule.render(match);
117
+ tokens.push({
118
+ ...token,
119
+ raw: match[0] || ""
120
+ });
121
+ remaining = remaining.slice(match[0]?.length || 0);
122
+ matched = true;
123
+ } catch (error) {
124
+ const errorMessage = error instanceof Error ? error.message : String(error);
125
+ this.warnings.push(`Failed to render ${rule.name}: ${errorMessage}`);
126
+ const char = remaining[0];
127
+ if (char) {
128
+ tokens.push({
129
+ type: "text",
130
+ content: char,
131
+ raw: char
132
+ });
133
+ remaining = remaining.slice(1);
134
+ }
135
+ }
136
+ }
137
+ if (!matched) {
138
+ const char = remaining[0];
139
+ if (char) {
140
+ tokens.push({
141
+ type: "text",
142
+ content: char,
143
+ raw: char
144
+ });
145
+ remaining = remaining.slice(1);
146
+ }
147
+ }
148
+ }
149
+ if (iterationCount >= maxIterations) {
150
+ this.warnings.push("Parser hit maximum iterations - possible infinite loop detected");
151
+ }
152
+ const processedTokens = this.postProcessTokens(tokens);
153
+ return processedTokens;
154
+ }
155
+ getWarnings() {
156
+ return [...this.warnings];
157
+ }
158
+ getConfig() {
159
+ return { ...this.config };
160
+ }
161
+ updateConfig(config) {
162
+ this.config = { ...this.config, ...config };
163
+ }
164
+ setDebugMode(enabled) {
165
+ this.config.debugMode = enabled;
166
+ }
167
+ clearWarnings() {
168
+ this.warnings = [];
169
+ }
170
+ getIterationCount() {
171
+ return 0;
172
+ }
173
+ preprocessMarkdown(markdown2) {
174
+ if (this.config.validateMarkdown) {
175
+ this.validateMarkdown(markdown2);
176
+ }
177
+ return markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
178
+ }
179
+ validateMarkdown(markdown2) {
180
+ const boldMatches = markdown2.match(/\*\*/g);
181
+ if (boldMatches && boldMatches.length % 2 !== 0) {
182
+ this.warnings.push("Unclosed bold markers (**) detected - some bold formatting may not work");
183
+ }
184
+ const italicMatches = markdown2.match(/(?<!\*)\*(?!\*)/g);
185
+ if (italicMatches && italicMatches.length % 2 !== 0) {
186
+ this.warnings.push("Unclosed italic markers (*) detected - some italic formatting may not work");
187
+ }
188
+ const codeBlockMatches = markdown2.match(/```/g);
189
+ if (codeBlockMatches && codeBlockMatches.length % 2 !== 0) {
190
+ this.warnings.push("Unclosed code blocks (```) detected - some code formatting may not work");
191
+ }
192
+ const inlineCodeMatches = markdown2.match(/`/g);
193
+ if (inlineCodeMatches && inlineCodeMatches.length % 2 !== 0) {
194
+ this.warnings.push("Unclosed inline code markers (`) detected - some code formatting may not work");
195
+ }
196
+ }
197
+ postProcessTokens(tokens) {
198
+ const processed = [];
199
+ let i = 0;
200
+ while (i < tokens.length) {
201
+ const token = tokens[i];
202
+ if (token && token.type === "text") {
203
+ let textContent = token.content || token.raw || "";
204
+ let j = i + 1;
205
+ while (j < tokens.length && tokens[j] && tokens[j].type === "text") {
206
+ const nextToken = tokens[j];
207
+ textContent += nextToken.content || nextToken.raw || "";
208
+ j++;
209
+ }
210
+ if (textContent.trim().length > 0 || textContent.includes("\n")) {
211
+ processed.push({
212
+ type: "text",
213
+ content: textContent,
214
+ raw: textContent
215
+ });
216
+ }
217
+ i = j;
218
+ } else if (token) {
219
+ processed.push(token);
220
+ i++;
221
+ } else {
222
+ i++;
223
+ }
224
+ }
225
+ return processed;
226
+ }
227
+ setupDefaultRules() {
228
+ this.addRule({
229
+ name: "heading",
230
+ pattern: /^(#{1,6})\s+(.+)$/m,
231
+ render: (match) => ({
232
+ type: "heading",
233
+ content: match[2]?.trim() || "",
234
+ raw: match[0] || "",
235
+ attributes: {
236
+ level: String(match[1]?.length || 1)
237
+ }
238
+ })
239
+ });
240
+ this.addRule({
241
+ name: "codeblock",
242
+ pattern: /```(\w+)?\s*\n([\s\S]*?)\n```/,
243
+ render: (match) => ({
244
+ type: "codeblock",
245
+ content: match[2] || "",
246
+ raw: match[0] || "",
247
+ attributes: {
248
+ language: match[1] || "text"
249
+ }
250
+ })
251
+ });
252
+ this.addRule({
253
+ name: "hard-break-backslash",
254
+ pattern: /\\\s*\n/,
255
+ render: (match) => ({
256
+ type: "line-break",
257
+ content: "",
258
+ raw: match[0] || ""
259
+ })
260
+ });
261
+ this.addRule({
262
+ name: "hard-break-spaces",
263
+ pattern: / +\n/,
264
+ render: (match) => ({
265
+ type: "line-break",
266
+ content: "",
267
+ raw: match[0] || ""
268
+ })
269
+ });
270
+ this.addRule({
271
+ name: "paragraph-break",
272
+ pattern: /\n\s*\n/,
273
+ render: (match) => ({
274
+ type: "paragraph-break",
275
+ content: "",
276
+ raw: match[0] || ""
277
+ })
278
+ });
279
+ this.addRule({
280
+ name: "bold",
281
+ pattern: /\*\*((?:(?!\*\*).)+)\*\*/,
282
+ render: (match) => ({
283
+ type: "bold",
284
+ content: match[1] || "",
285
+ raw: match[0] || ""
286
+ })
287
+ });
288
+ this.addRule({
289
+ name: "italic",
290
+ pattern: /\*((?:(?!\*).)+)\*/,
291
+ render: (match) => ({
292
+ type: "italic",
293
+ content: match[1] || "",
294
+ raw: match[0] || ""
295
+ })
296
+ });
297
+ this.addRule({
298
+ name: "code",
299
+ pattern: /`([^`]+)`/,
300
+ render: (match) => ({
301
+ type: "code",
302
+ content: match[1] || "",
303
+ raw: match[0] || ""
304
+ })
305
+ });
306
+ this.addRule({
307
+ name: "image",
308
+ pattern: /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]+)")?\)/,
309
+ render: (match) => ({
310
+ type: "image",
311
+ content: match[1] || "",
312
+ raw: match[0] || "",
313
+ attributes: {
314
+ alt: match[1] || "",
315
+ src: match[2] || "",
316
+ title: match[3] || ""
317
+ }
318
+ })
319
+ });
320
+ this.addRule({
321
+ name: "link",
322
+ pattern: /\[([^\]]+)\]\(([^)]+)\)/,
323
+ render: (match) => ({
324
+ type: "link",
325
+ content: match[1] || "",
326
+ raw: match[0] || "",
327
+ attributes: {
328
+ href: match[2] || ""
329
+ }
330
+ })
331
+ });
332
+ this.addRule({
333
+ name: "task-list",
334
+ pattern: /^(\s*)-\s*\[([ xX])\]\s*(.+)$/m,
335
+ render: (match) => ({
336
+ type: "task-item",
337
+ content: match[3] || "",
338
+ raw: match[0] || "",
339
+ attributes: {
340
+ checked: String((match[2] || "").toLowerCase() === "x")
341
+ }
342
+ })
343
+ });
344
+ this.addRule({
345
+ name: "list",
346
+ pattern: /^(\s*)[-*+]\s+(.+)$/m,
347
+ render: (match) => ({
348
+ type: "list-item",
349
+ content: match[2] || "",
350
+ raw: match[0] || ""
351
+ })
352
+ });
353
+ this.addRule({
354
+ name: "blockquote",
355
+ pattern: /^>\s+(.+)$/m,
356
+ render: (match) => ({
357
+ type: "blockquote",
358
+ content: match[1] || "",
359
+ raw: match[0] || ""
360
+ })
361
+ });
362
+ this.addRule({
363
+ name: "hr",
364
+ pattern: /^---$/m,
365
+ render: (match) => ({
366
+ type: "hr",
367
+ content: "",
368
+ raw: match[0] || ""
369
+ })
370
+ });
371
+ this.addRule({
372
+ name: "soft-break",
373
+ pattern: /\n/,
374
+ render: (match) => ({
375
+ type: "soft-break",
376
+ content: " ",
377
+ // Convert to space for inline text
378
+ raw: match[0] || ""
379
+ })
380
+ });
381
+ }
382
+ };
383
+
384
+ // src/utils.ts
385
+ var DOMPurify = {
386
+ sanitize: (html) => html
387
+ };
388
+ if (typeof window !== "undefined") {
389
+ import("dompurify").then((module2) => {
390
+ DOMPurify = module2.default;
391
+ }).catch((err) => {
392
+ console.error("Failed to load DOMPurify", err);
393
+ });
394
+ }
395
+ var ALLOWED_TAGS = [
396
+ // Standard HTML
397
+ "h1",
398
+ "h2",
399
+ "h3",
400
+ "h4",
401
+ "h5",
402
+ "h6",
403
+ "p",
404
+ "br",
405
+ "strong",
406
+ "em",
407
+ "del",
408
+ "ins",
409
+ "a",
410
+ "img",
411
+ "ul",
412
+ "ol",
413
+ "li",
414
+ "blockquote",
415
+ "pre",
416
+ "code",
417
+ "table",
418
+ "thead",
419
+ "tbody",
420
+ "tr",
421
+ "th",
422
+ "td",
423
+ "div",
424
+ "span",
425
+ "sup",
426
+ "sub",
427
+ "hr",
428
+ "input",
429
+ // Embeds
430
+ "iframe",
431
+ "embed",
432
+ "object",
433
+ "param",
434
+ "video",
435
+ "audio",
436
+ "source",
437
+ // SVG
438
+ "svg",
439
+ "path",
440
+ "polyline",
441
+ "line",
442
+ "circle",
443
+ "rect",
444
+ "g",
445
+ "defs",
446
+ "use",
447
+ // Form elements
448
+ "form",
449
+ "fieldset",
450
+ "legend",
451
+ "label",
452
+ "select",
453
+ "option",
454
+ "textarea",
455
+ "button"
456
+ ];
457
+ var ALLOWED_ATTR = [
458
+ // Standard attributes
459
+ "href",
460
+ "title",
461
+ "alt",
462
+ "src",
463
+ "class",
464
+ "id",
465
+ "target",
466
+ "rel",
467
+ "type",
468
+ "checked",
469
+ "disabled",
470
+ "loading",
471
+ "width",
472
+ "height",
473
+ "style",
474
+ "role",
475
+ // Iframe attributes
476
+ "frameborder",
477
+ "allowfullscreen",
478
+ "allow",
479
+ "sandbox",
480
+ "scrolling",
481
+ "allowtransparency",
482
+ "name",
483
+ "seamless",
484
+ "srcdoc",
485
+ // Data attributes (for embeds)
486
+ "data-*",
487
+ // SVG attributes
488
+ "viewBox",
489
+ "fill",
490
+ "stroke",
491
+ "stroke-width",
492
+ "stroke-linecap",
493
+ "stroke-linejoin",
494
+ "d",
495
+ "points",
496
+ "x1",
497
+ "y1",
498
+ "x2",
499
+ "y2",
500
+ "cx",
501
+ "cy",
502
+ "r",
503
+ "rx",
504
+ "ry",
505
+ // Media attributes
506
+ "autoplay",
507
+ "controls",
508
+ "loop",
509
+ "muted",
510
+ "preload",
511
+ "poster",
512
+ // Form attributes
513
+ "value",
514
+ "placeholder",
515
+ "required",
516
+ "readonly",
517
+ "maxlength",
518
+ "minlength",
519
+ "max",
520
+ "min",
521
+ "step",
522
+ "pattern",
523
+ "autocomplete",
524
+ "autofocus"
525
+ ];
526
+ function escapeHtml(text) {
527
+ const map = {
528
+ "&": "&amp;",
529
+ "<": "&lt;",
530
+ ">": "&gt;",
531
+ '"': "&quot;",
532
+ "'": "&#39;"
533
+ };
534
+ return text.replace(/[&<>"']/g, (char) => map[char] || char);
535
+ }
536
+ function generateId(text) {
537
+ return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").trim();
538
+ }
539
+ function sanitizeHtml(html) {
540
+ try {
541
+ if (html.includes("codepen.io/") || html.includes("youtube.com/embed/")) {
542
+ return html;
543
+ }
544
+ if (typeof DOMPurify?.sanitize === "function") {
545
+ const sanitized = DOMPurify.sanitize(html, {
546
+ ALLOWED_TAGS,
547
+ ALLOWED_ATTR,
548
+ ALLOW_DATA_ATTR: true,
549
+ ALLOW_UNKNOWN_PROTOCOLS: false,
550
+ SAFE_FOR_TEMPLATES: false,
551
+ WHOLE_DOCUMENT: false,
552
+ RETURN_DOM: false,
553
+ RETURN_DOM_FRAGMENT: false,
554
+ FORCE_BODY: false,
555
+ SANITIZE_DOM: false,
556
+ SANITIZE_NAMED_PROPS: false,
557
+ FORBID_ATTR: ["onload", "onerror", "onclick", "onmouseover", "onmouseout", "onfocus", "onblur"],
558
+ ADD_TAGS: ["iframe", "embed", "object", "param"],
559
+ ADD_ATTR: [
560
+ "allow",
561
+ "allowfullscreen",
562
+ "frameborder",
563
+ "scrolling",
564
+ "allowtransparency",
565
+ "sandbox",
566
+ "loading",
567
+ "style",
568
+ "title",
569
+ "name",
570
+ "seamless",
571
+ "srcdoc"
572
+ ],
573
+ ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|xxx):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
574
+ });
575
+ if (sanitized.length < html.length * 0.7) {
576
+ return basicSanitize(html);
577
+ }
578
+ return sanitized;
579
+ }
580
+ return basicSanitize(html);
581
+ } catch (error) {
582
+ console.error("Sanitization failed:", error);
583
+ return basicSanitize(html);
584
+ }
585
+ }
586
+ function basicSanitize(html) {
587
+ return html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/on\w+\s*=\s*"[^"]*"/gi, "").replace(/on\w+\s*=\s*'[^']*'/gi, "").replace(/javascript:/gi, "");
588
+ }
589
+ function extractDomain(url) {
590
+ try {
591
+ return new URL(url).hostname;
592
+ } catch {
593
+ return url;
594
+ }
595
+ }
596
+ function parseOptions(options) {
597
+ const parsed = {};
598
+ if (!options) return parsed;
599
+ options.split(",").forEach((option) => {
600
+ const [key, value] = option.split(":").map((s) => s.trim());
601
+ if (key && value) {
602
+ parsed[key] = value;
603
+ }
604
+ });
605
+ return parsed;
606
+ }
607
+
608
+ // src/renderer.ts
609
+ var MarkdownRenderer = class {
610
+ constructor(config) {
611
+ this.rules = /* @__PURE__ */ new Map();
612
+ this.warnings = [];
613
+ this.config = {
614
+ format: "tailwind",
615
+ sanitize: true,
616
+ allowUnsafeHtml: false,
617
+ debugMode: false,
618
+ ...config
619
+ };
620
+ this.setupDefaultRules();
621
+ }
622
+ addRule(rule) {
623
+ this.rules.set(rule.type, rule);
624
+ }
625
+ hasRule(type) {
626
+ return this.rules.has(type);
627
+ }
628
+ render(tokens) {
629
+ this.warnings = [];
630
+ const htmlParts = tokens.map((token) => this.renderToken(token));
631
+ const combinedHtml = htmlParts.join("");
632
+ if (this.config.sanitize && !this.config.allowUnsafeHtml) {
633
+ return sanitizeHtml(combinedHtml);
634
+ }
635
+ return combinedHtml;
636
+ }
637
+ getWarnings() {
638
+ return [...this.warnings];
639
+ }
640
+ getConfig() {
641
+ return { ...this.config };
642
+ }
643
+ updateConfig(config) {
644
+ this.config = { ...this.config, ...config };
645
+ }
646
+ setDebugMode(enabled) {
647
+ this.config.debugMode = enabled;
648
+ }
649
+ clearWarnings() {
650
+ this.warnings = [];
651
+ }
652
+ renderToken(token) {
653
+ const rule = this.rules.get(token.type);
654
+ if (rule) {
655
+ try {
656
+ return rule.render(token);
657
+ } catch (error) {
658
+ const errorMessage = error instanceof Error ? error.message : String(error);
659
+ this.warnings.push(`Render error for ${token.type}: ${errorMessage}`);
660
+ return this.createErrorBlock(`Render error for ${token.type}: ${errorMessage}`);
661
+ }
662
+ }
663
+ if (token.type === "text") {
664
+ return escapeHtml(token.content || token.raw || "");
665
+ }
666
+ if (this.config.debugMode) {
667
+ return this.createDebugBlock(token);
668
+ }
669
+ return escapeHtml(token.content || token.raw || "");
670
+ }
671
+ createErrorBlock(message) {
672
+ if (this.config.format === "html") {
673
+ return `<div style="background-color: #fee; border: 1px solid #fcc; color: #c66; padding: 8px; border-radius: 4px; margin: 8px 0; font-size: 14px;">
674
+ <strong>Render Error:</strong> ${escapeHtml(message)}
675
+ </div>`;
676
+ }
677
+ return `<div class="bg-red-100 border border-red-300 text-red-800 p-2 rounded text-sm mb-2">
678
+ <strong>Render Error:</strong> ${escapeHtml(message)}
679
+ </div>`;
680
+ }
681
+ createDebugBlock(token) {
682
+ if (this.config.format === "html") {
683
+ return `<div style="background-color: #fffbf0; border: 1px solid #fed; color: #b8860b; padding: 8px; border-radius: 4px; margin: 8px 0; font-size: 14px;">
684
+ <strong>Unknown token type:</strong> ${escapeHtml(token.type)}<br>
685
+ <strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
686
+ </div>`;
687
+ }
688
+ return `<div class="bg-yellow-100 border border-yellow-300 text-yellow-800 p-2 rounded text-sm mb-2">
689
+ <strong>Unknown token type:</strong> ${escapeHtml(token.type)}<br>
690
+ <strong>Content:</strong> ${escapeHtml(token.content || token.raw || "")}
691
+ </div>`;
692
+ }
693
+ setupDefaultRules() {
694
+ this.addRule({
695
+ type: "heading",
696
+ render: (token) => {
697
+ const level = parseInt(token.attributes?.level || "1");
698
+ const text = token.content;
699
+ const id = generateId(text);
700
+ const escapedContent = escapeHtml(text);
701
+ if (this.config.format === "html") {
702
+ return `<h${level} id="${id}">${escapedContent}</h${level}>`;
703
+ }
704
+ let headingClasses = "group relative flex items-center gap-2";
705
+ switch (level) {
706
+ case 1:
707
+ headingClasses += " text-3xl font-bold mt-8 mb-4";
708
+ break;
709
+ case 2:
710
+ headingClasses += " text-2xl font-semibold mt-6 mb-3";
711
+ break;
712
+ case 3:
713
+ headingClasses += " text-xl font-medium mt-5 mb-3";
714
+ break;
715
+ case 4:
716
+ headingClasses += " text-lg font-medium mt-4 mb-2";
717
+ break;
718
+ case 5:
719
+ headingClasses += " text-base font-medium mt-3 mb-2";
720
+ break;
721
+ case 6:
722
+ headingClasses += " text-sm font-medium mt-3 mb-2";
723
+ break;
724
+ }
725
+ return `<h${level} id="${id}" class="${headingClasses}">
726
+ ${escapedContent}
727
+ <a href="#${id}" class="opacity-0 group-hover:opacity-100 text-muted-foreground transition-opacity">
17
728
  <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
18
729
  <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"/>
19
730
  </svg>
20
731
  </a>
21
- </h${t}>`}}),this.addRule({type:"bold",render:e=>{let t=o(e.content);return this.config.format==="html"?`<strong>${t}</strong>`:`<strong class="font-bold">${t}</strong>`}}),this.addRule({type:"italic",render:e=>{let t=o(e.content);return this.config.format==="html"?`<em>${t}</em>`:`<em class="italic">${t}</em>`}}),this.addRule({type:"code",render:e=>{let t=o(e.content);return this.config.format==="html"?`<code style="background-color: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 0.875rem;">${t}</code>`:`<code class="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">${t}</code>`}}),this.addRule({type:"codeblock",render:e=>{let t=e.attributes?.language||"text",n=o(e.content);return this.config.format==="html"?`<pre style="background-color: #f3f4f6; padding: 16px; border-radius: 6px; overflow-x: auto; margin: 16px 0;"><code class="language-${o(t)}">${n}</code></pre>`:`<pre class="bg-muted p-4 rounded-md overflow-x-auto my-4"><code class="language-${o(t)}">${n}</code></pre>`}}),this.addRule({type:"link",render:e=>{let t=e.attributes?.href||"#",n=o(t),s=o(e.content);return this.config.format==="html"?`<a href="${n}" target="_blank" rel="noopener noreferrer">${s}</a>`:`<a href="${n}" class="text-primary hover:underline inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
22
- ${s}
732
+ </h${level}>`;
733
+ }
734
+ });
735
+ this.addRule({
736
+ type: "bold",
737
+ render: (token) => {
738
+ const content = escapeHtml(token.content);
739
+ if (this.config.format === "html") {
740
+ return `<strong>${content}</strong>`;
741
+ }
742
+ return `<strong class="font-bold">${content}</strong>`;
743
+ }
744
+ });
745
+ this.addRule({
746
+ type: "italic",
747
+ render: (token) => {
748
+ const content = escapeHtml(token.content);
749
+ if (this.config.format === "html") {
750
+ return `<em>${content}</em>`;
751
+ }
752
+ return `<em class="italic">${content}</em>`;
753
+ }
754
+ });
755
+ this.addRule({
756
+ type: "code",
757
+ render: (token) => {
758
+ const content = escapeHtml(token.content);
759
+ if (this.config.format === "html") {
760
+ return `<code style="background-color: #f3f4f6; padding: 2px 6px; border-radius: 4px; font-family: monospace; font-size: 0.875rem;">${content}</code>`;
761
+ }
762
+ return `<code class="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">${content}</code>`;
763
+ }
764
+ });
765
+ this.addRule({
766
+ type: "codeblock",
767
+ render: (token) => {
768
+ const language = token.attributes?.language || "text";
769
+ const escapedCode = escapeHtml(token.content);
770
+ if (this.config.format === "html") {
771
+ 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>`;
772
+ }
773
+ return `<pre class="bg-muted p-4 rounded-md overflow-x-auto my-4"><code class="language-${escapeHtml(language)}">${escapedCode}</code></pre>`;
774
+ }
775
+ });
776
+ this.addRule({
777
+ type: "link",
778
+ render: (token) => {
779
+ const href = token.attributes?.href || "#";
780
+ const escapedHref = escapeHtml(href);
781
+ const escapedText = escapeHtml(token.content);
782
+ if (this.config.format === "html") {
783
+ return `<a href="${escapedHref}" target="_blank" rel="noopener noreferrer">${escapedText}</a>`;
784
+ }
785
+ return `<a href="${escapedHref}" class="text-primary hover:underline inline-flex items-center gap-1" target="_blank" rel="noopener noreferrer">
786
+ ${escapedText}
23
787
  <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link">
24
788
  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
25
789
  <polyline points="15 3 21 3 21 9"></polyline>
26
790
  <line x1="10" y1="14" x2="21" y2="3"></line>
27
791
  </svg>
28
- </a>`}}),this.addRule({type:"list-item",render:e=>`<li>${o(e.content)}</li>`}),this.addRule({type:"blockquote",render:e=>{let t=o(e.content);return this.config.format==="html"?`<blockquote style="border-left: 2px solid #d1d5db; padding: 8px 0 8px 16px; margin: 16px 0; font-style: italic; color: #6b7280;">${t}</blockquote>`:`<blockquote class="pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4">${t}</blockquote>`}}),this.addRule({type:"text",render:e=>e.content?o(e.content):""}),this.addRule({type:"paragraph",render:e=>{if(!e.content)return"";let t=e.content.trim();if(!t)return"";let n=t.includes("<br>")?t:o(t);return this.config.format==="html"?`<p style="line-height: 1.75; margin-bottom: 16px;">${n}</p>`:`<p class="leading-7 mb-4">${n}</p>`}}),this.addRule({type:"task-item",render:e=>{let t=e.attributes?.checked==="true",n=o(e.content);return this.config.format==="html"?`<div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
29
- <input type="checkbox" ${t?"checked":""} disabled style="margin: 0;" />
30
- <span${t?' style="text-decoration: line-through; color: #6b7280;"':""}>${n}</span>
31
- </div>`:`<div class="flex items-center gap-2 my-2 task-list-item">
32
- <input type="checkbox" ${t?"checked":""} disabled
792
+ </a>`;
793
+ }
794
+ });
795
+ this.addRule({
796
+ type: "list-item",
797
+ render: (token) => `<li>${escapeHtml(token.content)}</li>`
798
+ });
799
+ this.addRule({
800
+ type: "blockquote",
801
+ render: (token) => {
802
+ const content = escapeHtml(token.content);
803
+ if (this.config.format === "html") {
804
+ return `<blockquote style="border-left: 2px solid #d1d5db; padding: 8px 0 8px 16px; margin: 16px 0; font-style: italic; color: #6b7280;">${content}</blockquote>`;
805
+ }
806
+ return `<blockquote class="pl-4 py-2 border-l-2 border-border italic text-muted-foreground my-4">${content}</blockquote>`;
807
+ }
808
+ });
809
+ this.addRule({
810
+ type: "text",
811
+ render: (token) => {
812
+ if (!token.content) return "";
813
+ return escapeHtml(token.content);
814
+ }
815
+ });
816
+ this.addRule({
817
+ type: "paragraph",
818
+ render: (token) => {
819
+ if (!token.content) return "";
820
+ const content = token.content.trim();
821
+ if (!content) return "";
822
+ const processedContent = content.includes("<br>") ? content : escapeHtml(content);
823
+ if (this.config.format === "html") {
824
+ return `<p style="line-height: 1.75; margin-bottom: 16px;">${processedContent}</p>`;
825
+ }
826
+ return `<p class="leading-7 mb-4">${processedContent}</p>`;
827
+ }
828
+ });
829
+ this.addRule({
830
+ type: "task-item",
831
+ render: (token) => {
832
+ const isChecked = token.attributes?.checked === "true";
833
+ const escapedContent = escapeHtml(token.content);
834
+ if (this.config.format === "html") {
835
+ return `<div style="display: flex; align-items: center; gap: 8px; margin: 8px 0;">
836
+ <input type="checkbox" ${isChecked ? "checked" : ""} disabled style="margin: 0;" />
837
+ <span${isChecked ? ' style="text-decoration: line-through; color: #6b7280;"' : ""}>${escapedContent}</span>
838
+ </div>`;
839
+ }
840
+ return `<div class="flex items-center gap-2 my-2 task-list-item">
841
+ <input type="checkbox" ${isChecked ? "checked" : ""} disabled
33
842
  class="form-checkbox h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary" />
34
- <span${t?' class="line-through text-muted-foreground"':""}>${n}</span>
35
- </div>`}}),this.addRule({type:"image",render:e=>{let t=e.attributes?.src||"",n=e.attributes?.alt||"",s=e.attributes?.title||"",i=s?` title="${o(s)}"`:"";return this.config.format==="html"?`<img src="${o(t)}" alt="${o(n)}"${i} style="max-width: 100%; height: auto; border-radius: 8px; margin: 16px 0;" loading="lazy" />`:`<img src="${o(t)}" alt="${o(n)}"${i} class="max-w-full h-auto rounded-lg my-4" loading="lazy" />`}}),this.addRule({type:"hr",render:()=>this.config.format==="html"?'<hr style="margin: 24px 0; border: none; border-top: 1px solid #d1d5db;">':'<hr class="my-6 border-t border-border">'}),this.addRule({type:"line-break",render:()=>"<br>"}),this.addRule({type:"paragraph-break",render:()=>"</p><p>"}),this.addRule({type:"soft-break",render:e=>e.content||" "})}};var L={name:"alert",parseRules:[{name:"alert",pattern:/:::(\w+)(?:\s+(.*?))?\s*\n([\s\S]*?)\n:::/,render:r=>({type:"alert",content:r[3]?.trim()||"",raw:r[0]||"",attributes:{type:r[1]||"info",title:r[2]||""}})}],renderRules:[{type:"alert",render:r=>{let e=r.attributes?.type||"info",t=r.attributes?.title||"",n={info:{icon:"\u2139\uFE0F",classes:"bg-blue-500/10 border-blue-500/30 text-blue-600 border-l-blue-500"},warning:{icon:"\u26A0\uFE0F",classes:"bg-amber-500/10 border-amber-500/30 text-amber-600 border-l-amber-500"},error:{icon:"\u274C",classes:"bg-red-500/10 border-red-500/30 text-red-600 border-l-red-500"},success:{icon:"\u2705",classes:"bg-green-500/10 border-green-500/30 text-green-600 border-l-green-500"},tip:{icon:"\u{1F4A1}",classes:"bg-purple-500/10 border-purple-500/30 text-purple-600 border-l-purple-500"},note:{icon:"\u{1F4DD}",classes:"bg-gray-500/10 border-gray-500/30 text-gray-600 border-l-gray-500"}},s=n[e]||n.info,a=`border-l-4 p-4 mb-4 rounded-md transition-colors duration-200 ${s?.classes}`,g=t?`<div class="font-medium mb-2 flex items-center gap-2">
36
- <span class="text-lg" role="img" aria-label="${e}">${s?.icon}</span>
37
- <span>${t}</span>
38
- </div>`:`<div class="font-medium mb-2 flex items-center gap-2">
39
- <span class="text-lg" role="img" aria-label="${e}">${s?.icon}</span>
40
- <span>${e.charAt(0).toUpperCase()+e.slice(1)}</span>
41
- </div>`;return`<div class="${a}" role="alert" aria-live="polite">
42
- ${g}
43
- <div class="leading-relaxed">${r.content}</div>
44
- </div>`}}]};var z={name:"button",parseRules:[{name:"button",pattern:/\[button:([^\]]+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,render:r=>{let e=r[1]||"",t=r[2]||"",s=(r[3]||"").split(",").map(d=>d.trim()).filter(Boolean),i=["default","primary","secondary","success","danger","outline","ghost"],a=s.find(d=>i.includes(d))||"primary",g=["sm","md","lg"],f=s.find(d=>g.includes(d))||"md",c=s.includes("disabled"),l=s.includes("self")?"_self":"_blank";return{type:"button",content:e,raw:r[0]||"",attributes:{href:t,style:a,size:f,disabled:c.toString(),target:l}}}}],renderRules:[{type:"button",render:r=>{let e=r.attributes?.href||"#",t=r.attributes?.style||"primary",n=r.attributes?.size||"md",s=r.attributes?.disabled==="true",i=r.attributes?.target||"_blank",a=r.content,g=Z(t,n);return`<a href="${e}" class="${g}"${i==="_blank"?' target="_blank" rel="noopener noreferrer"':i==="_self"?' target="_self"':""}${s?' aria-disabled="true" tabindex="-1"':""}>
45
- ${a}${i==="_blank"&&!s?'<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>':""}
46
- </a>`}}]};function Z(r,e){let t=["inline-flex","items-center","justify-center","font-medium","rounded-lg","transition-colors","focus:outline-none","focus:ring-2","focus:ring-offset-2","disabled:opacity-50","disabled:cursor-not-allowed"],n={sm:["px-3","py-1.5","text-sm"],md:["px-4","py-2","text-base"],lg:["px-6","py-3","text-lg"]},s={default:["bg-slate-600","text-white","hover:bg-slate-700","focus:ring-slate-500"],primary:["bg-blue-600","text-white","hover:bg-blue-700","focus:ring-blue-500"],secondary:["bg-gray-600","text-white","hover:bg-gray-700","focus:ring-gray-500"],success:["bg-green-600","text-white","hover:bg-green-700","focus:ring-green-500"],danger:["bg-red-600","text-white","hover:bg-red-700","focus:ring-red-500"],outline:["border","border-blue-600","text-blue-600","hover:bg-blue-50","focus:ring-blue-500"],ghost:["text-gray-700","hover:bg-gray-100","focus:ring-gray-500"]};return[...t,...n[e]??n.md??[],...s[r]??s.primary??[]].join(" ")}var O={name:"embed",parseRules:[{name:"embed",pattern:/\[embed:(\w+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,render:r=>({type:"embed",content:r[2]||"",raw:r[0]||"",attributes:{provider:r[1]||"generic",url:r[2]||"",options:r[3]||""}})}],renderRules:[{type:"embed",render:r=>{let e=r.attributes?.provider||"generic",t=r.attributes?.url||"",n=r.attributes?.options||"";return K(e,t,n)}}]};function K(r,e,t){let n="rounded-lg border bg-card text-card-foreground shadow-sm mb-6 overflow-hidden",s=U(t);switch(r.toLowerCase()){case"youtube":return Q(e,s,n);case"codepen":return ee(e,s,n);case"figma":return se(e,s,n);case"twitter":case"tweet":return ie(e,s,n);case"github":return oe(e,s,n);case"vimeo":return te(e,s,n);case"spotify":return re(e,s,n);case"codesandbox":return ne(e,s,n);default:return ae(e,s,n)}}function Q(r,e,t){let n=le(r);if(!n)return v("Invalid YouTube URL",r,t);let s=new URLSearchParams;e.autoplay==="1"&&s.set("autoplay","1"),e.mute==="1"&&s.set("mute","1"),e.loop==="1"&&(s.set("loop","1"),s.set("playlist",n)),e.controls==="0"&&s.set("controls","0"),e.start&&s.set("start",e.start),s.set("rel","0"),s.set("modestbranding","1");let i=`https://www.youtube.com/embed/${n}?${s.toString()}`;return`
47
- <div class="${t}">
843
+ <span${isChecked ? ' class="line-through text-muted-foreground"' : ""}>${escapedContent}</span>
844
+ </div>`;
845
+ }
846
+ });
847
+ this.addRule({
848
+ type: "image",
849
+ render: (token) => {
850
+ const src = token.attributes?.src || "";
851
+ const alt = token.attributes?.alt || "";
852
+ const title = token.attributes?.title || "";
853
+ const titleAttr = title ? ` title="${escapeHtml(title)}"` : "";
854
+ if (this.config.format === "html") {
855
+ return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} style="max-width: 100%; height: auto; border-radius: 8px; margin: 16px 0;" loading="lazy" />`;
856
+ }
857
+ return `<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}"${titleAttr} class="max-w-full h-auto rounded-lg my-4" loading="lazy" />`;
858
+ }
859
+ });
860
+ this.addRule({
861
+ type: "hr",
862
+ render: () => {
863
+ if (this.config.format === "html") {
864
+ return '<hr style="margin: 24px 0; border: none; border-top: 1px solid #d1d5db;">';
865
+ }
866
+ return '<hr class="my-6 border-t border-border">';
867
+ }
868
+ });
869
+ this.addRule({
870
+ type: "line-break",
871
+ render: () => "<br>"
872
+ });
873
+ this.addRule({
874
+ type: "paragraph-break",
875
+ render: () => "</p><p>"
876
+ });
877
+ this.addRule({
878
+ type: "soft-break",
879
+ render: (token) => token.content || " "
880
+ });
881
+ }
882
+ };
883
+
884
+ // src/extensions/alert.ts
885
+ var AlertExtension = {
886
+ name: "alert",
887
+ parseRules: [
888
+ {
889
+ name: "alert",
890
+ pattern: /:::(\w+)(?:\s+(.*?))?\s*\n([\s\S]*?)\n:::/,
891
+ render: (match) => {
892
+ return {
893
+ type: "alert",
894
+ content: match[3]?.trim() || "",
895
+ raw: match[0] || "",
896
+ attributes: {
897
+ type: match[1] || "info",
898
+ title: match[2] || ""
899
+ }
900
+ };
901
+ }
902
+ }
903
+ ],
904
+ renderRules: [
905
+ {
906
+ type: "alert",
907
+ render: (token) => {
908
+ const type = token.attributes?.type || "info";
909
+ const title = token.attributes?.title || "";
910
+ const typeConfig = {
911
+ info: {
912
+ icon: "\u2139\uFE0F",
913
+ classes: "bg-blue-500/10 border-blue-500/30 text-blue-600 border-l-blue-500"
914
+ },
915
+ warning: {
916
+ icon: "\u26A0\uFE0F",
917
+ classes: "bg-amber-500/10 border-amber-500/30 text-amber-600 border-l-amber-500"
918
+ },
919
+ error: {
920
+ icon: "\u274C",
921
+ classes: "bg-red-500/10 border-red-500/30 text-red-600 border-l-red-500"
922
+ },
923
+ success: {
924
+ icon: "\u2705",
925
+ classes: "bg-green-500/10 border-green-500/30 text-green-600 border-l-green-500"
926
+ },
927
+ tip: {
928
+ icon: "\u{1F4A1}",
929
+ classes: "bg-purple-500/10 border-purple-500/30 text-purple-600 border-l-purple-500"
930
+ },
931
+ note: {
932
+ icon: "\u{1F4DD}",
933
+ classes: "bg-gray-500/10 border-gray-500/30 text-gray-600 border-l-gray-500"
934
+ }
935
+ };
936
+ const config = typeConfig[type] || typeConfig.info;
937
+ const baseClasses = "border-l-4 p-4 mb-4 rounded-md transition-colors duration-200";
938
+ const classes = `${baseClasses} ${config?.classes}`;
939
+ const titleHtml = title ? `<div class="font-medium mb-2 flex items-center gap-2">
940
+ <span class="text-lg" role="img" aria-label="${type}">${config?.icon}</span>
941
+ <span>${title}</span>
942
+ </div>` : `<div class="font-medium mb-2 flex items-center gap-2">
943
+ <span class="text-lg" role="img" aria-label="${type}">${config?.icon}</span>
944
+ <span>${type.charAt(0).toUpperCase() + type.slice(1)}</span>
945
+ </div>`;
946
+ return `<div class="${classes}" role="alert" aria-live="polite">
947
+ ${titleHtml}
948
+ <div class="leading-relaxed">${token.content}</div>
949
+ </div>`;
950
+ }
951
+ }
952
+ ]
953
+ };
954
+
955
+ // src/extensions/button.ts
956
+ var ButtonExtension = {
957
+ name: "button",
958
+ parseRules: [
959
+ {
960
+ name: "button",
961
+ pattern: /\[button:([^\]]+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
962
+ render: (match) => {
963
+ const text = match[1] || "";
964
+ const href = match[2] || "";
965
+ const optionsString = match[3] || "";
966
+ const options = optionsString.split(",").map((opt) => opt.trim()).filter(Boolean);
967
+ const styleOptions = ["default", "primary", "secondary", "success", "danger", "outline", "ghost"];
968
+ const style = options.find((opt) => styleOptions.includes(opt)) || "primary";
969
+ const sizeOptions = ["sm", "md", "lg"];
970
+ const size = options.find((opt) => sizeOptions.includes(opt)) || "md";
971
+ const disabled = options.includes("disabled");
972
+ const target = options.includes("self") ? "_self" : "_blank";
973
+ return {
974
+ type: "button",
975
+ content: text,
976
+ raw: match[0] || "",
977
+ attributes: {
978
+ href,
979
+ style,
980
+ size,
981
+ disabled: disabled.toString(),
982
+ target
983
+ }
984
+ };
985
+ }
986
+ }
987
+ ],
988
+ renderRules: [
989
+ {
990
+ type: "button",
991
+ render: (token) => {
992
+ const href = token.attributes?.href || "#";
993
+ const style = token.attributes?.style || "primary";
994
+ const size = token.attributes?.size || "md";
995
+ const disabled = token.attributes?.disabled === "true";
996
+ const target = token.attributes?.target || "_blank";
997
+ const text = token.content;
998
+ const classes = buildButtonClasses(style, size);
999
+ const targetAttr = target === "_blank" ? ' target="_blank" rel="noopener noreferrer"' : target === "_self" ? ' target="_self"' : "";
1000
+ const disabledAttr = disabled ? ' aria-disabled="true" tabindex="-1"' : "";
1001
+ 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>' : "";
1002
+ return `<a href="${href}" class="${classes}"${targetAttr}${disabledAttr}>
1003
+ ${text}${externalIcon}
1004
+ </a>`;
1005
+ }
1006
+ }
1007
+ ]
1008
+ };
1009
+ function buildButtonClasses(style, size) {
1010
+ const base = [
1011
+ "inline-flex",
1012
+ "items-center",
1013
+ "justify-center",
1014
+ "font-medium",
1015
+ "rounded-lg",
1016
+ "transition-colors",
1017
+ "focus:outline-none",
1018
+ "focus:ring-2",
1019
+ "focus:ring-offset-2",
1020
+ "disabled:opacity-50",
1021
+ "disabled:cursor-not-allowed"
1022
+ ];
1023
+ const sizes = {
1024
+ sm: ["px-3", "py-1.5", "text-sm"],
1025
+ md: ["px-4", "py-2", "text-base"],
1026
+ lg: ["px-6", "py-3", "text-lg"]
1027
+ };
1028
+ const styles = {
1029
+ default: ["bg-slate-600", "text-white", "hover:bg-slate-700", "focus:ring-slate-500"],
1030
+ primary: ["bg-blue-600", "text-white", "hover:bg-blue-700", "focus:ring-blue-500"],
1031
+ secondary: ["bg-gray-600", "text-white", "hover:bg-gray-700", "focus:ring-gray-500"],
1032
+ success: ["bg-green-600", "text-white", "hover:bg-green-700", "focus:ring-green-500"],
1033
+ danger: ["bg-red-600", "text-white", "hover:bg-red-700", "focus:ring-red-500"],
1034
+ outline: ["border", "border-blue-600", "text-blue-600", "hover:bg-blue-50", "focus:ring-blue-500"],
1035
+ ghost: ["text-gray-700", "hover:bg-gray-100", "focus:ring-gray-500"]
1036
+ };
1037
+ const allClasses = [
1038
+ ...base,
1039
+ ...sizes[size] ?? sizes.md ?? [],
1040
+ ...styles[style] ?? styles.primary ?? []
1041
+ ];
1042
+ return allClasses.join(" ");
1043
+ }
1044
+
1045
+ // src/extensions/embed.ts
1046
+ var EmbedExtension = {
1047
+ name: "embed",
1048
+ parseRules: [
1049
+ {
1050
+ name: "embed",
1051
+ pattern: /\[embed:(\w+)\]\(([^)]+)\)(?:\{([^}]+)\})?/,
1052
+ render: (match) => {
1053
+ return {
1054
+ type: "embed",
1055
+ content: match[2] || "",
1056
+ // URL
1057
+ raw: match[0] || "",
1058
+ attributes: {
1059
+ provider: match[1] || "generic",
1060
+ url: match[2] || "",
1061
+ options: match[3] || ""
1062
+ }
1063
+ };
1064
+ }
1065
+ }
1066
+ ],
1067
+ renderRules: [
1068
+ {
1069
+ type: "embed",
1070
+ render: (token) => {
1071
+ const provider = token.attributes?.provider || "generic";
1072
+ const url = token.attributes?.url || "";
1073
+ const options = token.attributes?.options || "";
1074
+ return renderEmbed(provider, url, options);
1075
+ }
1076
+ }
1077
+ ]
1078
+ };
1079
+ function renderEmbed(provider, url, options) {
1080
+ const baseClasses = "rounded-lg border bg-card text-card-foreground shadow-sm mb-6 overflow-hidden";
1081
+ const parsedOptions = parseOptions(options);
1082
+ switch (provider.toLowerCase()) {
1083
+ case "youtube":
1084
+ return renderYouTubeEmbed(url, parsedOptions, baseClasses);
1085
+ case "codepen":
1086
+ return renderCodePenEmbed(url, parsedOptions, baseClasses);
1087
+ case "figma":
1088
+ return renderFigmaEmbed(url, parsedOptions, baseClasses);
1089
+ case "twitter":
1090
+ case "tweet":
1091
+ return renderTwitterEmbed(url, parsedOptions, baseClasses);
1092
+ case "github":
1093
+ return renderGitHubEmbed(url, parsedOptions, baseClasses);
1094
+ case "vimeo":
1095
+ return renderVimeoEmbed(url, parsedOptions, baseClasses);
1096
+ case "spotify":
1097
+ return renderSpotifyEmbed(url, parsedOptions, baseClasses);
1098
+ case "codesandbox":
1099
+ return renderCodeSandboxEmbed(url, parsedOptions, baseClasses);
1100
+ default:
1101
+ return renderGenericEmbed(url, parsedOptions, baseClasses);
1102
+ }
1103
+ }
1104
+ function renderYouTubeEmbed(url, options, classes) {
1105
+ const videoId = extractYouTubeId(url);
1106
+ if (!videoId) {
1107
+ return createErrorEmbed("Invalid YouTube URL", url, classes);
1108
+ }
1109
+ const params = new URLSearchParams();
1110
+ if (options.autoplay === "1") params.set("autoplay", "1");
1111
+ if (options.mute === "1") params.set("mute", "1");
1112
+ if (options.loop === "1") {
1113
+ params.set("loop", "1");
1114
+ params.set("playlist", videoId);
1115
+ }
1116
+ if (options.controls === "0") params.set("controls", "0");
1117
+ if (options.start) params.set("start", options.start);
1118
+ params.set("rel", "0");
1119
+ params.set("modestbranding", "1");
1120
+ const embedUrl = `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
1121
+ return `
1122
+ <div class="${classes}">
48
1123
  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
49
1124
  <iframe
50
1125
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
51
- src="${i}"
1126
+ src="${embedUrl}"
52
1127
  title="YouTube video player"
53
1128
  frameborder="0"
54
1129
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
55
1130
  allowfullscreen>
56
1131
  </iframe>
57
1132
  </div>
58
- </div>`}function ee(r,e,t){let n=r.match(/codepen\.io\/([^\/]+)\/(?:pen|embed)\/([^\/\?#]+)/);if(!n)return v("Invalid CodePen URL",r,t);let[,s,i]=n,a=e.height||"400",g=e.theme==="light"?"light":"dark",f=e.tab||"result",c=new URLSearchParams({"default-tab":f,"theme-id":g,editable:"true"}),l=`https://codepen.io/${s}/embed/${i}?${c.toString()}`;return`
59
- <div class="${t}">
1133
+ </div>`;
1134
+ }
1135
+ function renderCodePenEmbed(url, options, classes) {
1136
+ const match = url.match(/codepen\.io\/([^\/]+)\/(?:pen|embed)\/([^\/\?#]+)/);
1137
+ if (!match) {
1138
+ return createErrorEmbed("Invalid CodePen URL", url, classes);
1139
+ }
1140
+ const [, user, penId] = match;
1141
+ const height = options.height || "400";
1142
+ const theme = options.theme === "light" ? "light" : "dark";
1143
+ const defaultTab = options.tab || "result";
1144
+ const embedParams = new URLSearchParams({
1145
+ "default-tab": defaultTab,
1146
+ "theme-id": theme,
1147
+ "editable": "true"
1148
+ });
1149
+ const embedUrl = `https://codepen.io/${user}/embed/${penId}?${embedParams.toString()}`;
1150
+ return `
1151
+ <div class="${classes}">
60
1152
  <iframe
61
- height="${a}"
1153
+ height="${height}"
62
1154
  style="width: 100%; border: 0;"
63
1155
  scrolling="no"
64
- title="CodePen Embed - ${i}"
65
- src="${l}"
1156
+ title="CodePen Embed - ${penId}"
1157
+ src="${embedUrl}"
66
1158
  frameborder="0"
67
1159
  loading="lazy"
68
1160
  allowtransparency="true"
69
1161
  allowfullscreen="true">
70
1162
  </iframe>
71
- </div>`}function te(r,e,t){let n=de(r);if(!n)return v("Invalid Vimeo URL",r,t);let s=new URLSearchParams;e.autoplay==="1"&&s.set("autoplay","1"),e.mute==="1"&&s.set("muted","1"),e.loop==="1"&&s.set("loop","1");let i=`https://player.vimeo.com/video/${n}?${s.toString()}`;return`
72
- <div class="${t}">
1163
+ </div>`;
1164
+ }
1165
+ function renderVimeoEmbed(url, options, classes) {
1166
+ const videoId = extractVimeoId(url);
1167
+ if (!videoId) {
1168
+ return createErrorEmbed("Invalid Vimeo URL", url, classes);
1169
+ }
1170
+ const params = new URLSearchParams();
1171
+ if (options.autoplay === "1") params.set("autoplay", "1");
1172
+ if (options.mute === "1") params.set("muted", "1");
1173
+ if (options.loop === "1") params.set("loop", "1");
1174
+ const embedUrl = `https://player.vimeo.com/video/${videoId}?${params.toString()}`;
1175
+ return `
1176
+ <div class="${classes}">
73
1177
  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
74
1178
  <iframe
75
1179
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
76
- src="${i}"
1180
+ src="${embedUrl}"
77
1181
  title="Vimeo video player"
78
1182
  frameborder="0"
79
1183
  allow="autoplay; fullscreen; picture-in-picture"
80
1184
  allowfullscreen>
81
1185
  </iframe>
82
1186
  </div>
83
- </div>`}function re(r,e,t){let n=r.replace("open.spotify.com","open.spotify.com/embed"),s=e.height||"380";return`
84
- <div class="${t}">
1187
+ </div>`;
1188
+ }
1189
+ function renderSpotifyEmbed(url, options, classes) {
1190
+ const embedUrl = url.replace("open.spotify.com", "open.spotify.com/embed");
1191
+ const height = options.height || "380";
1192
+ return `
1193
+ <div class="${classes}">
85
1194
  <iframe
86
1195
  style="border-radius: 12px;"
87
- src="${n}"
1196
+ src="${embedUrl}"
88
1197
  width="100%"
89
- height="${s}"
1198
+ height="${height}"
90
1199
  frameborder="0"
91
1200
  allowfullscreen=""
92
1201
  allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
93
1202
  loading="lazy">
94
1203
  </iframe>
95
- </div>`}function ne(r,e,t){let n=r;r.includes("/s/")&&(n=r.replace("/s/","/embed/"));let s=e.height||"500",i=e.view||"preview";return n.includes("?")||(n+=`?view=${i}`),`
96
- <div class="${t}">
1204
+ </div>`;
1205
+ }
1206
+ function renderCodeSandboxEmbed(url, options, classes) {
1207
+ let embedUrl = url;
1208
+ if (url.includes("/s/")) {
1209
+ embedUrl = url.replace("/s/", "/embed/");
1210
+ }
1211
+ const height = options.height || "500";
1212
+ const view = options.view || "preview";
1213
+ if (!embedUrl.includes("?")) {
1214
+ embedUrl += `?view=${view}`;
1215
+ }
1216
+ return `
1217
+ <div class="${classes}">
97
1218
  <iframe
98
- src="${n}"
99
- style="width: 100%; height: ${s}px; border: 0; border-radius: 4px; overflow: hidden;"
1219
+ src="${embedUrl}"
1220
+ style="width: 100%; height: ${height}px; border: 0; border-radius: 4px; overflow: hidden;"
100
1221
  title="CodeSandbox Embed"
101
1222
  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
102
1223
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts">
103
1224
  </iframe>
104
- </div>`}function se(r,e,t){let n=`https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(r)}`,s=e.height||"450";return`
105
- <div class="${t}">
1225
+ </div>`;
1226
+ }
1227
+ function renderFigmaEmbed(url, options, classes) {
1228
+ const embedUrl = `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`;
1229
+ const height = options.height || "450";
1230
+ return `
1231
+ <div class="${classes}">
106
1232
  <iframe
107
1233
  style="border: none;"
108
1234
  width="100%"
109
- height="${s}"
110
- src="${n}"
1235
+ height="${height}"
1236
+ src="${embedUrl}"
111
1237
  allowfullscreen>
112
1238
  </iframe>
113
- </div>`}function ie(r,e,t){return`
114
- <div class="${t}">
1239
+ </div>`;
1240
+ }
1241
+ function renderTwitterEmbed(url, _options, classes) {
1242
+ return `
1243
+ <div class="${classes}">
115
1244
  <div class="p-4">
116
1245
  <div class="flex items-center gap-3 mb-3">
117
1246
  <svg class="w-6 h-6 fill-current text-blue-500" viewBox="0 0 24 24">
@@ -122,7 +1251,7 @@
122
1251
  <div class="text-sm text-muted-foreground">External Link</div>
123
1252
  </div>
124
1253
  </div>
125
- <a href="${r}" target="_blank"
1254
+ <a href="${url}" target="_blank"
126
1255
  class="inline-flex items-center gap-2 text-primary hover:text-primary/80 font-medium transition-colors">
127
1256
  View on Twitter
128
1257
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -130,19 +1259,28 @@
130
1259
  </svg>
131
1260
  </a>
132
1261
  </div>
133
- </div>`}function oe(r,e,t){let n=r.replace("https://github.com/","").split("/"),s=n[0],i=n[1];return!s||!i?v("Invalid GitHub URL",r,t):`
134
- <div class="${t}">
1262
+ </div>`;
1263
+ }
1264
+ function renderGitHubEmbed(url, _options, classes) {
1265
+ const parts = url.replace("https://github.com/", "").split("/");
1266
+ const owner = parts[0];
1267
+ const repo = parts[1];
1268
+ if (!owner || !repo) {
1269
+ return createErrorEmbed("Invalid GitHub URL", url, classes);
1270
+ }
1271
+ return `
1272
+ <div class="${classes}">
135
1273
  <div class="p-4">
136
1274
  <div class="flex items-center gap-3 mb-3">
137
1275
  <svg class="w-6 h-6 fill-current" viewBox="0 0 24 24">
138
1276
  <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
139
1277
  </svg>
140
1278
  <div>
141
- <div class="font-semibold text-foreground text-lg">${s}/${i}</div>
1279
+ <div class="font-semibold text-foreground text-lg">${owner}/${repo}</div>
142
1280
  <div class="text-sm text-muted-foreground">GitHub Repository</div>
143
1281
  </div>
144
1282
  </div>
145
- <a href="${r}" target="_blank"
1283
+ <a href="${url}" target="_blank"
146
1284
  class="inline-flex items-center gap-2 text-primary hover:text-primary/80 font-medium transition-colors">
147
1285
  View on GitHub
148
1286
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -150,8 +1288,12 @@
150
1288
  </svg>
151
1289
  </a>
152
1290
  </div>
153
- </div>`}function ae(r,e,t){let n=S(r);return`
154
- <div class="${t}">
1291
+ </div>`;
1292
+ }
1293
+ function renderGenericEmbed(url, _options, classes) {
1294
+ const domain = extractDomain(url);
1295
+ return `
1296
+ <div class="${classes}">
155
1297
  <div class="p-4">
156
1298
  <div class="flex items-center gap-3 mb-3">
157
1299
  <div class="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
@@ -161,27 +1303,243 @@
161
1303
  </div>
162
1304
  <div>
163
1305
  <div class="font-semibold text-foreground">External Link</div>
164
- <div class="text-sm text-muted-foreground">${n}</div>
1306
+ <div class="text-sm text-muted-foreground">${domain}</div>
165
1307
  </div>
166
1308
  </div>
167
- <a href="${r}" target="_blank"
1309
+ <a href="${url}" target="_blank"
168
1310
  class="inline-flex items-center gap-2 text-primary hover:text-primary/80 font-medium transition-colors break-all">
169
- ${r}
1311
+ ${url}
170
1312
  <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
171
1313
  <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"/>
172
1314
  </svg>
173
1315
  </a>
174
1316
  </div>
175
- </div>`}function v(r,e,t){return`
176
- <div class="${t}">
1317
+ </div>`;
1318
+ }
1319
+ function createErrorEmbed(error, url, classes) {
1320
+ return `
1321
+ <div class="${classes}">
177
1322
  <div class="p-4 text-destructive">
178
1323
  <div class="font-medium flex items-center gap-2">
179
1324
  <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
180
1325
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
181
1326
  </svg>
182
- ${r}
1327
+ ${error}
183
1328
  </div>
184
- <div class="text-sm text-muted-foreground mt-1 break-all">${e}</div>
1329
+ <div class="text-sm text-muted-foreground mt-1 break-all">${url}</div>
185
1330
  </div>
186
- </div>`}function le(r){let e=[/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/)([^&\n?#]+)/,/youtube\.com\/watch\?.*v=([^&\n?#]+)/];for(let t of e){let n=r.match(t);if(n)return n[1]||null}return null}function de(r){return r.match(/vimeo\.com\/(?:.*\/)?(\d+)/)?.[1]??null}var m=class{constructor(e){this.extensions=new Map;this.parser=new b(e?.parser),this.renderer=new x(e?.renderer),this.registerBuiltInExtensions(),e?.extensions&&e.extensions.forEach(t=>{this.registerExtension(t)}),this.parser.setupDefaultRulesIfEmpty()}registerExtension(e){try{return this.extensions.set(e.name,e),e.parseRules.forEach(t=>{this.parser.addRule(t)}),e.renderRules.forEach(t=>{this.renderer.addRule(t)}),{success:!0,extensionName:e.name}}catch(t){let n=t instanceof Error?t.message:String(t);return{success:!1,extensionName:e.name,error:n}}}unregisterExtension(e){if(!this.extensions.get(e))return!1;try{return this.extensions.delete(e),this.rebuildParserAndRenderer(),!0}catch{return!1}}parse(e){return this.parser.parse(e)}render(e){return this.renderer.render(e)}toHtml(e){let t=this.parse(e);return this.render(t)}getExtensions(){return Array.from(this.extensions.keys())}hasExtension(e){return this.extensions.has(e)}getWarnings(){return[...this.parser.getWarnings(),...this.renderer.getWarnings()]}getDebugInfo(){return{warnings:this.getWarnings(),parseTime:0,renderTime:0,tokenCount:0,iterationCount:0}}getPerformanceMetrics(){return{parseTime:0,renderTime:0,totalTime:0,tokenCount:0}}registerBuiltInExtensions(){this.registerExtension(L),this.registerExtension(z),this.registerExtension(O)}rebuildParserAndRenderer(){let e=this.parser.getConfig(),t=this.renderer.getConfig();this.parser=new b(e),this.renderer=new x(t);let n=Array.from(this.extensions.values());this.extensions.clear(),n.forEach(s=>{this.registerExtension(s)}),this.parser.setupDefaultRulesIfEmpty()}},B=new m;function E(r){return B.parse(r)}function M(r){return B.toHtml(r)}function h(r,e){return new m(e).toHtml(r)}function y(r,e){return new m(e).parse(r)}function T(r){return new m(r)}function C(r){return h(r,{renderer:{format:"html"}})}function A(r){return h(r,{renderer:{format:"tailwind"}})}function P(r){return y(r)}typeof window<"u"&&(window.ChangerawrMarkdown={renderCum:h,parseCum:y,createCumEngine:T,renderCumToHtml:C,renderCumToTailwind:A,renderCumToJson:P,render:h,parse:y});var ce={renderCum:h,parseCum:y,createCumEngine:T,renderCumToHtml:C,renderCumToTailwind:A,renderCumToJson:P,ChangerawrMarkdown:m,parseMarkdown:E,renderMarkdown:M};0&&(module.exports={ChangerawrMarkdown,createCumEngine,parseCum,parseMarkdown,renderCum,renderCumToHtml,renderCumToJson,renderCumToTailwind,renderMarkdown});
1331
+ </div>`;
1332
+ }
1333
+ function extractYouTubeId(url) {
1334
+ const patterns = [
1335
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/shorts\/)([^&\n?#]+)/,
1336
+ /youtube\.com\/watch\?.*v=([^&\n?#]+)/
1337
+ ];
1338
+ for (const pattern of patterns) {
1339
+ const match = url.match(pattern);
1340
+ if (match) return match[1] || null;
1341
+ }
1342
+ return null;
1343
+ }
1344
+ function extractVimeoId(url) {
1345
+ const match = url.match(/vimeo\.com\/(?:.*\/)?(\d+)/);
1346
+ return match?.[1] ?? null;
1347
+ }
1348
+
1349
+ // src/engine.ts
1350
+ var ChangerawrMarkdown = class {
1351
+ constructor(config) {
1352
+ this.extensions = /* @__PURE__ */ new Map();
1353
+ this.parser = new MarkdownParser(config?.parser);
1354
+ this.renderer = new MarkdownRenderer(config?.renderer);
1355
+ this.registerBuiltInExtensions();
1356
+ if (config?.extensions) {
1357
+ config.extensions.forEach((extension) => {
1358
+ this.registerExtension(extension);
1359
+ });
1360
+ }
1361
+ this.parser.setupDefaultRulesIfEmpty();
1362
+ }
1363
+ /**
1364
+ * Register a custom extension
1365
+ */
1366
+ registerExtension(extension) {
1367
+ try {
1368
+ this.extensions.set(extension.name, extension);
1369
+ extension.parseRules.forEach((rule) => {
1370
+ this.parser.addRule(rule);
1371
+ });
1372
+ extension.renderRules.forEach((rule) => {
1373
+ this.renderer.addRule(rule);
1374
+ });
1375
+ return {
1376
+ success: true,
1377
+ extensionName: extension.name
1378
+ };
1379
+ } catch (error) {
1380
+ const errorMessage = error instanceof Error ? error.message : String(error);
1381
+ return {
1382
+ success: false,
1383
+ extensionName: extension.name,
1384
+ error: errorMessage
1385
+ };
1386
+ }
1387
+ }
1388
+ /**
1389
+ * Unregister an extension
1390
+ */
1391
+ unregisterExtension(name) {
1392
+ const extension = this.extensions.get(name);
1393
+ if (!extension) {
1394
+ return false;
1395
+ }
1396
+ try {
1397
+ this.extensions.delete(name);
1398
+ this.rebuildParserAndRenderer();
1399
+ return true;
1400
+ } catch {
1401
+ return false;
1402
+ }
1403
+ }
1404
+ /**
1405
+ * Parse markdown content into tokens
1406
+ */
1407
+ parse(markdown2) {
1408
+ return this.parser.parse(markdown2);
1409
+ }
1410
+ /**
1411
+ * Render tokens to HTML
1412
+ */
1413
+ render(tokens) {
1414
+ return this.renderer.render(tokens);
1415
+ }
1416
+ /**
1417
+ * Parse and render markdown to HTML in one step
1418
+ */
1419
+ toHtml(markdown2) {
1420
+ const tokens = this.parse(markdown2);
1421
+ return this.render(tokens);
1422
+ }
1423
+ /**
1424
+ * Get list of registered extensions
1425
+ */
1426
+ getExtensions() {
1427
+ return Array.from(this.extensions.keys());
1428
+ }
1429
+ /**
1430
+ * Check if extension is registered
1431
+ */
1432
+ hasExtension(name) {
1433
+ return this.extensions.has(name);
1434
+ }
1435
+ /**
1436
+ * Get parser warnings
1437
+ */
1438
+ getWarnings() {
1439
+ return [...this.parser.getWarnings(), ...this.renderer.getWarnings()];
1440
+ }
1441
+ /**
1442
+ * Get debug information from last render
1443
+ */
1444
+ getDebugInfo() {
1445
+ return {
1446
+ warnings: this.getWarnings(),
1447
+ parseTime: 0,
1448
+ renderTime: 0,
1449
+ tokenCount: 0,
1450
+ iterationCount: 0
1451
+ };
1452
+ }
1453
+ /**
1454
+ * Get performance metrics for the last operation
1455
+ */
1456
+ getPerformanceMetrics() {
1457
+ return {
1458
+ parseTime: 0,
1459
+ renderTime: 0,
1460
+ totalTime: 0,
1461
+ tokenCount: 0
1462
+ };
1463
+ }
1464
+ /**
1465
+ * Register built-in extensions
1466
+ */
1467
+ registerBuiltInExtensions() {
1468
+ this.registerExtension(AlertExtension);
1469
+ this.registerExtension(ButtonExtension);
1470
+ this.registerExtension(EmbedExtension);
1471
+ }
1472
+ /**
1473
+ * Rebuild parser and renderer with current extensions
1474
+ */
1475
+ rebuildParserAndRenderer() {
1476
+ const parserConfig = this.parser.getConfig();
1477
+ const rendererConfig = this.renderer.getConfig();
1478
+ this.parser = new MarkdownParser(parserConfig);
1479
+ this.renderer = new MarkdownRenderer(rendererConfig);
1480
+ const extensionsToRegister = Array.from(this.extensions.values());
1481
+ this.extensions.clear();
1482
+ extensionsToRegister.forEach((extension) => {
1483
+ this.registerExtension(extension);
1484
+ });
1485
+ this.parser.setupDefaultRulesIfEmpty();
1486
+ }
1487
+ };
1488
+ var markdown = new ChangerawrMarkdown();
1489
+
1490
+ // src/standalone.ts
1491
+ function renderCum(markdown2, config) {
1492
+ const engine = new ChangerawrMarkdown(config);
1493
+ return engine.toHtml(markdown2);
1494
+ }
1495
+ function parseCum(markdown2, config) {
1496
+ const engine = new ChangerawrMarkdown(config);
1497
+ return engine.parse(markdown2);
1498
+ }
1499
+ function createCumEngine(config) {
1500
+ return new ChangerawrMarkdown(config);
1501
+ }
1502
+ function renderCumToHtml(markdown2) {
1503
+ return renderCum(markdown2, {
1504
+ renderer: { format: "html" }
1505
+ });
1506
+ }
1507
+ function renderCumToTailwind(markdown2) {
1508
+ return renderCum(markdown2, {
1509
+ renderer: { format: "tailwind" }
1510
+ });
1511
+ }
1512
+ function renderCumToJson(markdown2) {
1513
+ return parseCum(markdown2);
1514
+ }
1515
+ var globalAPI = {
1516
+ renderCum,
1517
+ parseCum,
1518
+ createCumEngine,
1519
+ renderCumToHtml,
1520
+ renderCumToTailwind,
1521
+ renderCumToJson,
1522
+ // Legacy aliases
1523
+ render: renderCum,
1524
+ parse: parseCum,
1525
+ // Include the main class
1526
+ ChangerawrMarkdown
1527
+ };
1528
+ var standalone_default = globalAPI;
1529
+ if (typeof window !== "undefined") {
1530
+ window.ChangerawrMarkdown = globalAPI;
1531
+ }
1532
+ if (typeof globalThis !== "undefined") {
1533
+ globalThis.ChangerawrMarkdown = globalAPI;
1534
+ }
1535
+ // Annotate the CommonJS export names for ESM import in node:
1536
+ 0 && (module.exports = {
1537
+ ChangerawrMarkdown,
1538
+ createCumEngine,
1539
+ parseCum,
1540
+ renderCum,
1541
+ renderCumToHtml,
1542
+ renderCumToJson,
1543
+ renderCumToTailwind
1544
+ });
187
1545
  //# sourceMappingURL=standalone.js.map