quikdown 1.2.2 → 1.2.7

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.
Files changed (60) hide show
  1. package/README.md +22 -27
  2. package/dist/quikdown.cjs +25 -9
  3. package/dist/quikdown.dark.css +1 -1
  4. package/dist/quikdown.esm.js +25 -9
  5. package/dist/quikdown.esm.min.js +2 -2
  6. package/dist/quikdown.esm.min.js.gz +0 -0
  7. package/dist/quikdown.esm.min.js.map +1 -1
  8. package/dist/quikdown.light.css +1 -1
  9. package/dist/quikdown.umd.js +25 -9
  10. package/dist/quikdown.umd.min.js +2 -2
  11. package/dist/quikdown.umd.min.js.gz +0 -0
  12. package/dist/quikdown.umd.min.js.map +1 -1
  13. package/dist/quikdown_ast.cjs +2 -2
  14. package/dist/quikdown_ast.esm.js +2 -2
  15. package/dist/quikdown_ast.esm.min.js +2 -2
  16. package/dist/quikdown_ast.esm.min.js.gz +0 -0
  17. package/dist/quikdown_ast.umd.js +2 -2
  18. package/dist/quikdown_ast.umd.min.js +2 -2
  19. package/dist/quikdown_ast.umd.min.js.gz +0 -0
  20. package/dist/quikdown_ast_html.cjs +3 -3
  21. package/dist/quikdown_ast_html.esm.js +3 -3
  22. package/dist/quikdown_ast_html.esm.min.js +2 -2
  23. package/dist/quikdown_ast_html.esm.min.js.gz +0 -0
  24. package/dist/quikdown_ast_html.umd.js +3 -3
  25. package/dist/quikdown_ast_html.umd.min.js +2 -2
  26. package/dist/quikdown_ast_html.umd.min.js.gz +0 -0
  27. package/dist/quikdown_bd.cjs +34 -12
  28. package/dist/quikdown_bd.esm.js +34 -12
  29. package/dist/quikdown_bd.esm.min.js +2 -2
  30. package/dist/quikdown_bd.esm.min.js.gz +0 -0
  31. package/dist/quikdown_bd.esm.min.js.map +1 -1
  32. package/dist/quikdown_bd.umd.js +34 -12
  33. package/dist/quikdown_bd.umd.min.js +2 -2
  34. package/dist/quikdown_bd.umd.min.js.gz +0 -0
  35. package/dist/quikdown_bd.umd.min.js.map +1 -1
  36. package/dist/quikdown_edit.cjs +471 -124
  37. package/dist/quikdown_edit.d.ts +15 -1
  38. package/dist/quikdown_edit.esm.js +471 -124
  39. package/dist/quikdown_edit.esm.min.js +3 -3
  40. package/dist/quikdown_edit.esm.min.js.gz +0 -0
  41. package/dist/quikdown_edit.esm.min.js.map +1 -1
  42. package/dist/quikdown_edit.umd.js +471 -124
  43. package/dist/quikdown_edit.umd.min.js +3 -3
  44. package/dist/quikdown_edit.umd.min.js.gz +0 -0
  45. package/dist/quikdown_edit.umd.min.js.map +1 -1
  46. package/dist/quikdown_json.cjs +3 -3
  47. package/dist/quikdown_json.esm.js +3 -3
  48. package/dist/quikdown_json.esm.min.js +2 -2
  49. package/dist/quikdown_json.esm.min.js.gz +0 -0
  50. package/dist/quikdown_json.umd.js +3 -3
  51. package/dist/quikdown_json.umd.min.js +2 -2
  52. package/dist/quikdown_json.umd.min.js.gz +0 -0
  53. package/dist/quikdown_yaml.cjs +3 -3
  54. package/dist/quikdown_yaml.esm.js +3 -3
  55. package/dist/quikdown_yaml.esm.min.js +2 -2
  56. package/dist/quikdown_yaml.esm.min.js.gz +0 -0
  57. package/dist/quikdown_yaml.umd.js +3 -3
  58. package/dist/quikdown_yaml.umd.min.js +2 -2
  59. package/dist/quikdown_yaml.umd.min.js.gz +0 -0
  60. package/package.json +17 -14
package/README.md CHANGED
@@ -2,41 +2,36 @@
2
2
 
3
3
  [![CI](https://github.com/deftio/quikdown/actions/workflows/ci.yml/badge.svg)](https://github.com/deftio/quikdown/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/quikdown.svg)](https://www.npmjs.com/package/quikdown)
5
- [![Coverage Status](https://codecov.io/gh/deftio/quikdown/branch/main/graph/badge.svg)](https://codecov.io/gh/deftio/quikdown)
5
+ [![Coverage](https://img.shields.io/badge/coverage-95.3%25-brightgreen)](https://github.com/deftio/quikdown)
6
6
  [![License: BSD-2-Clause](https://img.shields.io/badge/License-BSD%202--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause)
7
- [![Bundle Size](https://img.shields.io/bundlephobia/minzip/quikdown)](https://bundlephobia.com/package/quikdown)
7
+ [![Bundle Size](https://img.shields.io/badge/minified-9.3KB-green.svg)](https://bundlephobia.com/package/quikdown)
8
8
 
9
- Quikdown is a small, secure markdown parser with bidirectional conversion. Zero dependencies, XSS protection built-in, extensible via plugins for code highlighting and diagrams, and works in browser and Node.js.
9
+ A small, secure markdown parser and editor for browsers and Node.js. Three modules use only what you need.
10
10
 
11
- For small and fast projects quikdown includes built-in inline styles for a "batteries included" rendering experience, but these can be overridden with themed css (see light and dark examples).
11
+ - **quikdown.js** (9.3 KB) Markdown to HTML parser. XSS-safe, fence plugin callbacks, inline styles or CSS classes.
12
+ - **quikdown_bd.js** (14.1 KB) — Bidirectional: everything in core plus HTML to Markdown round-trip.
13
+ - **quikdown_edit.js** (80 KB) — Drop-in split-view editor with live preview, undo/redo, bidirectional editing, and lazy-loaded fence plugins for code highlighting, Mermaid, MathJax, SVG, CSV, GeoJSON, and STL.
14
+ - **quikdown_ast.js** / **quikdown_json.js** / **quikdown_yaml.js** / **quikdown_ast_html.js** — AST companion libraries for structured output.
12
15
 
13
- - **quikdown.js** (9.0KB) - Markdown to HTML Parser with theme support, XSS protection, fence callbacks
14
- - **quikdown_bd.js** (13.8KB) - Bidirectional (HTML ↔ Markdown) Parser
15
- - **quikdown_edit.js** (68.0KB) - Drop-in editor component with live preview, copy-as-rich-text, and lazy-loaded fence handlers
16
- - **quikdown_ast.js** - Markdown to AST (Abstract Syntax Tree) Parser
17
- - **quikdown_json.js** - Markdown to JSON conversion
18
- - **quikdown_yaml.js** - Markdown to YAML conversion
19
- - **quikdown_ast_html.js** - AST/JSON/YAML to HTML renderer
16
+ **[Try the Editor](https://deftio.github.io/quikdown/edit/)** | **[Examples](https://deftio.github.io/quikdown/examples/)** | **[Frameworks](https://deftio.github.io/quikdown/frameworks/)** | **[Downloads](https://deftio.github.io/quikdown/downloads/)** | **[Docs](docs/)**
20
17
 
21
- 🚀 **[Live Demo](https://deftio.github.io/quikdown/examples/quikdown-live.html)** | **[Editor Demo](https://deftio.github.io/quikdown/examples/qde/)** | **[Documentation](docs/)**
22
-
23
- 📍 **Quick Links:** [Installation](#installation) • [Quick Start](#quick-start) • [API](#api-reference) • [TypeScript](#typescript-support) • [Plugins](#fence-plugins) • [Examples](examples/)
18
+ ![quikdown editor in split mode with Mermaid diagram rendering](site/assets/editor-screenshot.png)
19
+ *quikdown_edit in split mode — markdown source on the left, live rendered preview with Mermaid diagram on the right. Toolbar shows mode switching, undo/redo, copy, and linefeed controls.*
24
20
 
25
21
  ## Features
26
22
 
27
- - 📦 **Zero dependencies** - No external libraries required
28
- - 🌐 **Universal** - Works in browsers and Node.js
29
- - 🚀 **Lightweight** - 9.0KB (core), 13.8KB (bidirectional), 68.0KB (editor)
30
- - 🔒 **Secure by default** - Built-in XSS protection with URL sanitization
31
- - 🎨 **Flexible styling** - Inline styles or CSS classes with theme support
32
- - 🔌 **Plugin system** - Extensible fence block handlers
33
- - **Fast** - Optimized regex-based parsing
34
- - 📝 **CommonMark subset** - Essential markdown features
35
- - **Task Lists** - GitHub-style checkboxes
36
- - 🔗 **Autolinks** - Automatic URL detection
37
- - 🔄 **Bidirectional** - Convert HTML back to Markdown (quikdown_bd)
38
- - 💬 **Lazy linefeeds** - Single newlines become line breaks (configurable)
39
- - 📱 **Editor component** - Drop-in markdown editor with live preview
23
+ - **Compact markdown parser** — single-pass markdown to HTML. Handles headings, lists, tables, code blocks, inline formatting, task lists, autolinks, and lazy linefeeds.
24
+ - **Bidirectional editing** — edit the rendered HTML and get markdown back. Round-trip preserves formatting, fences, and tables.
25
+ - **Drop-in editor** one `<div>`, one import. Source, split, and preview modes. Undo/redo, toolbar, copy as rich text, light/dark/auto themes.
26
+ - **Fence plugins** code (highlight.js), Mermaid diagrams, MathJax equations, inline SVG, CSV/TSV/PSV tables, GeoJSON maps, STL 3D models, raw HTML. All bidirectional.
27
+ - **XSS-safe** HTML entities escaped by default. URL sanitization blocks `javascript:`, `vbscript:`, and non-image `data:` URIs.
28
+ - **Browser and Node.js** parser and bidirectional modules work in both. Editor requires DOM.
29
+ - **Zero runtime deps** parser and bidirectional modules have no dependencies. Editor lazy-loads fence libraries (highlight.js, Mermaid, MathJax, etc.) from CDN on demand.
30
+ - **TypeScript definitions** — hand-maintained `.d.ts` files for all modules.
31
+ - **Inline styles or CSS classes** built-in light/dark themes, or bring your own CSS.
32
+ - **Copy as rich text** copies the rendered preview to clipboard with images, tables, and rendered fences. Paste into Gmail, Word, Slack, Notion.
33
+ - **Headless mode** run the editor without a toolbar. Wire `undo()`, `setTheme()`, `setMode()` to your own UI.
34
+ - **Structured output** parse markdown into AST, JSON, or YAML via companion libraries.
40
35
 
41
36
  ## Installation
42
37
 
package/dist/quikdown.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * quikdown - Lightweight Markdown Parser
3
- * @version 1.2.2
3
+ * @version 1.2.7
4
4
  * @license BSD-2-Clause
5
5
  * @copyright DeftIO 2025
6
6
  */
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  // Version will be injected at build time
23
- const quikdownVersion = '1.2.2';
23
+ const quikdownVersion = '1.2.7';
24
24
 
25
25
  // Constants for reuse
26
26
  const CLASS_PREFIX = 'quikdown-';
@@ -68,6 +68,11 @@ function createGetAttr(inline_styles, styles) {
68
68
  // Remove default text-align if we're adding a different alignment
69
69
  if (additionalStyle && additionalStyle.includes('text-align') && style && style.includes('text-align')) {
70
70
  style = style.replace(/text-align:[^;]+;?/, '').trim();
71
+ // Ensure trailing semicolon before concatenating additionalStyle.
72
+ // Both short-circuit paths of this guard (empty `style` or
73
+ // already-has-`;`) are defensive and unreachable with the
74
+ // current QUIKDOWN_STYLES values — istanbul ignore next.
75
+ /* istanbul ignore next */
71
76
  if (style && !style.endsWith(';')) style += ';';
72
77
  }
73
78
 
@@ -90,7 +95,7 @@ function quikdown(markdown, options = {}) {
90
95
  return '';
91
96
  }
92
97
 
93
- const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false } = options;
98
+ const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false, allow_unsafe_html = false } = options;
94
99
  const styles = QUIKDOWN_STYLES; // Use module-level styles
95
100
  const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once
96
101
 
@@ -99,9 +104,12 @@ function quikdown(markdown, options = {}) {
99
104
  return text.replace(/[&<>"']/g, m => ESC_MAP[m]);
100
105
  }
101
106
 
102
- // Helper to add data-qd attributes for bidirectional support
107
+ // Helper to add data-qd attributes for bidirectional support.
108
+ // The non-bidirectional branch is a trivial no-op arrow; it's exercised in
109
+ // the core bundle but never in quikdown_bd (which always sets bidirectional=true).
110
+ /* istanbul ignore next - trivial no-op fallback */
103
111
  const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
104
-
112
+
105
113
  // Sanitize URLs to prevent XSS attacks
106
114
  function sanitizeUrl(url, allowUnsafe = false) {
107
115
  /* istanbul ignore next - defensive programming, regex ensures url is never empty */
@@ -173,8 +181,11 @@ function quikdown(markdown, options = {}) {
173
181
  return placeholder;
174
182
  });
175
183
 
176
- // Now escape HTML in the rest of the content
177
- html = escapeHtml(html);
184
+ // Escape HTML in the rest of the content (skip if allow_unsafe_html is on —
185
+ // useful for trusted pipelines where the markdown contains intentional HTML)
186
+ if (!allow_unsafe_html) {
187
+ html = escapeHtml(html);
188
+ }
178
189
 
179
190
  // Phase 2: Process block elements
180
191
 
@@ -507,8 +518,13 @@ function processLists(text, getAttr, inline_styles, bidirectional) {
507
518
  const result = [];
508
519
  const listStack = []; // Track nested lists
509
520
 
510
- // Helper to escape HTML for data-qd attributes
511
- const escapeHtml = (text) => text.replace(/[&<>"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[m]);
521
+ // Helper to escape HTML for data-qd attributes. List markers (`-`, `*`,
522
+ // `+`, `1.`, etc.) never contain HTML-special chars, so the replace
523
+ // callback is defensive-only and never actually fires in practice.
524
+ const escapeHtml = (text) => text.replace(/[&<>"']/g,
525
+ /* istanbul ignore next - defensive: list markers never contain HTML specials */
526
+ m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[m]);
527
+ /* istanbul ignore next - trivial no-op fallback; not exercised via bd bundle */
512
528
  const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
513
529
 
514
530
  for (let i = 0; i < lines.length; i++) {
@@ -5,7 +5,7 @@
5
5
  * Theme with container-based scoping.
6
6
  * Usage: <div class="quikdown-dark">...content...</div>
7
7
  *
8
- * @version 1.2.2
8
+ * @version 1.2.7
9
9
  * @source tools/generateThemeCSS.js
10
10
  */
11
11
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * quikdown - Lightweight Markdown Parser
3
- * @version 1.2.2
3
+ * @version 1.2.7
4
4
  * @license BSD-2-Clause
5
5
  * @copyright DeftIO 2025
6
6
  */
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  // Version will be injected at build time
21
- const quikdownVersion = '1.2.2';
21
+ const quikdownVersion = '1.2.7';
22
22
 
23
23
  // Constants for reuse
24
24
  const CLASS_PREFIX = 'quikdown-';
@@ -66,6 +66,11 @@ function createGetAttr(inline_styles, styles) {
66
66
  // Remove default text-align if we're adding a different alignment
67
67
  if (additionalStyle && additionalStyle.includes('text-align') && style && style.includes('text-align')) {
68
68
  style = style.replace(/text-align:[^;]+;?/, '').trim();
69
+ // Ensure trailing semicolon before concatenating additionalStyle.
70
+ // Both short-circuit paths of this guard (empty `style` or
71
+ // already-has-`;`) are defensive and unreachable with the
72
+ // current QUIKDOWN_STYLES values — istanbul ignore next.
73
+ /* istanbul ignore next */
69
74
  if (style && !style.endsWith(';')) style += ';';
70
75
  }
71
76
 
@@ -88,7 +93,7 @@ function quikdown(markdown, options = {}) {
88
93
  return '';
89
94
  }
90
95
 
91
- const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false } = options;
96
+ const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false, allow_unsafe_html = false } = options;
92
97
  const styles = QUIKDOWN_STYLES; // Use module-level styles
93
98
  const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once
94
99
 
@@ -97,9 +102,12 @@ function quikdown(markdown, options = {}) {
97
102
  return text.replace(/[&<>"']/g, m => ESC_MAP[m]);
98
103
  }
99
104
 
100
- // Helper to add data-qd attributes for bidirectional support
105
+ // Helper to add data-qd attributes for bidirectional support.
106
+ // The non-bidirectional branch is a trivial no-op arrow; it's exercised in
107
+ // the core bundle but never in quikdown_bd (which always sets bidirectional=true).
108
+ /* istanbul ignore next - trivial no-op fallback */
101
109
  const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
102
-
110
+
103
111
  // Sanitize URLs to prevent XSS attacks
104
112
  function sanitizeUrl(url, allowUnsafe = false) {
105
113
  /* istanbul ignore next - defensive programming, regex ensures url is never empty */
@@ -171,8 +179,11 @@ function quikdown(markdown, options = {}) {
171
179
  return placeholder;
172
180
  });
173
181
 
174
- // Now escape HTML in the rest of the content
175
- html = escapeHtml(html);
182
+ // Escape HTML in the rest of the content (skip if allow_unsafe_html is on —
183
+ // useful for trusted pipelines where the markdown contains intentional HTML)
184
+ if (!allow_unsafe_html) {
185
+ html = escapeHtml(html);
186
+ }
176
187
 
177
188
  // Phase 2: Process block elements
178
189
 
@@ -505,8 +516,13 @@ function processLists(text, getAttr, inline_styles, bidirectional) {
505
516
  const result = [];
506
517
  const listStack = []; // Track nested lists
507
518
 
508
- // Helper to escape HTML for data-qd attributes
509
- const escapeHtml = (text) => text.replace(/[&<>"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[m]);
519
+ // Helper to escape HTML for data-qd attributes. List markers (`-`, `*`,
520
+ // `+`, `1.`, etc.) never contain HTML-special chars, so the replace
521
+ // callback is defensive-only and never actually fires in practice.
522
+ const escapeHtml = (text) => text.replace(/[&<>"']/g,
523
+ /* istanbul ignore next - defensive: list markers never contain HTML specials */
524
+ m => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[m]);
525
+ /* istanbul ignore next - trivial no-op fallback; not exercised via bd bundle */
510
526
  const dataQd = bidirectional ? (marker) => ` data-qd="${escapeHtml(marker)}"` : () => '';
511
527
 
512
528
  for (let i = 0; i < lines.length; i++) {
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * quikdown - Lightweight Markdown Parser
3
- * @version 1.2.2
3
+ * @version 1.2.7
4
4
  * @license BSD-2-Clause
5
5
  * @copyright DeftIO 2025
6
6
  */
7
- const e="quikdown-",t="§CB",n={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},r={h1:"font-size:2em;font-weight:600;margin:.67em 0;text-align:left",h2:"font-size:1.5em;font-weight:600;margin:.83em 0",h3:"font-size:1.25em;font-weight:600;margin:1em 0",h4:"font-size:1em;font-weight:600;margin:1.33em 0",h5:"font-size:.875em;font-weight:600;margin:1.67em 0",h6:"font-size:.85em;font-weight:600;margin:2em 0",pre:"background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0",code:"background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace",blockquote:"border-left:4px solid #ddd;margin-left:0;padding-left:1em",table:"border-collapse:collapse;width:100%;margin:1em 0",th:"border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left",td:"border:1px solid #ddd;padding:8px;text-align:left",hr:"border:none;border-top:1px solid #ddd;margin:1em 0",img:"max-width:100%;height:auto",a:"color:#06c;text-decoration:underline",strong:"font-weight:bold",em:"font-style:italic",del:"text-decoration:line-through",ul:"margin:.5em 0;padding-left:2em",ol:"margin:.5em 0;padding-left:2em",li:"margin:.25em 0","task-item":"list-style:none","task-checkbox":"margin-right:.5em"};function o(o,l={}){if(!o||"string"!=typeof o)return"";const{fence_plugin:c,inline_styles:s=!1,bidirectional:i=!1,lazy_linefeeds:p=!1}=l,d=function(t,n){return function(r,o=""){if(t){let e=n[r];return e||o?(o&&o.includes("text-align")&&e&&e.includes("text-align")&&(e=e.replace(/text-align:[^;]+;?/,"").trim(),e&&!e.endsWith(";")&&(e+=";")),` style="${o?e?`${e}${o}`:o:e}"`):""}{const t=` class="${e}${r}"`;return o?`${t} style="${o}"`:t}}}(s,r);function g(e){return e.replace(/[&<>"']/g,e=>n[e])}const $=i?e=>` data-qd="${g(e)}"`:()=>"";function f(e,t=!1){if(!e)return"";if(t)return e;const n=e.trim(),r=n.toLowerCase(),o=["javascript:","vbscript:","data:"];for(const e of o)if(r.startsWith(e))return"data:"===e&&r.startsWith("data:image/")?n:"#";return n}let h=o;const u=[],m=[];h=h.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm,(e,n,r,o)=>{const l=`${t}${u.length}§`,a=r?r.trim():"";return c&&c.render&&"function"==typeof c.render?u.push({lang:a,code:o.trimEnd(),custom:!0,fence:n,hasReverse:!!c.reverse}):u.push({lang:a,code:g(o.trimEnd()),custom:!1,fence:n}),l}),h=h.replace(/`([^`]+)`/g,(e,t)=>{const n=`§IC${m.length}§`;return m.push(g(t)),n}),h=g(h),h=function(e,t){const n=e.split("\n"),r=[];let o=!1,l=[];for(let e=0;e<n.length;e++){const c=n[e].trim();if(c.includes("|")&&(c.startsWith("|")||/[^\\|]/.test(c)))o||(o=!0,l=[]),l.push(c);else{if(o){const e=a(l,t);e?r.push(e):r.push(...l),o=!1,l=[]}r.push(n[e])}}if(o&&l.length>0){const e=a(l,t);e?r.push(e):r.push(...l)}return r.join("\n")}(h,d),h=h.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm,(e,t,n)=>{const r=t.length;return`<h${r}${d("h"+r)}${$(t)}>${n}</h${r}>`}),h=h.replace(/^&gt;\s+(.+)$/gm,`<blockquote${d("blockquote")}>$1</blockquote>`),h=h.replace(/<\/blockquote>\n<blockquote>/g,"\n"),h=h.replace(/^---+\s*$/gm,`<hr${d("hr")}>`),h=function(t,n,r,o){const l=t.split("\n"),a=[],c=[],s=e=>e.replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[e])),i=o?e=>` data-qd="${s(e)}"`:()=>"";for(let t=0;t<l.length;t++){const o=l[t],s=o.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);if(s){const[,t,o,l]=s,p=Math.floor(t.length/2),d=/^\d+\./.test(o),g=d?"ol":"ul";let $=l,f="";const h=l.match(/^\[([x ])\]\s+(.*)$/i);if(h&&!d){const[,t,n]=h,o="x"===t.toLowerCase();$=`<input type="checkbox"${r?' style="margin-right:.5em"':` class="${e}task-checkbox"`}${o?" checked":""} disabled> ${n}`,f=r?' style="list-style:none"':` class="${e}task-item"`}for(;c.length>p+1;){const e=c.pop();a.push(`</${e.type}>`)}if(c.length===p)c.push({type:g,level:p}),a.push(`<${g}${n(g)}>`);else if(c.length===p+1){const e=c[c.length-1];e.type!==g&&(a.push(`</${e.type}>`),c.pop(),c.push({type:g,level:p}),a.push(`<${g}${n(g)}>`))}const u=f||n("li");a.push(`<li${u}${i(o)}>${$}</li>`)}else{for(;c.length>0;){const e=c.pop();a.push(`</${e.type}>`)}a.push(o)}}for(;c.length>0;){const e=c.pop();a.push(`</${e.type}>`)}return a.join("\n")}(h,d,s,i),h=h.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(e,t,n)=>{const r=f(n,l.allow_unsafe_urls),o=i&&t?` data-qd-alt="${g(t)}"`:"",a=i?` data-qd-src="${g(n)}"`:"";return`<img${d("img")} src="${r}" alt="${t}"${o}${a}${$("!")}>`}),h=h.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>{const r=f(n,l.allow_unsafe_urls),o=/^https?:\/\//i.test(r)?' rel="noopener noreferrer"':"",a=i?` data-qd-text="${g(t)}"`:"";return`<a${d("a")} href="${r}"${o}${a}${$("[")}>${t}</a>`}),h=h.replace(/(^|\s)(https?:\/\/[^\s<]+)/g,(e,t,n)=>{const r=f(n,l.allow_unsafe_urls);return`${t}<a${d("a")} href="${r}" rel="noopener noreferrer">${n}</a>`});if([[/\*\*(.+?)\*\*/g,"strong","**"],[/__(.+?)__/g,"strong","__"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em","*"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em","_"],[/~~(.+?)~~/g,"del","~~"]].forEach(([e,t,n])=>{h=h.replace(e,`<${t}${d(t)}${$(n)}>$1</${t}>`)}),p){const e=[];let t=0;h=h.replace(/<(table|[uo]l)[^>]*>[\s\S]*?<\/\1>/g,n=>(e[t]=n,`§B${t++}§`)),h=h.replace(/\n\n+/g,"§P§").replace(/(<\/(?:h[1-6]|blockquote|pre)>)\n/g,"$1§N§").replace(/(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)\n/g,"$1§N§").replace(/\n(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)/g,"§N§$1").replace(/\n(§B\d+§)/g,"§N§$1").replace(/(§B\d+§)\n/g,"$1§N§").replace(/\n/g,`<br${d("br")}>`).replace(/§N§/g,"\n").replace(/§P§/g,"</p><p>"),e.forEach((e,t)=>h=h.replace(`§B${t}§`,e)),h="<p>"+h+"</p>"}else h=h.replace(/ {2}$/gm,`<br${d("br")}>`),h=h.replace(/\n\n+/g,(e,t)=>h.substring(0,t).match(/<\/(h[1-6]|blockquote|ul|ol|table|pre|hr)>$/)?"<p>":"</p><p>"),h="<p>"+h+"</p>";return[[/<p><\/p>/g,""],[/<p>(<h[1-6][^>]*>)/g,"$1"],[/(<\/h[1-6]>)<\/p>/g,"$1"],[/<p>(<blockquote[^>]*>)/g,"$1"],[/(<\/blockquote>)<\/p>/g,"$1"],[/<p>(<ul[^>]*>|<ol[^>]*>)/g,"$1"],[/(<\/ul>|<\/ol>)<\/p>/g,"$1"],[/<p>(<hr[^>]*>)<\/p>/g,"$1"],[/<p>(<table[^>]*>)/g,"$1"],[/(<\/table>)<\/p>/g,"$1"],[/<p>(<pre[^>]*>)/g,"$1"],[/(<\/pre>)<\/p>/g,"$1"],[new RegExp(`<p>(${t}\\d+§)</p>`,"g"),"$1"]].forEach(([e,t])=>{h=h.replace(e,t)}),h=h.replace(/(<\/(?:h[1-6]|blockquote|ul|ol|table|pre|hr)>)\n([^<])/g,"$1\n<p>$2"),u.forEach((e,n)=>{let r;if(e.custom&&c&&c.render)if(r=c.render(e.code,e.lang),void 0===r){const t=!s&&e.lang?` class="language-${e.lang}"`:"",n=s?d("code"):t,o=i&&e.lang?` data-qd-lang="${g(e.lang)}"`:"",l=i?` data-qd-fence="${g(e.fence)}"`:"";r=`<pre${d("pre")}${l}${o}><code${n}>${g(e.code)}</code></pre>`}else i&&(r=r.replace(/^<(\w+)/,`<$1 data-qd-fence="${g(e.fence)}" data-qd-lang="${g(e.lang)}" data-qd-source="${g(e.code)}"`));else{const t=!s&&e.lang?` class="language-${e.lang}"`:"",n=s?d("code"):t,o=i&&e.lang?` data-qd-lang="${g(e.lang)}"`:"",l=i?` data-qd-fence="${g(e.fence)}"`:"";r=`<pre${d("pre")}${l}${o}><code${n}>${e.code}</code></pre>`}const o=`${t}${n}§`;h=h.replace(o,r)}),m.forEach((e,t)=>{const n=`§IC${t}§`;h=h.replace(n,`<code${d("code")}${$("`")}>${e}</code>`)}),h.trim()}function l(e,t){return[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"],[/`([^`]+)`/g,"code"]].forEach(([n,r])=>{e=e.replace(n,`<${r}${t(r)}>$1</${r}>`)}),e}function a(e,t){if(e.length<2)return null;let n=-1;for(let t=1;t<e.length;t++)if(/^\|?[\s\-:|]+\|?$/.test(e[t])&&e[t].includes("-")){n=t;break}if(-1===n)return null;const r=e.slice(0,n),o=e.slice(n+1),a=e[n].trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(e=>{const t=e.trim();return t.startsWith(":")&&t.endsWith(":")?"center":t.endsWith(":")?"right":"left"});let c=`<table${t("table")}>\n`;return c+=`<thead${t("thead")}>\n`,r.forEach(e=>{c+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const r=a[n]&&"left"!==a[n]?`text-align:${a[n]}`:"",o=l(e.trim(),t);c+=`<th${t("th",r)}>${o}</th>\n`}),c+="</tr>\n"}),c+="</thead>\n",o.length>0&&(c+=`<tbody${t("tbody")}>\n`,o.forEach(e=>{c+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const r=a[n]&&"left"!==a[n]?`text-align:${a[n]}`:"",o=l(e.trim(),t);c+=`<td${t("td",r)}>${o}</td>\n`}),c+="</tr>\n"}),c+="</tbody>\n"),c+="</table>",c}o.emitStyles=function(e="quikdown-",t="light"){const n=r,o={"#f4f4f4":"#2a2a2a","#f0f0f0":"#2a2a2a","#f2f2f2":"#2a2a2a","#ddd":"#3a3a3a","#06c":"#6db3f2",_textColor:"#e0e0e0"},l={_textColor:"#333"};let a="";for(const[r,c]of Object.entries(n)){let n=c;if("dark"===t&&o){for(const[e,t]of Object.entries(o))e.startsWith("_")||(n=n.replace(new RegExp(e,"g"),t));["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(r)&&(n+=`;color:${o._textColor}`)}else if("light"===t&&l){["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(r)&&(n+=`;color:${l._textColor}`)}a+=`.${e}${r} { ${n} }\n`}return a},o.configure=function(e){return function(t){return o(t,e)}},o.version="1.2.2","undefined"!=typeof module&&module.exports&&(module.exports=o),"undefined"!=typeof window&&(window.quikdown=o);export{o as default};
7
+ const e="quikdown-",t="§CB",n={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},r={h1:"font-size:2em;font-weight:600;margin:.67em 0;text-align:left",h2:"font-size:1.5em;font-weight:600;margin:.83em 0",h3:"font-size:1.25em;font-weight:600;margin:1em 0",h4:"font-size:1em;font-weight:600;margin:1.33em 0",h5:"font-size:.875em;font-weight:600;margin:1.67em 0",h6:"font-size:.85em;font-weight:600;margin:2em 0",pre:"background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0",code:"background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace",blockquote:"border-left:4px solid #ddd;margin-left:0;padding-left:1em",table:"border-collapse:collapse;width:100%;margin:1em 0",th:"border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left",td:"border:1px solid #ddd;padding:8px;text-align:left",hr:"border:none;border-top:1px solid #ddd;margin:1em 0",img:"max-width:100%;height:auto",a:"color:#06c;text-decoration:underline",strong:"font-weight:bold",em:"font-style:italic",del:"text-decoration:line-through",ul:"margin:.5em 0;padding-left:2em",ol:"margin:.5em 0;padding-left:2em",li:"margin:.25em 0","task-item":"list-style:none","task-checkbox":"margin-right:.5em"};function o(o,l={}){if(!o||"string"!=typeof o)return"";const{fence_plugin:c,inline_styles:s=!1,bidirectional:i=!1,lazy_linefeeds:p=!1,allow_unsafe_html:d=!1}=l,g=function(t,n){return function(r,o=""){if(t){let e=n[r];return e||o?(o&&o.includes("text-align")&&e&&e.includes("text-align")&&(e=e.replace(/text-align:[^;]+;?/,"").trim(),e&&!e.endsWith(";")&&(e+=";")),` style="${o?e?`${e}${o}`:o:e}"`):""}{const t=` class="${e}${r}"`;return o?`${t} style="${o}"`:t}}}(s,r);function $(e){return e.replace(/[&<>"']/g,e=>n[e])}const f=i?e=>` data-qd="${$(e)}"`:()=>"";function h(e,t=!1){if(!e)return"";if(t)return e;const n=e.trim(),r=n.toLowerCase(),o=["javascript:","vbscript:","data:"];for(const e of o)if(r.startsWith(e))return"data:"===e&&r.startsWith("data:image/")?n:"#";return n}let u=o;const m=[],b=[];u=u.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm,(e,n,r,o)=>{const l=`${t}${m.length}§`,a=r?r.trim():"";return c&&c.render&&"function"==typeof c.render?m.push({lang:a,code:o.trimEnd(),custom:!0,fence:n,hasReverse:!!c.reverse}):m.push({lang:a,code:$(o.trimEnd()),custom:!1,fence:n}),l}),u=u.replace(/`([^`]+)`/g,(e,t)=>{const n=`§IC${b.length}§`;return b.push($(t)),n}),d||(u=$(u)),u=function(e,t){const n=e.split("\n"),r=[];let o=!1,l=[];for(let e=0;e<n.length;e++){const c=n[e].trim();if(c.includes("|")&&(c.startsWith("|")||/[^\\|]/.test(c)))o||(o=!0,l=[]),l.push(c);else{if(o){const e=a(l,t);e?r.push(e):r.push(...l),o=!1,l=[]}r.push(n[e])}}if(o&&l.length>0){const e=a(l,t);e?r.push(e):r.push(...l)}return r.join("\n")}(u,g),u=u.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm,(e,t,n)=>{const r=t.length;return`<h${r}${g("h"+r)}${f(t)}>${n}</h${r}>`}),u=u.replace(/^&gt;\s+(.+)$/gm,`<blockquote${g("blockquote")}>$1</blockquote>`),u=u.replace(/<\/blockquote>\n<blockquote>/g,"\n"),u=u.replace(/^---+\s*$/gm,`<hr${g("hr")}>`),u=function(t,n,r,o){const l=t.split("\n"),a=[],c=[],s=e=>e.replace(/[&<>"']/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[e])),i=o?e=>` data-qd="${s(e)}"`:()=>"";for(let t=0;t<l.length;t++){const o=l[t],s=o.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);if(s){const[,t,o,l]=s,p=Math.floor(t.length/2),d=/^\d+\./.test(o),g=d?"ol":"ul";let $=l,f="";const h=l.match(/^\[([x ])\]\s+(.*)$/i);if(h&&!d){const[,t,n]=h,o="x"===t.toLowerCase();$=`<input type="checkbox"${r?' style="margin-right:.5em"':` class="${e}task-checkbox"`}${o?" checked":""} disabled> ${n}`,f=r?' style="list-style:none"':` class="${e}task-item"`}for(;c.length>p+1;){const e=c.pop();a.push(`</${e.type}>`)}if(c.length===p)c.push({type:g,level:p}),a.push(`<${g}${n(g)}>`);else if(c.length===p+1){const e=c[c.length-1];e.type!==g&&(a.push(`</${e.type}>`),c.pop(),c.push({type:g,level:p}),a.push(`<${g}${n(g)}>`))}const u=f||n("li");a.push(`<li${u}${i(o)}>${$}</li>`)}else{for(;c.length>0;){const e=c.pop();a.push(`</${e.type}>`)}a.push(o)}}for(;c.length>0;){const e=c.pop();a.push(`</${e.type}>`)}return a.join("\n")}(u,g,s,i),u=u.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(e,t,n)=>{const r=h(n,l.allow_unsafe_urls),o=i&&t?` data-qd-alt="${$(t)}"`:"",a=i?` data-qd-src="${$(n)}"`:"";return`<img${g("img")} src="${r}" alt="${t}"${o}${a}${f("!")}>`}),u=u.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>{const r=h(n,l.allow_unsafe_urls),o=/^https?:\/\//i.test(r)?' rel="noopener noreferrer"':"",a=i?` data-qd-text="${$(t)}"`:"";return`<a${g("a")} href="${r}"${o}${a}${f("[")}>${t}</a>`}),u=u.replace(/(^|\s)(https?:\/\/[^\s<]+)/g,(e,t,n)=>{const r=h(n,l.allow_unsafe_urls);return`${t}<a${g("a")} href="${r}" rel="noopener noreferrer">${n}</a>`});if([[/\*\*(.+?)\*\*/g,"strong","**"],[/__(.+?)__/g,"strong","__"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em","*"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em","_"],[/~~(.+?)~~/g,"del","~~"]].forEach(([e,t,n])=>{u=u.replace(e,`<${t}${g(t)}${f(n)}>$1</${t}>`)}),p){const e=[];let t=0;u=u.replace(/<(table|[uo]l)[^>]*>[\s\S]*?<\/\1>/g,n=>(e[t]=n,`§B${t++}§`)),u=u.replace(/\n\n+/g,"§P§").replace(/(<\/(?:h[1-6]|blockquote|pre)>)\n/g,"$1§N§").replace(/(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)\n/g,"$1§N§").replace(/\n(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)/g,"§N§$1").replace(/\n(§B\d+§)/g,"§N§$1").replace(/(§B\d+§)\n/g,"$1§N§").replace(/\n/g,`<br${g("br")}>`).replace(/§N§/g,"\n").replace(/§P§/g,"</p><p>"),e.forEach((e,t)=>u=u.replace(`§B${t}§`,e)),u="<p>"+u+"</p>"}else u=u.replace(/ {2}$/gm,`<br${g("br")}>`),u=u.replace(/\n\n+/g,(e,t)=>u.substring(0,t).match(/<\/(h[1-6]|blockquote|ul|ol|table|pre|hr)>$/)?"<p>":"</p><p>"),u="<p>"+u+"</p>";return[[/<p><\/p>/g,""],[/<p>(<h[1-6][^>]*>)/g,"$1"],[/(<\/h[1-6]>)<\/p>/g,"$1"],[/<p>(<blockquote[^>]*>)/g,"$1"],[/(<\/blockquote>)<\/p>/g,"$1"],[/<p>(<ul[^>]*>|<ol[^>]*>)/g,"$1"],[/(<\/ul>|<\/ol>)<\/p>/g,"$1"],[/<p>(<hr[^>]*>)<\/p>/g,"$1"],[/<p>(<table[^>]*>)/g,"$1"],[/(<\/table>)<\/p>/g,"$1"],[/<p>(<pre[^>]*>)/g,"$1"],[/(<\/pre>)<\/p>/g,"$1"],[new RegExp(`<p>(${t}\\d+§)</p>`,"g"),"$1"]].forEach(([e,t])=>{u=u.replace(e,t)}),u=u.replace(/(<\/(?:h[1-6]|blockquote|ul|ol|table|pre|hr)>)\n([^<])/g,"$1\n<p>$2"),m.forEach((e,n)=>{let r;if(e.custom&&c&&c.render)if(r=c.render(e.code,e.lang),void 0===r){const t=!s&&e.lang?` class="language-${e.lang}"`:"",n=s?g("code"):t,o=i&&e.lang?` data-qd-lang="${$(e.lang)}"`:"",l=i?` data-qd-fence="${$(e.fence)}"`:"";r=`<pre${g("pre")}${l}${o}><code${n}>${$(e.code)}</code></pre>`}else i&&(r=r.replace(/^<(\w+)/,`<$1 data-qd-fence="${$(e.fence)}" data-qd-lang="${$(e.lang)}" data-qd-source="${$(e.code)}"`));else{const t=!s&&e.lang?` class="language-${e.lang}"`:"",n=s?g("code"):t,o=i&&e.lang?` data-qd-lang="${$(e.lang)}"`:"",l=i?` data-qd-fence="${$(e.fence)}"`:"";r=`<pre${g("pre")}${l}${o}><code${n}>${e.code}</code></pre>`}const o=`${t}${n}§`;u=u.replace(o,r)}),b.forEach((e,t)=>{const n=`§IC${t}§`;u=u.replace(n,`<code${g("code")}${f("`")}>${e}</code>`)}),u.trim()}function l(e,t){return[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"],[/`([^`]+)`/g,"code"]].forEach(([n,r])=>{e=e.replace(n,`<${r}${t(r)}>$1</${r}>`)}),e}function a(e,t){if(e.length<2)return null;let n=-1;for(let t=1;t<e.length;t++)if(/^\|?[\s\-:|]+\|?$/.test(e[t])&&e[t].includes("-")){n=t;break}if(-1===n)return null;const r=e.slice(0,n),o=e.slice(n+1),a=e[n].trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(e=>{const t=e.trim();return t.startsWith(":")&&t.endsWith(":")?"center":t.endsWith(":")?"right":"left"});let c=`<table${t("table")}>\n`;return c+=`<thead${t("thead")}>\n`,r.forEach(e=>{c+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const r=a[n]&&"left"!==a[n]?`text-align:${a[n]}`:"",o=l(e.trim(),t);c+=`<th${t("th",r)}>${o}</th>\n`}),c+="</tr>\n"}),c+="</thead>\n",o.length>0&&(c+=`<tbody${t("tbody")}>\n`,o.forEach(e=>{c+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const r=a[n]&&"left"!==a[n]?`text-align:${a[n]}`:"",o=l(e.trim(),t);c+=`<td${t("td",r)}>${o}</td>\n`}),c+="</tr>\n"}),c+="</tbody>\n"),c+="</table>",c}o.emitStyles=function(e="quikdown-",t="light"){const n=r,o={"#f4f4f4":"#2a2a2a","#f0f0f0":"#2a2a2a","#f2f2f2":"#2a2a2a","#ddd":"#3a3a3a","#06c":"#6db3f2",_textColor:"#e0e0e0"},l={_textColor:"#333"};let a="";for(const[r,c]of Object.entries(n)){let n=c;if("dark"===t&&o){for(const[e,t]of Object.entries(o))e.startsWith("_")||(n=n.replace(new RegExp(e,"g"),t));["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(r)&&(n+=`;color:${o._textColor}`)}else if("light"===t&&l){["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(r)&&(n+=`;color:${l._textColor}`)}a+=`.${e}${r} { ${n} }\n`}return a},o.configure=function(e){return function(t){return o(t,e)}},o.version="1.2.7","undefined"!=typeof module&&module.exports&&(module.exports=o),"undefined"!=typeof window&&(window.quikdown=o);export{o as default};
8
8
  //# sourceMappingURL=quikdown.esm.min.js.map
Binary file
@@ -1 +1 @@
1
- {"version":3,"file":"quikdown.esm.min.js","sources":["../src/quikdown.js"],"sourcesContent":["/**\n * quikdown - A minimal markdown parser optimized for chat/LLM output\n * Supports tables, code blocks, lists, and common formatting\n * @param {string} markdown - The markdown source text\n * @param {Object} options - Optional configuration object\n * @param {Function} options.fence_plugin - Custom renderer for fenced code blocks\n * (content, fence_string) => html string\n * @param {boolean} options.inline_styles - If true, uses inline styles instead of classes\n * @param {boolean} options.bidirectional - If true, adds data-qd attributes for source tracking\n * @param {boolean} options.lazy_linefeeds - If true, single newlines become <br> tags\n * @returns {string} - The rendered HTML\n */\n\n// Version will be injected at build time \nconst quikdownVersion = '__QUIKDOWN_VERSION__';\n\n// Constants for reuse\nconst CLASS_PREFIX = 'quikdown-';\nconst PLACEHOLDER_CB = '§CB';\nconst PLACEHOLDER_IC = '§IC';\n\n// Escape map at module level\nconst ESC_MAP = {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'};\n\n// Single source of truth for all style definitions - optimized\nconst QUIKDOWN_STYLES = {\n h1: 'font-size:2em;font-weight:600;margin:.67em 0;text-align:left',\n h2: 'font-size:1.5em;font-weight:600;margin:.83em 0',\n h3: 'font-size:1.25em;font-weight:600;margin:1em 0',\n h4: 'font-size:1em;font-weight:600;margin:1.33em 0',\n h5: 'font-size:.875em;font-weight:600;margin:1.67em 0',\n h6: 'font-size:.85em;font-weight:600;margin:2em 0',\n pre: 'background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0',\n code: 'background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace',\n blockquote: 'border-left:4px solid #ddd;margin-left:0;padding-left:1em',\n table: 'border-collapse:collapse;width:100%;margin:1em 0',\n th: 'border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left',\n td: 'border:1px solid #ddd;padding:8px;text-align:left',\n hr: 'border:none;border-top:1px solid #ddd;margin:1em 0',\n img: 'max-width:100%;height:auto',\n a: 'color:#06c;text-decoration:underline',\n strong: 'font-weight:bold',\n em: 'font-style:italic',\n del: 'text-decoration:line-through',\n ul: 'margin:.5em 0;padding-left:2em',\n ol: 'margin:.5em 0;padding-left:2em',\n li: 'margin:.25em 0',\n // Task list specific styles\n 'task-item': 'list-style:none',\n 'task-checkbox': 'margin-right:.5em'\n};\n\n// Factory function to create getAttr for a given context\nfunction createGetAttr(inline_styles, styles) {\n return function(tag, additionalStyle = '') {\n if (inline_styles) {\n let style = styles[tag];\n if (!style && !additionalStyle) return '';\n \n // Remove default text-align if we're adding a different alignment\n if (additionalStyle && additionalStyle.includes('text-align') && style && style.includes('text-align')) {\n style = style.replace(/text-align:[^;]+;?/, '').trim();\n if (style && !style.endsWith(';')) style += ';';\n }\n \n /* istanbul ignore next - defensive: additionalStyle without style doesn't occur with current tags */\n const fullStyle = additionalStyle ? (style ? `${style}${additionalStyle}` : additionalStyle) : style;\n return ` style=\"${fullStyle}\"`;\n } else {\n const classAttr = ` class=\"${CLASS_PREFIX}${tag}\"`;\n // Apply inline styles for alignment even when using CSS classes\n if (additionalStyle) {\n return `${classAttr} style=\"${additionalStyle}\"`;\n }\n return classAttr;\n }\n };\n}\n\nfunction quikdown(markdown, options = {}) {\n if (!markdown || typeof markdown !== 'string') {\n return '';\n }\n \n const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false } = options;\n const styles = QUIKDOWN_STYLES; // Use module-level styles\n const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once\n\n // Escape HTML entities to prevent XSS\n function escapeHtml(text) {\n return text.replace(/[&<>\"']/g, m => ESC_MAP[m]);\n }\n \n // Helper to add data-qd attributes for bidirectional support\n const dataQd = bidirectional ? (marker) => ` data-qd=\"${escapeHtml(marker)}\"` : () => '';\n \n // Sanitize URLs to prevent XSS attacks\n function sanitizeUrl(url, allowUnsafe = false) {\n /* istanbul ignore next - defensive programming, regex ensures url is never empty */\n if (!url) return '';\n \n // If unsafe URLs are explicitly allowed, return as-is\n if (allowUnsafe) return url;\n \n const trimmedUrl = url.trim();\n const lowerUrl = trimmedUrl.toLowerCase();\n \n // Block dangerous protocols\n const dangerousProtocols = ['javascript:', 'vbscript:', 'data:'];\n \n for (const protocol of dangerousProtocols) {\n if (lowerUrl.startsWith(protocol)) {\n // Exception: Allow data:image/* for images\n if (protocol === 'data:' && lowerUrl.startsWith('data:image/')) {\n return trimmedUrl;\n }\n // Return safe empty link for dangerous protocols\n return '#';\n }\n }\n \n return trimmedUrl;\n }\n\n // Process the markdown in phases\n let html = markdown;\n \n // Phase 1: Extract and protect code blocks and inline code\n const codeBlocks = [];\n const inlineCodes = [];\n \n // Extract fenced code blocks first (supports both ``` and ~~~)\n // Match paired fences - ``` with ``` and ~~~ with ~~~\n // Fence must be at start of line\n html = html.replace(/^(```|~~~)([^\\n]*)\\n([\\s\\S]*?)^\\1$/gm, (match, fence, lang, code) => {\n const placeholder = `${PLACEHOLDER_CB}${codeBlocks.length}§`;\n \n // Trim the language specification\n const langTrimmed = lang ? lang.trim() : '';\n \n // If custom fence plugin is provided, use it (v1.1.0: object format required)\n if (fence_plugin && fence_plugin.render && typeof fence_plugin.render === 'function') {\n codeBlocks.push({\n lang: langTrimmed,\n code: code.trimEnd(),\n custom: true,\n fence: fence,\n hasReverse: !!fence_plugin.reverse\n });\n } else {\n codeBlocks.push({\n lang: langTrimmed,\n code: escapeHtml(code.trimEnd()),\n custom: false,\n fence: fence\n });\n }\n return placeholder;\n });\n \n // Extract inline code\n html = html.replace(/`([^`]+)`/g, (match, code) => {\n const placeholder = `${PLACEHOLDER_IC}${inlineCodes.length}§`;\n inlineCodes.push(escapeHtml(code));\n return placeholder;\n });\n \n // Now escape HTML in the rest of the content\n html = escapeHtml(html);\n \n // Phase 2: Process block elements\n \n // Process tables\n html = processTable(html, getAttr);\n \n // Process headings (supports optional trailing #'s)\n html = html.replace(/^(#{1,6})\\s+(.+?)\\s*#*$/gm, (match, hashes, content) => {\n const level = hashes.length;\n return `<h${level}${getAttr('h' + level)}${dataQd(hashes)}>${content}</h${level}>`;\n });\n \n // Process blockquotes (must handle escaped > since we already escaped HTML)\n html = html.replace(/^&gt;\\s+(.+)$/gm, `<blockquote${getAttr('blockquote')}>$1</blockquote>`);\n // Merge consecutive blockquotes\n html = html.replace(/<\\/blockquote>\\n<blockquote>/g, '\\n');\n \n // Process horizontal rules (allow trailing spaces)\n html = html.replace(/^---+\\s*$/gm, `<hr${getAttr('hr')}>`);\n \n // Process lists\n html = processLists(html, getAttr, inline_styles, bidirectional);\n \n // Phase 3: Process inline elements\n \n // Images (must come before links, with URL sanitization)\n html = html.replace(/!\\[([^\\]]*)\\]\\(([^)]+)\\)/g, (match, alt, src) => {\n const sanitizedSrc = sanitizeUrl(src, options.allow_unsafe_urls);\n const altAttr = bidirectional && alt ? ` data-qd-alt=\"${escapeHtml(alt)}\"` : '';\n const srcAttr = bidirectional ? ` data-qd-src=\"${escapeHtml(src)}\"` : '';\n return `<img${getAttr('img')} src=\"${sanitizedSrc}\" alt=\"${alt}\"${altAttr}${srcAttr}${dataQd('!')}>`;\n });\n \n // Links (with URL sanitization)\n html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (match, text, href) => {\n // Sanitize URL to prevent XSS\n const sanitizedHref = sanitizeUrl(href, options.allow_unsafe_urls);\n const isExternal = /^https?:\\/\\//i.test(sanitizedHref);\n const rel = isExternal ? ' rel=\"noopener noreferrer\"' : '';\n const textAttr = bidirectional ? ` data-qd-text=\"${escapeHtml(text)}\"` : '';\n return `<a${getAttr('a')} href=\"${sanitizedHref}\"${rel}${textAttr}${dataQd('[')}>${text}</a>`;\n });\n \n // Autolinks - convert bare URLs to clickable links\n html = html.replace(/(^|\\s)(https?:\\/\\/[^\\s<]+)/g, (match, prefix, url) => {\n const sanitizedUrl = sanitizeUrl(url, options.allow_unsafe_urls);\n return `${prefix}<a${getAttr('a')} href=\"${sanitizedUrl}\" rel=\"noopener noreferrer\">${url}</a>`;\n });\n \n // Process inline formatting (bold, italic, strikethrough)\n const inlinePatterns = [\n [/\\*\\*(.+?)\\*\\*/g, 'strong', '**'],\n [/__(.+?)__/g, 'strong', '__'],\n [/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, 'em', '*'],\n [/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em', '_'],\n [/~~(.+?)~~/g, 'del', '~~']\n ];\n \n inlinePatterns.forEach(([pattern, tag, marker]) => {\n html = html.replace(pattern, `<${tag}${getAttr(tag)}${dataQd(marker)}>$1</${tag}>`);\n });\n \n // Line breaks\n if (lazy_linefeeds) {\n // Lazy linefeeds: single newline becomes <br> (except between paragraphs and after/before block elements)\n const blocks = [];\n let bi = 0;\n \n // Protect tables and lists \n html = html.replace(/<(table|[uo]l)[^>]*>[\\s\\S]*?<\\/\\1>/g, m => {\n blocks[bi] = m;\n return `§B${bi++}§`;\n });\n \n // Handle paragraphs and block elements\n html = html.replace(/\\n\\n+/g, '§P§')\n // After block elements\n .replace(/(<\\/(?:h[1-6]|blockquote|pre)>)\\n/g, '$1§N§')\n .replace(/(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)\\n/g, '$1§N§')\n // Before block elements \n .replace(/\\n(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)/g, '§N§$1')\n .replace(/\\n(§B\\d+§)/g, '§N§$1')\n .replace(/(§B\\d+§)\\n/g, '$1§N§')\n // Convert remaining newlines\n .replace(/\\n/g, `<br${getAttr('br')}>`)\n // Restore\n .replace(/§N§/g, '\\n')\n .replace(/§P§/g, '</p><p>');\n \n // Restore protected blocks\n blocks.forEach((b, i) => html = html.replace(`§B${i}§`, b));\n \n html = '<p>' + html + '</p>';\n } else {\n // Standard: two spaces at end of line for line breaks\n html = html.replace(/ {2}$/gm, `<br${getAttr('br')}>`);\n \n // Paragraphs (double newlines)\n // Don't add </p> after block elements (they're not in paragraphs)\n html = html.replace(/\\n\\n+/g, (match, offset) => {\n // Check if we're after a block element closing tag\n const before = html.substring(0, offset);\n if (before.match(/<\\/(h[1-6]|blockquote|ul|ol|table|pre|hr)>$/)) {\n return '<p>'; // Just open a new paragraph\n }\n return '</p><p>'; // Normal paragraph break\n });\n html = '<p>' + html + '</p>';\n }\n \n // Clean up empty paragraphs and unwrap block elements\n const cleanupPatterns = [\n [/<p><\\/p>/g, ''],\n [/<p>(<h[1-6][^>]*>)/g, '$1'],\n [/(<\\/h[1-6]>)<\\/p>/g, '$1'],\n [/<p>(<blockquote[^>]*>)/g, '$1'],\n [/(<\\/blockquote>)<\\/p>/g, '$1'],\n [/<p>(<ul[^>]*>|<ol[^>]*>)/g, '$1'],\n [/(<\\/ul>|<\\/ol>)<\\/p>/g, '$1'],\n [/<p>(<hr[^>]*>)<\\/p>/g, '$1'],\n [/<p>(<table[^>]*>)/g, '$1'],\n [/(<\\/table>)<\\/p>/g, '$1'],\n [/<p>(<pre[^>]*>)/g, '$1'],\n [/(<\\/pre>)<\\/p>/g, '$1'],\n [new RegExp(`<p>(${PLACEHOLDER_CB}\\\\d+§)</p>`, 'g'), '$1']\n ];\n \n cleanupPatterns.forEach(([pattern, replacement]) => {\n html = html.replace(pattern, replacement);\n });\n \n // Fix orphaned closing </p> tags after block elements\n // When a paragraph follows a block element, ensure it has opening <p>\n html = html.replace(/(<\\/(?:h[1-6]|blockquote|ul|ol|table|pre|hr)>)\\n([^<])/g, '$1\\n<p>$2');\n \n // Phase 4: Restore code blocks and inline code\n \n // Restore code blocks\n codeBlocks.forEach((block, i) => {\n let replacement;\n \n if (block.custom && fence_plugin && fence_plugin.render) {\n // Use custom fence plugin (v1.1.0: object format with render function)\n replacement = fence_plugin.render(block.code, block.lang);\n \n // If plugin returns undefined, fall back to default rendering\n if (replacement === undefined) {\n const langClass = !inline_styles && block.lang ? ` class=\"language-${block.lang}\"` : '';\n const codeAttr = inline_styles ? getAttr('code') : langClass;\n const langAttr = bidirectional && block.lang ? ` data-qd-lang=\"${escapeHtml(block.lang)}\"` : '';\n const fenceAttr = bidirectional ? ` data-qd-fence=\"${escapeHtml(block.fence)}\"` : '';\n replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${escapeHtml(block.code)}</code></pre>`;\n } else if (bidirectional) {\n // If bidirectional and plugin provided HTML, add data attributes for roundtrip\n replacement = replacement.replace(/^<(\\w+)/, \n `<$1 data-qd-fence=\"${escapeHtml(block.fence)}\" data-qd-lang=\"${escapeHtml(block.lang)}\" data-qd-source=\"${escapeHtml(block.code)}\"`);\n }\n } else {\n // Default rendering\n const langClass = !inline_styles && block.lang ? ` class=\"language-${block.lang}\"` : '';\n const codeAttr = inline_styles ? getAttr('code') : langClass;\n const langAttr = bidirectional && block.lang ? ` data-qd-lang=\"${escapeHtml(block.lang)}\"` : '';\n const fenceAttr = bidirectional ? ` data-qd-fence=\"${escapeHtml(block.fence)}\"` : '';\n replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${block.code}</code></pre>`;\n }\n \n const placeholder = `${PLACEHOLDER_CB}${i}§`;\n html = html.replace(placeholder, replacement);\n });\n \n // Restore inline code\n inlineCodes.forEach((code, i) => {\n const placeholder = `${PLACEHOLDER_IC}${i}§`;\n html = html.replace(placeholder, `<code${getAttr('code')}${dataQd('`')}>${code}</code>`);\n });\n \n return html.trim();\n}\n\n/**\n * Process inline markdown formatting\n */\nfunction processInlineMarkdown(text, getAttr) {\n \n // Process inline formatting patterns\n const patterns = [\n [/\\*\\*(.+?)\\*\\*/g, 'strong'],\n [/__(.+?)__/g, 'strong'],\n [/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, 'em'],\n [/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],\n [/~~(.+?)~~/g, 'del'],\n [/`([^`]+)`/g, 'code']\n ];\n \n patterns.forEach(([pattern, tag]) => {\n text = text.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);\n });\n \n return text;\n}\n\n/**\n * Process markdown tables\n */\nfunction processTable(text, getAttr) {\n const lines = text.split('\\n');\n const result = [];\n let inTable = false;\n let tableLines = [];\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n \n // Check if this line looks like a table row (with or without trailing |)\n if (line.includes('|') && (line.startsWith('|') || /[^\\\\|]/.test(line))) {\n if (!inTable) {\n inTable = true;\n tableLines = [];\n }\n tableLines.push(line);\n } else {\n // Not a table line\n if (inTable) {\n // Process the accumulated table\n const tableHtml = buildTable(tableLines, getAttr);\n if (tableHtml) {\n result.push(tableHtml);\n } else {\n // Not a valid table, restore original lines\n result.push(...tableLines);\n }\n inTable = false;\n tableLines = [];\n }\n result.push(lines[i]);\n }\n }\n \n // Handle table at end of text\n if (inTable && tableLines.length > 0) {\n const tableHtml = buildTable(tableLines, getAttr);\n if (tableHtml) {\n result.push(tableHtml);\n } else {\n result.push(...tableLines);\n }\n }\n \n return result.join('\\n');\n}\n\n/**\n * Build an HTML table from markdown table lines\n */\nfunction buildTable(lines, getAttr) {\n \n if (lines.length < 2) return null;\n \n // Check for separator line (second line should be the separator)\n let separatorIndex = -1;\n for (let i = 1; i < lines.length; i++) {\n // Support separator with or without leading/trailing pipes\n if (/^\\|?[\\s\\-:|]+\\|?$/.test(lines[i]) && lines[i].includes('-')) {\n separatorIndex = i;\n break;\n }\n }\n \n if (separatorIndex === -1) return null;\n \n const headerLines = lines.slice(0, separatorIndex);\n const bodyLines = lines.slice(separatorIndex + 1);\n \n // Parse alignment from separator\n const separator = lines[separatorIndex];\n // Handle pipes at start/end or not\n const separatorCells = separator.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|');\n const alignments = separatorCells.map(cell => {\n const trimmed = cell.trim();\n if (trimmed.startsWith(':') && trimmed.endsWith(':')) return 'center';\n if (trimmed.endsWith(':')) return 'right';\n return 'left';\n });\n \n let html = `<table${getAttr('table')}>\\n`;\n \n // Build header\n // Note: headerLines will always have length > 0 since separatorIndex starts from 1\n html += `<thead${getAttr('thead')}>\\n`;\n headerLines.forEach(line => {\n html += `<tr${getAttr('tr')}>\\n`;\n // Handle pipes at start/end or not\n const cells = line.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|');\n cells.forEach((cell, i) => {\n const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';\n const processedCell = processInlineMarkdown(cell.trim(), getAttr);\n html += `<th${getAttr('th', alignStyle)}>${processedCell}</th>\\n`;\n });\n html += '</tr>\\n';\n });\n html += '</thead>\\n';\n \n // Build body\n if (bodyLines.length > 0) {\n html += `<tbody${getAttr('tbody')}>\\n`;\n bodyLines.forEach(line => {\n html += `<tr${getAttr('tr')}>\\n`;\n // Handle pipes at start/end or not\n const cells = line.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|');\n cells.forEach((cell, i) => {\n const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';\n const processedCell = processInlineMarkdown(cell.trim(), getAttr);\n html += `<td${getAttr('td', alignStyle)}>${processedCell}</td>\\n`;\n });\n html += '</tr>\\n';\n });\n html += '</tbody>\\n';\n }\n \n html += '</table>';\n return html;\n}\n\n/**\n * Process markdown lists (ordered and unordered)\n */\nfunction processLists(text, getAttr, inline_styles, bidirectional) {\n \n const lines = text.split('\\n');\n const result = [];\n const listStack = []; // Track nested lists\n \n // Helper to escape HTML for data-qd attributes\n const escapeHtml = (text) => text.replace(/[&<>\"']/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'})[m]);\n const dataQd = bidirectional ? (marker) => ` data-qd=\"${escapeHtml(marker)}\"` : () => '';\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const match = line.match(/^(\\s*)([*\\-+]|\\d+\\.)\\s+(.+)$/);\n \n if (match) {\n const [, indent, marker, content] = match;\n const level = Math.floor(indent.length / 2);\n const isOrdered = /^\\d+\\./.test(marker);\n const listType = isOrdered ? 'ol' : 'ul';\n \n // Check for task list items\n let listItemContent = content;\n let taskListClass = '';\n const taskMatch = content.match(/^\\[([x ])\\]\\s+(.*)$/i);\n if (taskMatch && !isOrdered) {\n const [, checked, taskContent] = taskMatch;\n const isChecked = checked.toLowerCase() === 'x';\n const checkboxAttr = inline_styles \n ? ' style=\"margin-right:.5em\"' \n : ` class=\"${CLASS_PREFIX}task-checkbox\"`;\n listItemContent = `<input type=\"checkbox\"${checkboxAttr}${isChecked ? ' checked' : ''} disabled> ${taskContent}`;\n taskListClass = inline_styles ? ' style=\"list-style:none\"' : ` class=\"${CLASS_PREFIX}task-item\"`;\n }\n \n // Close deeper levels\n while (listStack.length > level + 1) {\n const list = listStack.pop();\n result.push(`</${list.type}>`);\n }\n \n // Open new level if needed\n if (listStack.length === level) {\n // Need to open a new list\n listStack.push({ type: listType, level });\n result.push(`<${listType}${getAttr(listType)}>`);\n } else if (listStack.length === level + 1) {\n // Check if we need to switch list type\n const currentList = listStack[listStack.length - 1];\n if (currentList.type !== listType) {\n result.push(`</${currentList.type}>`);\n listStack.pop();\n listStack.push({ type: listType, level });\n result.push(`<${listType}${getAttr(listType)}>`);\n }\n }\n \n const liAttr = taskListClass || getAttr('li');\n result.push(`<li${liAttr}${dataQd(marker)}>${listItemContent}</li>`);\n } else {\n // Not a list item, close all lists\n while (listStack.length > 0) {\n const list = listStack.pop();\n result.push(`</${list.type}>`);\n }\n result.push(line);\n }\n }\n \n // Close any remaining lists\n while (listStack.length > 0) {\n const list = listStack.pop();\n result.push(`</${list.type}>`);\n }\n \n return result.join('\\n');\n}\n\n/**\n * Emit CSS styles for quikdown elements\n * @param {string} prefix - Optional class prefix (default: 'quikdown-')\n * @param {string} theme - Optional theme: 'light' (default) or 'dark'\n * @returns {string} CSS string with quikdown styles\n */\nquikdown.emitStyles = function(prefix = 'quikdown-', theme = 'light') {\n const styles = QUIKDOWN_STYLES;\n \n // Define theme color overrides\n const themeOverrides = {\n dark: {\n '#f4f4f4': '#2a2a2a', // pre background\n '#f0f0f0': '#2a2a2a', // code background\n '#f2f2f2': '#2a2a2a', // th background\n '#ddd': '#3a3a3a', // borders\n '#06c': '#6db3f2', // links\n _textColor: '#e0e0e0'\n },\n light: {\n _textColor: '#333' // Explicit text color for light theme\n }\n };\n \n let css = '';\n for (const [tag, style] of Object.entries(styles)) {\n let themedStyle = style;\n \n // Apply theme overrides if dark theme\n if (theme === 'dark' && themeOverrides.dark) {\n // Replace colors\n for (const [oldColor, newColor] of Object.entries(themeOverrides.dark)) {\n if (!oldColor.startsWith('_')) {\n themedStyle = themedStyle.replace(new RegExp(oldColor, 'g'), newColor);\n }\n }\n \n // Add text color for certain elements in dark theme\n const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];\n if (needsTextColor.includes(tag)) {\n themedStyle += `;color:${themeOverrides.dark._textColor}`;\n }\n } else if (theme === 'light' && themeOverrides.light) {\n // Add explicit text color for light theme elements too\n const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];\n if (needsTextColor.includes(tag)) {\n themedStyle += `;color:${themeOverrides.light._textColor}`;\n }\n }\n \n css += `.${prefix}${tag} { ${themedStyle} }\\n`;\n }\n \n return css;\n};\n\n/**\n * Configure quikdown with options and return a function\n * @param {Object} options - Configuration options\n * @returns {Function} Configured quikdown function\n */\nquikdown.configure = function(options) {\n return function(markdown) {\n return quikdown(markdown, options);\n };\n};\n\n/**\n * Version information\n */\nquikdown.version = quikdownVersion;\n\n// Export for both CommonJS and ES6\n/* istanbul ignore next */\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = quikdown;\n}\n\n// For browser global\n/* istanbul ignore next */\nif (typeof window !== 'undefined') {\n window.quikdown = quikdown;\n}\n\nexport default quikdown;"],"names":["CLASS_PREFIX","PLACEHOLDER_CB","ESC_MAP","QUIKDOWN_STYLES","h1","h2","h3","h4","h5","h6","pre","code","blockquote","table","th","td","hr","img","a","strong","em","del","ul","ol","li","quikdown","markdown","options","fence_plugin","inline_styles","bidirectional","lazy_linefeeds","getAttr","styles","tag","additionalStyle","style","includes","replace","trim","endsWith","classAttr","createGetAttr","escapeHtml","text","m","dataQd","marker","sanitizeUrl","url","allowUnsafe","trimmedUrl","lowerUrl","toLowerCase","dangerousProtocols","protocol","startsWith","html","codeBlocks","inlineCodes","match","fence","lang","placeholder","length","langTrimmed","render","push","trimEnd","custom","hasReverse","reverse","lines","split","result","inTable","tableLines","i","line","test","tableHtml","buildTable","join","processTable","hashes","content","level","listStack","indent","Math","floor","isOrdered","listType","listItemContent","taskListClass","taskMatch","checked","taskContent","isChecked","list","pop","type","currentList","liAttr","processLists","alt","src","sanitizedSrc","allow_unsafe_urls","altAttr","srcAttr","href","sanitizedHref","rel","textAttr","prefix","sanitizedUrl","forEach","pattern","blocks","bi","b","offset","substring","RegExp","replacement","block","undefined","langClass","codeAttr","langAttr","fenceAttr","processInlineMarkdown","separatorIndex","headerLines","slice","bodyLines","alignments","map","cell","trimmed","alignStyle","processedCell","emitStyles","theme","themeOverrides","_textColor","css","Object","entries","themedStyle","oldColor","newColor","configure","version","module","exports","window"],"mappings":"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"}
1
+ {"version":3,"file":"quikdown.esm.min.js","sources":["../src/quikdown.js"],"sourcesContent":["/**\n * quikdown - A minimal markdown parser optimized for chat/LLM output\n * Supports tables, code blocks, lists, and common formatting\n * @param {string} markdown - The markdown source text\n * @param {Object} options - Optional configuration object\n * @param {Function} options.fence_plugin - Custom renderer for fenced code blocks\n * (content, fence_string) => html string\n * @param {boolean} options.inline_styles - If true, uses inline styles instead of classes\n * @param {boolean} options.bidirectional - If true, adds data-qd attributes for source tracking\n * @param {boolean} options.lazy_linefeeds - If true, single newlines become <br> tags\n * @returns {string} - The rendered HTML\n */\n\n// Version will be injected at build time \nconst quikdownVersion = '__QUIKDOWN_VERSION__';\n\n// Constants for reuse\nconst CLASS_PREFIX = 'quikdown-';\nconst PLACEHOLDER_CB = '§CB';\nconst PLACEHOLDER_IC = '§IC';\n\n// Escape map at module level\nconst ESC_MAP = {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'};\n\n// Single source of truth for all style definitions - optimized\nconst QUIKDOWN_STYLES = {\n h1: 'font-size:2em;font-weight:600;margin:.67em 0;text-align:left',\n h2: 'font-size:1.5em;font-weight:600;margin:.83em 0',\n h3: 'font-size:1.25em;font-weight:600;margin:1em 0',\n h4: 'font-size:1em;font-weight:600;margin:1.33em 0',\n h5: 'font-size:.875em;font-weight:600;margin:1.67em 0',\n h6: 'font-size:.85em;font-weight:600;margin:2em 0',\n pre: 'background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0',\n code: 'background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace',\n blockquote: 'border-left:4px solid #ddd;margin-left:0;padding-left:1em',\n table: 'border-collapse:collapse;width:100%;margin:1em 0',\n th: 'border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left',\n td: 'border:1px solid #ddd;padding:8px;text-align:left',\n hr: 'border:none;border-top:1px solid #ddd;margin:1em 0',\n img: 'max-width:100%;height:auto',\n a: 'color:#06c;text-decoration:underline',\n strong: 'font-weight:bold',\n em: 'font-style:italic',\n del: 'text-decoration:line-through',\n ul: 'margin:.5em 0;padding-left:2em',\n ol: 'margin:.5em 0;padding-left:2em',\n li: 'margin:.25em 0',\n // Task list specific styles\n 'task-item': 'list-style:none',\n 'task-checkbox': 'margin-right:.5em'\n};\n\n// Factory function to create getAttr for a given context\nfunction createGetAttr(inline_styles, styles) {\n return function(tag, additionalStyle = '') {\n if (inline_styles) {\n let style = styles[tag];\n if (!style && !additionalStyle) return '';\n \n // Remove default text-align if we're adding a different alignment\n if (additionalStyle && additionalStyle.includes('text-align') && style && style.includes('text-align')) {\n style = style.replace(/text-align:[^;]+;?/, '').trim();\n // Ensure trailing semicolon before concatenating additionalStyle.\n // Both short-circuit paths of this guard (empty `style` or\n // already-has-`;`) are defensive and unreachable with the\n // current QUIKDOWN_STYLES values — istanbul ignore next.\n /* istanbul ignore next */\n if (style && !style.endsWith(';')) style += ';';\n }\n \n /* istanbul ignore next - defensive: additionalStyle without style doesn't occur with current tags */\n const fullStyle = additionalStyle ? (style ? `${style}${additionalStyle}` : additionalStyle) : style;\n return ` style=\"${fullStyle}\"`;\n } else {\n const classAttr = ` class=\"${CLASS_PREFIX}${tag}\"`;\n // Apply inline styles for alignment even when using CSS classes\n if (additionalStyle) {\n return `${classAttr} style=\"${additionalStyle}\"`;\n }\n return classAttr;\n }\n };\n}\n\nfunction quikdown(markdown, options = {}) {\n if (!markdown || typeof markdown !== 'string') {\n return '';\n }\n \n const { fence_plugin, inline_styles = false, bidirectional = false, lazy_linefeeds = false, allow_unsafe_html = false } = options;\n const styles = QUIKDOWN_STYLES; // Use module-level styles\n const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once\n\n // Escape HTML entities to prevent XSS\n function escapeHtml(text) {\n return text.replace(/[&<>\"']/g, m => ESC_MAP[m]);\n }\n \n // Helper to add data-qd attributes for bidirectional support.\n // The non-bidirectional branch is a trivial no-op arrow; it's exercised in\n // the core bundle but never in quikdown_bd (which always sets bidirectional=true).\n /* istanbul ignore next - trivial no-op fallback */\n const dataQd = bidirectional ? (marker) => ` data-qd=\"${escapeHtml(marker)}\"` : () => '';\n\n // Sanitize URLs to prevent XSS attacks\n function sanitizeUrl(url, allowUnsafe = false) {\n /* istanbul ignore next - defensive programming, regex ensures url is never empty */\n if (!url) return '';\n \n // If unsafe URLs are explicitly allowed, return as-is\n if (allowUnsafe) return url;\n \n const trimmedUrl = url.trim();\n const lowerUrl = trimmedUrl.toLowerCase();\n \n // Block dangerous protocols\n const dangerousProtocols = ['javascript:', 'vbscript:', 'data:'];\n \n for (const protocol of dangerousProtocols) {\n if (lowerUrl.startsWith(protocol)) {\n // Exception: Allow data:image/* for images\n if (protocol === 'data:' && lowerUrl.startsWith('data:image/')) {\n return trimmedUrl;\n }\n // Return safe empty link for dangerous protocols\n return '#';\n }\n }\n \n return trimmedUrl;\n }\n\n // Process the markdown in phases\n let html = markdown;\n \n // Phase 1: Extract and protect code blocks and inline code\n const codeBlocks = [];\n const inlineCodes = [];\n \n // Extract fenced code blocks first (supports both ``` and ~~~)\n // Match paired fences - ``` with ``` and ~~~ with ~~~\n // Fence must be at start of line\n html = html.replace(/^(```|~~~)([^\\n]*)\\n([\\s\\S]*?)^\\1$/gm, (match, fence, lang, code) => {\n const placeholder = `${PLACEHOLDER_CB}${codeBlocks.length}§`;\n \n // Trim the language specification\n const langTrimmed = lang ? lang.trim() : '';\n \n // If custom fence plugin is provided, use it (v1.1.0: object format required)\n if (fence_plugin && fence_plugin.render && typeof fence_plugin.render === 'function') {\n codeBlocks.push({\n lang: langTrimmed,\n code: code.trimEnd(),\n custom: true,\n fence: fence,\n hasReverse: !!fence_plugin.reverse\n });\n } else {\n codeBlocks.push({\n lang: langTrimmed,\n code: escapeHtml(code.trimEnd()),\n custom: false,\n fence: fence\n });\n }\n return placeholder;\n });\n \n // Extract inline code\n html = html.replace(/`([^`]+)`/g, (match, code) => {\n const placeholder = `${PLACEHOLDER_IC}${inlineCodes.length}§`;\n inlineCodes.push(escapeHtml(code));\n return placeholder;\n });\n \n // Escape HTML in the rest of the content (skip if allow_unsafe_html is on —\n // useful for trusted pipelines where the markdown contains intentional HTML)\n if (!allow_unsafe_html) {\n html = escapeHtml(html);\n }\n \n // Phase 2: Process block elements\n \n // Process tables\n html = processTable(html, getAttr);\n \n // Process headings (supports optional trailing #'s)\n html = html.replace(/^(#{1,6})\\s+(.+?)\\s*#*$/gm, (match, hashes, content) => {\n const level = hashes.length;\n return `<h${level}${getAttr('h' + level)}${dataQd(hashes)}>${content}</h${level}>`;\n });\n \n // Process blockquotes (must handle escaped > since we already escaped HTML)\n html = html.replace(/^&gt;\\s+(.+)$/gm, `<blockquote${getAttr('blockquote')}>$1</blockquote>`);\n // Merge consecutive blockquotes\n html = html.replace(/<\\/blockquote>\\n<blockquote>/g, '\\n');\n \n // Process horizontal rules (allow trailing spaces)\n html = html.replace(/^---+\\s*$/gm, `<hr${getAttr('hr')}>`);\n \n // Process lists\n html = processLists(html, getAttr, inline_styles, bidirectional);\n \n // Phase 3: Process inline elements\n \n // Images (must come before links, with URL sanitization)\n html = html.replace(/!\\[([^\\]]*)\\]\\(([^)]+)\\)/g, (match, alt, src) => {\n const sanitizedSrc = sanitizeUrl(src, options.allow_unsafe_urls);\n const altAttr = bidirectional && alt ? ` data-qd-alt=\"${escapeHtml(alt)}\"` : '';\n const srcAttr = bidirectional ? ` data-qd-src=\"${escapeHtml(src)}\"` : '';\n return `<img${getAttr('img')} src=\"${sanitizedSrc}\" alt=\"${alt}\"${altAttr}${srcAttr}${dataQd('!')}>`;\n });\n \n // Links (with URL sanitization)\n html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (match, text, href) => {\n // Sanitize URL to prevent XSS\n const sanitizedHref = sanitizeUrl(href, options.allow_unsafe_urls);\n const isExternal = /^https?:\\/\\//i.test(sanitizedHref);\n const rel = isExternal ? ' rel=\"noopener noreferrer\"' : '';\n const textAttr = bidirectional ? ` data-qd-text=\"${escapeHtml(text)}\"` : '';\n return `<a${getAttr('a')} href=\"${sanitizedHref}\"${rel}${textAttr}${dataQd('[')}>${text}</a>`;\n });\n \n // Autolinks - convert bare URLs to clickable links\n html = html.replace(/(^|\\s)(https?:\\/\\/[^\\s<]+)/g, (match, prefix, url) => {\n const sanitizedUrl = sanitizeUrl(url, options.allow_unsafe_urls);\n return `${prefix}<a${getAttr('a')} href=\"${sanitizedUrl}\" rel=\"noopener noreferrer\">${url}</a>`;\n });\n \n // Process inline formatting (bold, italic, strikethrough)\n const inlinePatterns = [\n [/\\*\\*(.+?)\\*\\*/g, 'strong', '**'],\n [/__(.+?)__/g, 'strong', '__'],\n [/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, 'em', '*'],\n [/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em', '_'],\n [/~~(.+?)~~/g, 'del', '~~']\n ];\n \n inlinePatterns.forEach(([pattern, tag, marker]) => {\n html = html.replace(pattern, `<${tag}${getAttr(tag)}${dataQd(marker)}>$1</${tag}>`);\n });\n \n // Line breaks\n if (lazy_linefeeds) {\n // Lazy linefeeds: single newline becomes <br> (except between paragraphs and after/before block elements)\n const blocks = [];\n let bi = 0;\n \n // Protect tables and lists \n html = html.replace(/<(table|[uo]l)[^>]*>[\\s\\S]*?<\\/\\1>/g, m => {\n blocks[bi] = m;\n return `§B${bi++}§`;\n });\n \n // Handle paragraphs and block elements\n html = html.replace(/\\n\\n+/g, '§P§')\n // After block elements\n .replace(/(<\\/(?:h[1-6]|blockquote|pre)>)\\n/g, '$1§N§')\n .replace(/(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)\\n/g, '$1§N§')\n // Before block elements \n .replace(/\\n(<(?:h[1-6]|blockquote|pre|hr)[^>]*>)/g, '§N§$1')\n .replace(/\\n(§B\\d+§)/g, '§N§$1')\n .replace(/(§B\\d+§)\\n/g, '$1§N§')\n // Convert remaining newlines\n .replace(/\\n/g, `<br${getAttr('br')}>`)\n // Restore\n .replace(/§N§/g, '\\n')\n .replace(/§P§/g, '</p><p>');\n \n // Restore protected blocks\n blocks.forEach((b, i) => html = html.replace(`§B${i}§`, b));\n \n html = '<p>' + html + '</p>';\n } else {\n // Standard: two spaces at end of line for line breaks\n html = html.replace(/ {2}$/gm, `<br${getAttr('br')}>`);\n \n // Paragraphs (double newlines)\n // Don't add </p> after block elements (they're not in paragraphs)\n html = html.replace(/\\n\\n+/g, (match, offset) => {\n // Check if we're after a block element closing tag\n const before = html.substring(0, offset);\n if (before.match(/<\\/(h[1-6]|blockquote|ul|ol|table|pre|hr)>$/)) {\n return '<p>'; // Just open a new paragraph\n }\n return '</p><p>'; // Normal paragraph break\n });\n html = '<p>' + html + '</p>';\n }\n \n // Clean up empty paragraphs and unwrap block elements\n const cleanupPatterns = [\n [/<p><\\/p>/g, ''],\n [/<p>(<h[1-6][^>]*>)/g, '$1'],\n [/(<\\/h[1-6]>)<\\/p>/g, '$1'],\n [/<p>(<blockquote[^>]*>)/g, '$1'],\n [/(<\\/blockquote>)<\\/p>/g, '$1'],\n [/<p>(<ul[^>]*>|<ol[^>]*>)/g, '$1'],\n [/(<\\/ul>|<\\/ol>)<\\/p>/g, '$1'],\n [/<p>(<hr[^>]*>)<\\/p>/g, '$1'],\n [/<p>(<table[^>]*>)/g, '$1'],\n [/(<\\/table>)<\\/p>/g, '$1'],\n [/<p>(<pre[^>]*>)/g, '$1'],\n [/(<\\/pre>)<\\/p>/g, '$1'],\n [new RegExp(`<p>(${PLACEHOLDER_CB}\\\\d+§)</p>`, 'g'), '$1']\n ];\n \n cleanupPatterns.forEach(([pattern, replacement]) => {\n html = html.replace(pattern, replacement);\n });\n \n // Fix orphaned closing </p> tags after block elements\n // When a paragraph follows a block element, ensure it has opening <p>\n html = html.replace(/(<\\/(?:h[1-6]|blockquote|ul|ol|table|pre|hr)>)\\n([^<])/g, '$1\\n<p>$2');\n \n // Phase 4: Restore code blocks and inline code\n \n // Restore code blocks\n codeBlocks.forEach((block, i) => {\n let replacement;\n \n if (block.custom && fence_plugin && fence_plugin.render) {\n // Use custom fence plugin (v1.1.0: object format with render function)\n replacement = fence_plugin.render(block.code, block.lang);\n \n // If plugin returns undefined, fall back to default rendering\n if (replacement === undefined) {\n const langClass = !inline_styles && block.lang ? ` class=\"language-${block.lang}\"` : '';\n const codeAttr = inline_styles ? getAttr('code') : langClass;\n const langAttr = bidirectional && block.lang ? ` data-qd-lang=\"${escapeHtml(block.lang)}\"` : '';\n const fenceAttr = bidirectional ? ` data-qd-fence=\"${escapeHtml(block.fence)}\"` : '';\n replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${escapeHtml(block.code)}</code></pre>`;\n } else if (bidirectional) {\n // If bidirectional and plugin provided HTML, add data attributes for roundtrip\n replacement = replacement.replace(/^<(\\w+)/, \n `<$1 data-qd-fence=\"${escapeHtml(block.fence)}\" data-qd-lang=\"${escapeHtml(block.lang)}\" data-qd-source=\"${escapeHtml(block.code)}\"`);\n }\n } else {\n // Default rendering\n const langClass = !inline_styles && block.lang ? ` class=\"language-${block.lang}\"` : '';\n const codeAttr = inline_styles ? getAttr('code') : langClass;\n const langAttr = bidirectional && block.lang ? ` data-qd-lang=\"${escapeHtml(block.lang)}\"` : '';\n const fenceAttr = bidirectional ? ` data-qd-fence=\"${escapeHtml(block.fence)}\"` : '';\n replacement = `<pre${getAttr('pre')}${fenceAttr}${langAttr}><code${codeAttr}>${block.code}</code></pre>`;\n }\n \n const placeholder = `${PLACEHOLDER_CB}${i}§`;\n html = html.replace(placeholder, replacement);\n });\n \n // Restore inline code\n inlineCodes.forEach((code, i) => {\n const placeholder = `${PLACEHOLDER_IC}${i}§`;\n html = html.replace(placeholder, `<code${getAttr('code')}${dataQd('`')}>${code}</code>`);\n });\n \n return html.trim();\n}\n\n/**\n * Process inline markdown formatting\n */\nfunction processInlineMarkdown(text, getAttr) {\n \n // Process inline formatting patterns\n const patterns = [\n [/\\*\\*(.+?)\\*\\*/g, 'strong'],\n [/__(.+?)__/g, 'strong'],\n [/(?<!\\*)\\*(?!\\*)(.+?)(?<!\\*)\\*(?!\\*)/g, 'em'],\n [/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],\n [/~~(.+?)~~/g, 'del'],\n [/`([^`]+)`/g, 'code']\n ];\n \n patterns.forEach(([pattern, tag]) => {\n text = text.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);\n });\n \n return text;\n}\n\n/**\n * Process markdown tables\n */\nfunction processTable(text, getAttr) {\n const lines = text.split('\\n');\n const result = [];\n let inTable = false;\n let tableLines = [];\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n \n // Check if this line looks like a table row (with or without trailing |)\n if (line.includes('|') && (line.startsWith('|') || /[^\\\\|]/.test(line))) {\n if (!inTable) {\n inTable = true;\n tableLines = [];\n }\n tableLines.push(line);\n } else {\n // Not a table line\n if (inTable) {\n // Process the accumulated table\n const tableHtml = buildTable(tableLines, getAttr);\n if (tableHtml) {\n result.push(tableHtml);\n } else {\n // Not a valid table, restore original lines\n result.push(...tableLines);\n }\n inTable = false;\n tableLines = [];\n }\n result.push(lines[i]);\n }\n }\n \n // Handle table at end of text\n if (inTable && tableLines.length > 0) {\n const tableHtml = buildTable(tableLines, getAttr);\n if (tableHtml) {\n result.push(tableHtml);\n } else {\n result.push(...tableLines);\n }\n }\n \n return result.join('\\n');\n}\n\n/**\n * Build an HTML table from markdown table lines\n */\nfunction buildTable(lines, getAttr) {\n \n if (lines.length < 2) return null;\n \n // Check for separator line (second line should be the separator)\n let separatorIndex = -1;\n for (let i = 1; i < lines.length; i++) {\n // Support separator with or without leading/trailing pipes\n if (/^\\|?[\\s\\-:|]+\\|?$/.test(lines[i]) && lines[i].includes('-')) {\n separatorIndex = i;\n break;\n }\n }\n \n if (separatorIndex === -1) return null;\n \n const headerLines = lines.slice(0, separatorIndex);\n const bodyLines = lines.slice(separatorIndex + 1);\n \n // Parse alignment from separator\n const separator = lines[separatorIndex];\n // Handle pipes at start/end or not\n const separatorCells = separator.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|');\n const alignments = separatorCells.map(cell => {\n const trimmed = cell.trim();\n if (trimmed.startsWith(':') && trimmed.endsWith(':')) return 'center';\n if (trimmed.endsWith(':')) return 'right';\n return 'left';\n });\n \n let html = `<table${getAttr('table')}>\\n`;\n \n // Build header\n // Note: headerLines will always have length > 0 since separatorIndex starts from 1\n html += `<thead${getAttr('thead')}>\\n`;\n headerLines.forEach(line => {\n html += `<tr${getAttr('tr')}>\\n`;\n // Handle pipes at start/end or not\n const cells = line.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|');\n cells.forEach((cell, i) => {\n const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';\n const processedCell = processInlineMarkdown(cell.trim(), getAttr);\n html += `<th${getAttr('th', alignStyle)}>${processedCell}</th>\\n`;\n });\n html += '</tr>\\n';\n });\n html += '</thead>\\n';\n \n // Build body\n if (bodyLines.length > 0) {\n html += `<tbody${getAttr('tbody')}>\\n`;\n bodyLines.forEach(line => {\n html += `<tr${getAttr('tr')}>\\n`;\n // Handle pipes at start/end or not\n const cells = line.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|');\n cells.forEach((cell, i) => {\n const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';\n const processedCell = processInlineMarkdown(cell.trim(), getAttr);\n html += `<td${getAttr('td', alignStyle)}>${processedCell}</td>\\n`;\n });\n html += '</tr>\\n';\n });\n html += '</tbody>\\n';\n }\n \n html += '</table>';\n return html;\n}\n\n/**\n * Process markdown lists (ordered and unordered)\n */\nfunction processLists(text, getAttr, inline_styles, bidirectional) {\n \n const lines = text.split('\\n');\n const result = [];\n const listStack = []; // Track nested lists\n \n // Helper to escape HTML for data-qd attributes. List markers (`-`, `*`,\n // `+`, `1.`, etc.) never contain HTML-special chars, so the replace\n // callback is defensive-only and never actually fires in practice.\n const escapeHtml = (text) => text.replace(/[&<>\"']/g,\n /* istanbul ignore next - defensive: list markers never contain HTML specials */\n m => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'})[m]);\n /* istanbul ignore next - trivial no-op fallback; not exercised via bd bundle */\n const dataQd = bidirectional ? (marker) => ` data-qd=\"${escapeHtml(marker)}\"` : () => '';\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const match = line.match(/^(\\s*)([*\\-+]|\\d+\\.)\\s+(.+)$/);\n \n if (match) {\n const [, indent, marker, content] = match;\n const level = Math.floor(indent.length / 2);\n const isOrdered = /^\\d+\\./.test(marker);\n const listType = isOrdered ? 'ol' : 'ul';\n \n // Check for task list items\n let listItemContent = content;\n let taskListClass = '';\n const taskMatch = content.match(/^\\[([x ])\\]\\s+(.*)$/i);\n if (taskMatch && !isOrdered) {\n const [, checked, taskContent] = taskMatch;\n const isChecked = checked.toLowerCase() === 'x';\n const checkboxAttr = inline_styles \n ? ' style=\"margin-right:.5em\"' \n : ` class=\"${CLASS_PREFIX}task-checkbox\"`;\n listItemContent = `<input type=\"checkbox\"${checkboxAttr}${isChecked ? ' checked' : ''} disabled> ${taskContent}`;\n taskListClass = inline_styles ? ' style=\"list-style:none\"' : ` class=\"${CLASS_PREFIX}task-item\"`;\n }\n \n // Close deeper levels\n while (listStack.length > level + 1) {\n const list = listStack.pop();\n result.push(`</${list.type}>`);\n }\n \n // Open new level if needed\n if (listStack.length === level) {\n // Need to open a new list\n listStack.push({ type: listType, level });\n result.push(`<${listType}${getAttr(listType)}>`);\n } else if (listStack.length === level + 1) {\n // Check if we need to switch list type\n const currentList = listStack[listStack.length - 1];\n if (currentList.type !== listType) {\n result.push(`</${currentList.type}>`);\n listStack.pop();\n listStack.push({ type: listType, level });\n result.push(`<${listType}${getAttr(listType)}>`);\n }\n }\n \n const liAttr = taskListClass || getAttr('li');\n result.push(`<li${liAttr}${dataQd(marker)}>${listItemContent}</li>`);\n } else {\n // Not a list item, close all lists\n while (listStack.length > 0) {\n const list = listStack.pop();\n result.push(`</${list.type}>`);\n }\n result.push(line);\n }\n }\n \n // Close any remaining lists\n while (listStack.length > 0) {\n const list = listStack.pop();\n result.push(`</${list.type}>`);\n }\n \n return result.join('\\n');\n}\n\n/**\n * Emit CSS styles for quikdown elements\n * @param {string} prefix - Optional class prefix (default: 'quikdown-')\n * @param {string} theme - Optional theme: 'light' (default) or 'dark'\n * @returns {string} CSS string with quikdown styles\n */\nquikdown.emitStyles = function(prefix = 'quikdown-', theme = 'light') {\n const styles = QUIKDOWN_STYLES;\n \n // Define theme color overrides\n const themeOverrides = {\n dark: {\n '#f4f4f4': '#2a2a2a', // pre background\n '#f0f0f0': '#2a2a2a', // code background\n '#f2f2f2': '#2a2a2a', // th background\n '#ddd': '#3a3a3a', // borders\n '#06c': '#6db3f2', // links\n _textColor: '#e0e0e0'\n },\n light: {\n _textColor: '#333' // Explicit text color for light theme\n }\n };\n \n let css = '';\n for (const [tag, style] of Object.entries(styles)) {\n let themedStyle = style;\n \n // Apply theme overrides if dark theme\n if (theme === 'dark' && themeOverrides.dark) {\n // Replace colors\n for (const [oldColor, newColor] of Object.entries(themeOverrides.dark)) {\n if (!oldColor.startsWith('_')) {\n themedStyle = themedStyle.replace(new RegExp(oldColor, 'g'), newColor);\n }\n }\n \n // Add text color for certain elements in dark theme\n const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];\n if (needsTextColor.includes(tag)) {\n themedStyle += `;color:${themeOverrides.dark._textColor}`;\n }\n } else if (theme === 'light' && themeOverrides.light) {\n // Add explicit text color for light theme elements too\n const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];\n if (needsTextColor.includes(tag)) {\n themedStyle += `;color:${themeOverrides.light._textColor}`;\n }\n }\n \n css += `.${prefix}${tag} { ${themedStyle} }\\n`;\n }\n \n return css;\n};\n\n/**\n * Configure quikdown with options and return a function\n * @param {Object} options - Configuration options\n * @returns {Function} Configured quikdown function\n */\nquikdown.configure = function(options) {\n return function(markdown) {\n return quikdown(markdown, options);\n };\n};\n\n/**\n * Version information\n */\nquikdown.version = quikdownVersion;\n\n// Export for both CommonJS and ES6\n/* istanbul ignore next */\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = quikdown;\n}\n\n// For browser global\n/* istanbul ignore next */\nif (typeof window !== 'undefined') {\n window.quikdown = quikdown;\n}\n\nexport default quikdown;"],"names":["CLASS_PREFIX","PLACEHOLDER_CB","ESC_MAP","QUIKDOWN_STYLES","h1","h2","h3","h4","h5","h6","pre","code","blockquote","table","th","td","hr","img","a","strong","em","del","ul","ol","li","quikdown","markdown","options","fence_plugin","inline_styles","bidirectional","lazy_linefeeds","allow_unsafe_html","getAttr","styles","tag","additionalStyle","style","includes","replace","trim","endsWith","classAttr","createGetAttr","escapeHtml","text","m","dataQd","marker","sanitizeUrl","url","allowUnsafe","trimmedUrl","lowerUrl","toLowerCase","dangerousProtocols","protocol","startsWith","html","codeBlocks","inlineCodes","match","fence","lang","placeholder","length","langTrimmed","render","push","trimEnd","custom","hasReverse","reverse","lines","split","result","inTable","tableLines","i","line","test","tableHtml","buildTable","join","processTable","hashes","content","level","listStack","indent","Math","floor","isOrdered","listType","listItemContent","taskListClass","taskMatch","checked","taskContent","isChecked","list","pop","type","currentList","liAttr","processLists","alt","src","sanitizedSrc","allow_unsafe_urls","altAttr","srcAttr","href","sanitizedHref","rel","textAttr","prefix","sanitizedUrl","forEach","pattern","blocks","bi","b","offset","substring","RegExp","replacement","block","undefined","langClass","codeAttr","langAttr","fenceAttr","processInlineMarkdown","separatorIndex","headerLines","slice","bodyLines","alignments","map","cell","trimmed","alignStyle","processedCell","emitStyles","theme","themeOverrides","_textColor","css","Object","entries","themedStyle","oldColor","newColor","configure","version","module","exports","window"],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  * Theme with container-based scoping.
6
6
  * Usage: <div class="quikdown-light">...content...</div>
7
7
  *
8
- * @version 1.2.2
8
+ * @version 1.2.7
9
9
  * @source tools/generateThemeCSS.js
10
10
  */
11
11