quikdown 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -17
- package/dist/quikdown-lex.cjs +810 -0
- package/dist/quikdown-lex.esm.js +808 -0
- package/dist/quikdown-lex.esm.min.js +8 -0
- package/dist/quikdown-lex.esm.min.js.map +1 -0
- package/dist/quikdown-lex.umd.js +816 -0
- package/dist/quikdown-lex.umd.min.js +8 -0
- package/dist/quikdown-lex.umd.min.js.map +1 -0
- package/dist/quikdown.cjs +164 -169
- package/dist/quikdown.d.ts +70 -0
- package/dist/quikdown.dark.css +115 -56
- package/dist/quikdown.dark.min.css +2 -0
- package/dist/quikdown.esm.js +164 -169
- package/dist/quikdown.esm.min.js +2 -2
- package/dist/quikdown.esm.min.js.map +1 -1
- package/dist/quikdown.light.css +84 -52
- package/dist/quikdown.light.min.css +2 -0
- package/dist/quikdown.umd.js +164 -169
- package/dist/quikdown.umd.min.js +2 -2
- package/dist/quikdown.umd.min.js.map +1 -1
- package/package.json +8 -3
package/dist/quikdown.esm.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* quikdown - Lightweight Markdown Parser
|
|
3
|
-
* @version 1.0.
|
|
3
|
+
* @version 1.0.3
|
|
4
4
|
* @license BSD-2-Clause
|
|
5
5
|
* @copyright DeftIO 2025
|
|
6
6
|
*/
|
|
7
|
-
// Auto-generated version file - DO NOT EDIT MANUALLY
|
|
8
|
-
// This file is automatically updated by tools/updateVersion.js
|
|
9
|
-
|
|
10
|
-
const quikdownVersion = "1.0.2";
|
|
11
|
-
|
|
12
7
|
/**
|
|
13
8
|
* quikdown - A minimal markdown parser optimized for chat/LLM output
|
|
14
9
|
* Supports tables, code blocks, lists, and common formatting
|
|
@@ -20,64 +15,71 @@ const quikdownVersion = "1.0.2";
|
|
|
20
15
|
* @returns {string} - The rendered HTML
|
|
21
16
|
*/
|
|
22
17
|
|
|
18
|
+
// Version will be injected at build time
|
|
19
|
+
const quikdownVersion = '1.0.3';
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const { fence_plugin, inline_styles = false } = options;
|
|
21
|
+
// Constants for reuse
|
|
22
|
+
const CLASS_PREFIX = 'quikdown-';
|
|
23
|
+
const PLACEHOLDER_CB = '§CB';
|
|
24
|
+
const PLACEHOLDER_IC = '§IC';
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
h1: 'font-size: 2em; font-weight: 600; margin: 0.67em 0; text-align: left',
|
|
34
|
-
h2: 'font-size: 1.5em; font-weight: 600; margin: 0.83em 0',
|
|
35
|
-
h3: 'font-size: 1.25em; font-weight: 600; margin: 1em 0',
|
|
36
|
-
h4: 'font-size: 1em; font-weight: 600; margin: 1.33em 0',
|
|
37
|
-
h5: 'font-size: 0.875em; font-weight: 600; margin: 1.67em 0',
|
|
38
|
-
h6: 'font-size: 0.85em; font-weight: 600; margin: 2em 0',
|
|
39
|
-
pre: 'background: #f4f4f4; padding: 10px; border-radius: 4px; overflow-x: auto; margin: 1em 0',
|
|
40
|
-
code: 'background: #f0f0f0; padding: 2px 4px; border-radius: 3px; font-family: monospace',
|
|
41
|
-
blockquote: 'border-left: 4px solid #ddd; margin-left: 0; padding-left: 1em',
|
|
42
|
-
table: 'border-collapse: collapse; width: 100%; margin: 1em 0',
|
|
43
|
-
thead: '',
|
|
44
|
-
tbody: '',
|
|
45
|
-
tr: '',
|
|
46
|
-
th: 'border: 1px solid #ddd; padding: 8px; background-color: #f2f2f2; font-weight: bold; text-align: left',
|
|
47
|
-
td: 'border: 1px solid #ddd; padding: 8px; text-align: left',
|
|
48
|
-
hr: 'border: none; border-top: 1px solid #ddd; margin: 1em 0',
|
|
49
|
-
img: 'max-width: 100%; height: auto',
|
|
50
|
-
a: 'color: #0066cc; text-decoration: underline',
|
|
51
|
-
strong: 'font-weight: bold',
|
|
52
|
-
em: 'font-style: italic',
|
|
53
|
-
del: 'text-decoration: line-through',
|
|
54
|
-
ul: 'margin: 0.5em 0; padding-left: 2em',
|
|
55
|
-
ol: 'margin: 0.5em 0; padding-left: 2em',
|
|
56
|
-
li: 'margin: 0.25em 0',
|
|
57
|
-
br: ''
|
|
58
|
-
};
|
|
26
|
+
// Escape map at module level
|
|
27
|
+
const ESC_MAP = {'&':'&','<':'<','>':'>','"':'"',"'":'''};
|
|
59
28
|
|
|
60
|
-
|
|
61
|
-
|
|
29
|
+
// Single source of truth for all style definitions - optimized
|
|
30
|
+
const QUIKDOWN_STYLES = {
|
|
31
|
+
h1: 'font-size:2em;font-weight:600;margin:.67em 0;text-align:left',
|
|
32
|
+
h2: 'font-size:1.5em;font-weight:600;margin:.83em 0',
|
|
33
|
+
h3: 'font-size:1.25em;font-weight:600;margin:1em 0',
|
|
34
|
+
h4: 'font-size:1em;font-weight:600;margin:1.33em 0',
|
|
35
|
+
h5: 'font-size:.875em;font-weight:600;margin:1.67em 0',
|
|
36
|
+
h6: 'font-size:.85em;font-weight:600;margin:2em 0',
|
|
37
|
+
pre: 'background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0',
|
|
38
|
+
code: 'background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace',
|
|
39
|
+
blockquote: 'border-left:4px solid #ddd;margin-left:0;padding-left:1em',
|
|
40
|
+
table: 'border-collapse:collapse;width:100%;margin:1em 0',
|
|
41
|
+
th: 'border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left',
|
|
42
|
+
td: 'border:1px solid #ddd;padding:8px;text-align:left',
|
|
43
|
+
hr: 'border:none;border-top:1px solid #ddd;margin:1em 0',
|
|
44
|
+
img: 'max-width:100%;height:auto',
|
|
45
|
+
a: 'color:#06c;text-decoration:underline',
|
|
46
|
+
strong: 'font-weight:bold',
|
|
47
|
+
em: 'font-style:italic',
|
|
48
|
+
del: 'text-decoration:line-through',
|
|
49
|
+
ul: 'margin:.5em 0;padding-left:2em',
|
|
50
|
+
ol: 'margin:.5em 0;padding-left:2em',
|
|
51
|
+
li: 'margin:.25em 0',
|
|
52
|
+
// Task list specific styles
|
|
53
|
+
'task-item': 'list-style:none',
|
|
54
|
+
'task-checkbox': 'margin-right:.5em'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Factory function to create getAttr for a given context
|
|
58
|
+
function createGetAttr(inline_styles, styles) {
|
|
59
|
+
return function(tag, additionalStyle = '') {
|
|
62
60
|
if (inline_styles) {
|
|
63
|
-
const style = styles[tag]
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
const style = styles[tag];
|
|
62
|
+
if (!style && !additionalStyle) return '';
|
|
63
|
+
const fullStyle = additionalStyle ? (style ? `${style};${additionalStyle}` : additionalStyle) : style;
|
|
64
|
+
return ` style="${fullStyle}"`;
|
|
66
65
|
} else {
|
|
67
|
-
return ` class="
|
|
66
|
+
return ` class="${CLASS_PREFIX}${tag}"`;
|
|
68
67
|
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function quikdown(markdown, options = {}) {
|
|
72
|
+
if (!markdown || typeof markdown !== 'string') {
|
|
73
|
+
return '';
|
|
69
74
|
}
|
|
75
|
+
|
|
76
|
+
const { fence_plugin, inline_styles = false } = options;
|
|
77
|
+
const styles = QUIKDOWN_STYLES; // Use module-level styles
|
|
78
|
+
const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once
|
|
70
79
|
|
|
71
80
|
// Escape HTML entities to prevent XSS
|
|
72
81
|
function escapeHtml(text) {
|
|
73
|
-
|
|
74
|
-
'&': '&',
|
|
75
|
-
'<': '<',
|
|
76
|
-
'>': '>',
|
|
77
|
-
'"': '"',
|
|
78
|
-
"'": '''
|
|
79
|
-
};
|
|
80
|
-
return text.replace(/[&<>"']/g, m => map[m]);
|
|
82
|
+
return text.replace(/[&<>"']/g, m => ESC_MAP[m]);
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
// Sanitize URLs to prevent XSS attacks
|
|
@@ -87,7 +89,6 @@ function quikdown(markdown, options = {}) {
|
|
|
87
89
|
// If unsafe URLs are explicitly allowed, return as-is
|
|
88
90
|
if (allowUnsafe) return url;
|
|
89
91
|
|
|
90
|
-
// Trim and lowercase for checking
|
|
91
92
|
const trimmedUrl = url.trim();
|
|
92
93
|
const lowerUrl = trimmedUrl.toLowerCase();
|
|
93
94
|
|
|
@@ -119,7 +120,7 @@ function quikdown(markdown, options = {}) {
|
|
|
119
120
|
// Match paired fences - ``` with ``` and ~~~ with ~~~
|
|
120
121
|
// Fence must be at start of line
|
|
121
122
|
html = html.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm, (match, fence, lang, code) => {
|
|
122
|
-
const placeholder =
|
|
123
|
+
const placeholder = `${PLACEHOLDER_CB}${codeBlocks.length}§`;
|
|
123
124
|
|
|
124
125
|
// Trim the language specification
|
|
125
126
|
const langTrimmed = lang ? lang.trim() : '';
|
|
@@ -143,7 +144,7 @@ function quikdown(markdown, options = {}) {
|
|
|
143
144
|
|
|
144
145
|
// Extract inline code
|
|
145
146
|
html = html.replace(/`([^`]+)`/g, (match, code) => {
|
|
146
|
-
const placeholder =
|
|
147
|
+
const placeholder = `${PLACEHOLDER_IC}${inlineCodes.length}§`;
|
|
147
148
|
inlineCodes.push(escapeHtml(code));
|
|
148
149
|
return placeholder;
|
|
149
150
|
});
|
|
@@ -154,7 +155,7 @@ function quikdown(markdown, options = {}) {
|
|
|
154
155
|
// Phase 2: Process block elements
|
|
155
156
|
|
|
156
157
|
// Process tables
|
|
157
|
-
html = processTable(html,
|
|
158
|
+
html = processTable(html, getAttr);
|
|
158
159
|
|
|
159
160
|
// Process headings (supports optional trailing #'s)
|
|
160
161
|
html = html.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm, (match, hashes, content) => {
|
|
@@ -171,7 +172,7 @@ function quikdown(markdown, options = {}) {
|
|
|
171
172
|
html = html.replace(/^---+$/gm, `<hr${getAttr('hr')}>`);
|
|
172
173
|
|
|
173
174
|
// Process lists
|
|
174
|
-
html = processLists(html,
|
|
175
|
+
html = processLists(html, getAttr, inline_styles);
|
|
175
176
|
|
|
176
177
|
// Phase 3: Process inline elements
|
|
177
178
|
|
|
@@ -196,16 +197,18 @@ function quikdown(markdown, options = {}) {
|
|
|
196
197
|
return `${prefix}<a${getAttr('a')} href="${sanitizedUrl}" rel="noopener noreferrer">${url}</a>`;
|
|
197
198
|
});
|
|
198
199
|
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
// Process inline formatting (bold, italic, strikethrough)
|
|
201
|
+
const inlinePatterns = [
|
|
202
|
+
[/\*\*(.+?)\*\*/g, 'strong'],
|
|
203
|
+
[/__(.+?)__/g, 'strong'],
|
|
204
|
+
[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em'],
|
|
205
|
+
[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],
|
|
206
|
+
[/~~(.+?)~~/g, 'del']
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
inlinePatterns.forEach(([pattern, tag]) => {
|
|
210
|
+
html = html.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);
|
|
211
|
+
});
|
|
209
212
|
|
|
210
213
|
// Line breaks (two spaces at end of line)
|
|
211
214
|
html = html.replace(/ $/gm, `<br${getAttr('br')}>`);
|
|
@@ -214,21 +217,26 @@ function quikdown(markdown, options = {}) {
|
|
|
214
217
|
html = html.replace(/\n\n+/g, '</p><p>');
|
|
215
218
|
html = '<p>' + html + '</p>';
|
|
216
219
|
|
|
217
|
-
// Clean up empty paragraphs and unwrap block elements
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
220
|
+
// Clean up empty paragraphs and unwrap block elements
|
|
221
|
+
const cleanupPatterns = [
|
|
222
|
+
[/<p><\/p>/g, ''],
|
|
223
|
+
[/<p>(<h[1-6][^>]*>)/g, '$1'],
|
|
224
|
+
[/(<\/h[1-6]>)<\/p>/g, '$1'],
|
|
225
|
+
[/<p>(<blockquote[^>]*>)/g, '$1'],
|
|
226
|
+
[/(<\/blockquote>)<\/p>/g, '$1'],
|
|
227
|
+
[/<p>(<ul[^>]*>|<ol[^>]*>)/g, '$1'],
|
|
228
|
+
[/(<\/ul>|<\/ol>)<\/p>/g, '$1'],
|
|
229
|
+
[/<p>(<hr[^>]*>)<\/p>/g, '$1'],
|
|
230
|
+
[/<p>(<table[^>]*>)/g, '$1'],
|
|
231
|
+
[/(<\/table>)<\/p>/g, '$1'],
|
|
232
|
+
[/<p>(<pre[^>]*>)/g, '$1'],
|
|
233
|
+
[/(<\/pre>)<\/p>/g, '$1'],
|
|
234
|
+
[new RegExp(`<p>(${PLACEHOLDER_CB}\\d+§)<\/p>`, 'g'), '$1']
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
cleanupPatterns.forEach(([pattern, replacement]) => {
|
|
238
|
+
html = html.replace(pattern, replacement);
|
|
239
|
+
});
|
|
232
240
|
|
|
233
241
|
// Phase 4: Restore code blocks and inline code
|
|
234
242
|
|
|
@@ -252,13 +260,13 @@ function quikdown(markdown, options = {}) {
|
|
|
252
260
|
replacement = `<pre${getAttr('pre')}><code${codeAttr}>${block.code}</code></pre>`;
|
|
253
261
|
}
|
|
254
262
|
|
|
255
|
-
const placeholder =
|
|
263
|
+
const placeholder = `${PLACEHOLDER_CB}${i}§`;
|
|
256
264
|
html = html.replace(placeholder, replacement);
|
|
257
265
|
});
|
|
258
266
|
|
|
259
267
|
// Restore inline code
|
|
260
268
|
inlineCodes.forEach((code, i) => {
|
|
261
|
-
const placeholder =
|
|
269
|
+
const placeholder = `${PLACEHOLDER_IC}${i}§`;
|
|
262
270
|
html = html.replace(placeholder, `<code${getAttr('code')}>${code}</code>`);
|
|
263
271
|
});
|
|
264
272
|
|
|
@@ -268,31 +276,21 @@ function quikdown(markdown, options = {}) {
|
|
|
268
276
|
/**
|
|
269
277
|
* Process inline markdown formatting
|
|
270
278
|
*/
|
|
271
|
-
function processInlineMarkdown(text,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
// Process italic (must not match bold markers)
|
|
288
|
-
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, `<em${getAttr('em')}>$1</em>`);
|
|
289
|
-
text = text.replace(/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, `<em${getAttr('em')}>$1</em>`);
|
|
290
|
-
|
|
291
|
-
// Process strikethrough
|
|
292
|
-
text = text.replace(/~~(.+?)~~/g, `<del${getAttr('del')}>$1</del>`);
|
|
293
|
-
|
|
294
|
-
// Process inline code
|
|
295
|
-
text = text.replace(/`([^`]+)`/g, `<code${getAttr('code')}>$1</code>`);
|
|
279
|
+
function processInlineMarkdown(text, getAttr) {
|
|
280
|
+
|
|
281
|
+
// Process inline formatting patterns
|
|
282
|
+
const patterns = [
|
|
283
|
+
[/\*\*(.+?)\*\*/g, 'strong'],
|
|
284
|
+
[/__(.+?)__/g, 'strong'],
|
|
285
|
+
[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em'],
|
|
286
|
+
[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],
|
|
287
|
+
[/~~(.+?)~~/g, 'del'],
|
|
288
|
+
[/`([^`]+)`/g, 'code']
|
|
289
|
+
];
|
|
290
|
+
|
|
291
|
+
patterns.forEach(([pattern, tag]) => {
|
|
292
|
+
text = text.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);
|
|
293
|
+
});
|
|
296
294
|
|
|
297
295
|
return text;
|
|
298
296
|
}
|
|
@@ -300,7 +298,7 @@ function processInlineMarkdown(text, inline_styles, styles) {
|
|
|
300
298
|
/**
|
|
301
299
|
* Process markdown tables
|
|
302
300
|
*/
|
|
303
|
-
function processTable(text,
|
|
301
|
+
function processTable(text, getAttr) {
|
|
304
302
|
const lines = text.split('\n');
|
|
305
303
|
const result = [];
|
|
306
304
|
let inTable = false;
|
|
@@ -320,7 +318,7 @@ function processTable(text, inline_styles, styles) {
|
|
|
320
318
|
// Not a table line
|
|
321
319
|
if (inTable) {
|
|
322
320
|
// Process the accumulated table
|
|
323
|
-
const tableHtml = buildTable(tableLines,
|
|
321
|
+
const tableHtml = buildTable(tableLines, getAttr);
|
|
324
322
|
if (tableHtml) {
|
|
325
323
|
result.push(tableHtml);
|
|
326
324
|
} else {
|
|
@@ -336,7 +334,7 @@ function processTable(text, inline_styles, styles) {
|
|
|
336
334
|
|
|
337
335
|
// Handle table at end of text
|
|
338
336
|
if (inTable && tableLines.length > 0) {
|
|
339
|
-
const tableHtml = buildTable(tableLines,
|
|
337
|
+
const tableHtml = buildTable(tableLines, getAttr);
|
|
340
338
|
if (tableHtml) {
|
|
341
339
|
result.push(tableHtml);
|
|
342
340
|
} else {
|
|
@@ -350,17 +348,7 @@ function processTable(text, inline_styles, styles) {
|
|
|
350
348
|
/**
|
|
351
349
|
* Build an HTML table from markdown table lines
|
|
352
350
|
*/
|
|
353
|
-
function buildTable(lines,
|
|
354
|
-
// Helper to get attributes
|
|
355
|
-
function getAttr(tag, additionalStyle = '') {
|
|
356
|
-
if (inline_styles) {
|
|
357
|
-
const style = styles[tag] || '';
|
|
358
|
-
const fullStyle = additionalStyle ? `${style}; ${additionalStyle}` : style;
|
|
359
|
-
return fullStyle ? ` style="${fullStyle}"` : '';
|
|
360
|
-
} else {
|
|
361
|
-
return ` class="quikdown-${tag}"`;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
351
|
+
function buildTable(lines, getAttr) {
|
|
364
352
|
|
|
365
353
|
if (lines.length < 2) return null;
|
|
366
354
|
|
|
@@ -400,8 +388,8 @@ function buildTable(lines, inline_styles, styles) {
|
|
|
400
388
|
// Handle pipes at start/end or not
|
|
401
389
|
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
|
|
402
390
|
cells.forEach((cell, i) => {
|
|
403
|
-
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align
|
|
404
|
-
const processedCell = processInlineMarkdown(cell.trim(),
|
|
391
|
+
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';
|
|
392
|
+
const processedCell = processInlineMarkdown(cell.trim(), getAttr);
|
|
405
393
|
html += `<th${getAttr('th', alignStyle)}>${processedCell}</th>\n`;
|
|
406
394
|
});
|
|
407
395
|
html += '</tr>\n';
|
|
@@ -417,8 +405,8 @@ function buildTable(lines, inline_styles, styles) {
|
|
|
417
405
|
// Handle pipes at start/end or not
|
|
418
406
|
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
|
|
419
407
|
cells.forEach((cell, i) => {
|
|
420
|
-
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align
|
|
421
|
-
const processedCell = processInlineMarkdown(cell.trim(),
|
|
408
|
+
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';
|
|
409
|
+
const processedCell = processInlineMarkdown(cell.trim(), getAttr);
|
|
422
410
|
html += `<td${getAttr('td', alignStyle)}>${processedCell}</td>\n`;
|
|
423
411
|
});
|
|
424
412
|
html += '</tr>\n';
|
|
@@ -433,17 +421,7 @@ function buildTable(lines, inline_styles, styles) {
|
|
|
433
421
|
/**
|
|
434
422
|
* Process markdown lists (ordered and unordered)
|
|
435
423
|
*/
|
|
436
|
-
function processLists(text,
|
|
437
|
-
// Helper to get attributes
|
|
438
|
-
function getAttr(tag, additionalStyle = '') {
|
|
439
|
-
if (inline_styles) {
|
|
440
|
-
const style = styles[tag] || '';
|
|
441
|
-
const fullStyle = additionalStyle ? `${style}; ${additionalStyle}` : style;
|
|
442
|
-
return fullStyle ? ` style="${fullStyle}"` : '';
|
|
443
|
-
} else {
|
|
444
|
-
return ` class="quikdown-${tag}"`;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
424
|
+
function processLists(text, getAttr, inline_styles) {
|
|
447
425
|
|
|
448
426
|
const lines = text.split('\n');
|
|
449
427
|
const result = [];
|
|
@@ -467,10 +445,10 @@ function processLists(text, inline_styles, styles) {
|
|
|
467
445
|
const [, checked, taskContent] = taskMatch;
|
|
468
446
|
const isChecked = checked.toLowerCase() === 'x';
|
|
469
447
|
const checkboxAttr = inline_styles
|
|
470
|
-
? ' style="margin-right
|
|
471
|
-
:
|
|
448
|
+
? ' style="margin-right:.5em"'
|
|
449
|
+
: ` class="${CLASS_PREFIX}task-checkbox"`;
|
|
472
450
|
listItemContent = `<input type="checkbox"${checkboxAttr}${isChecked ? ' checked' : ''} disabled> ${taskContent}`;
|
|
473
|
-
taskListClass = inline_styles ? ' style="list-style:
|
|
451
|
+
taskListClass = inline_styles ? ' style="list-style:none"' : ` class="${CLASS_PREFIX}task-item"`;
|
|
474
452
|
}
|
|
475
453
|
|
|
476
454
|
// Close deeper levels
|
|
@@ -518,39 +496,56 @@ function processLists(text, inline_styles, styles) {
|
|
|
518
496
|
|
|
519
497
|
/**
|
|
520
498
|
* Emit CSS styles for quikdown elements
|
|
499
|
+
* @param {string} prefix - Optional class prefix (default: 'quikdown-')
|
|
500
|
+
* @param {string} theme - Optional theme: 'light' (default) or 'dark'
|
|
521
501
|
* @returns {string} CSS string with quikdown styles
|
|
522
502
|
*/
|
|
523
|
-
quikdown.emitStyles = function() {
|
|
524
|
-
const styles =
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
a: 'color: #0066cc; text-decoration: underline',
|
|
540
|
-
strong: 'font-weight: bold',
|
|
541
|
-
em: 'font-style: italic',
|
|
542
|
-
del: 'text-decoration: line-through',
|
|
543
|
-
ul: 'margin: 0.5em 0; padding-left: 2em',
|
|
544
|
-
ol: 'margin: 0.5em 0; padding-left: 2em',
|
|
545
|
-
li: 'margin: 0.25em 0',
|
|
546
|
-
'task-item': 'list-style: none',
|
|
547
|
-
'task-checkbox': 'margin-right: 0.5em'
|
|
503
|
+
quikdown.emitStyles = function(prefix = 'quikdown-', theme = 'light') {
|
|
504
|
+
const styles = QUIKDOWN_STYLES;
|
|
505
|
+
|
|
506
|
+
// Define theme color overrides
|
|
507
|
+
const themeOverrides = {
|
|
508
|
+
dark: {
|
|
509
|
+
'#f4f4f4': '#2a2a2a', // pre background
|
|
510
|
+
'#f0f0f0': '#2a2a2a', // code background
|
|
511
|
+
'#f2f2f2': '#2a2a2a', // th background
|
|
512
|
+
'#ddd': '#3a3a3a', // borders
|
|
513
|
+
'#06c': '#6db3f2', // links
|
|
514
|
+
_textColor: '#e0e0e0'
|
|
515
|
+
},
|
|
516
|
+
light: {
|
|
517
|
+
_textColor: '#333' // Explicit text color for light theme
|
|
518
|
+
}
|
|
548
519
|
};
|
|
549
520
|
|
|
550
521
|
let css = '';
|
|
551
522
|
for (const [tag, style] of Object.entries(styles)) {
|
|
552
523
|
if (style) {
|
|
553
|
-
|
|
524
|
+
let themedStyle = style;
|
|
525
|
+
|
|
526
|
+
// Apply theme overrides if dark theme
|
|
527
|
+
if (theme === 'dark' && themeOverrides.dark) {
|
|
528
|
+
// Replace colors
|
|
529
|
+
for (const [oldColor, newColor] of Object.entries(themeOverrides.dark)) {
|
|
530
|
+
if (!oldColor.startsWith('_')) {
|
|
531
|
+
themedStyle = themedStyle.replace(new RegExp(oldColor, 'g'), newColor);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Add text color for certain elements in dark theme
|
|
536
|
+
const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];
|
|
537
|
+
if (needsTextColor.includes(tag)) {
|
|
538
|
+
themedStyle += `;color:${themeOverrides.dark._textColor}`;
|
|
539
|
+
}
|
|
540
|
+
} else if (theme === 'light' && themeOverrides.light) {
|
|
541
|
+
// Add explicit text color for light theme elements too
|
|
542
|
+
const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];
|
|
543
|
+
if (needsTextColor.includes(tag)) {
|
|
544
|
+
themedStyle += `;color:${themeOverrides.light._textColor}`;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
css += `.${prefix}${tag} { ${themedStyle} }\n`;
|
|
554
549
|
}
|
|
555
550
|
}
|
|
556
551
|
|
package/dist/quikdown.esm.min.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* quikdown - Lightweight Markdown Parser
|
|
3
|
-
* @version 1.0.
|
|
3
|
+
* @version 1.0.3
|
|
4
4
|
* @license BSD-2-Clause
|
|
5
5
|
* @copyright DeftIO 2025
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
const e="quikdown-",t="§CB",n={"&":"&","<":"<",">":">",'"':""","'":"'"},o={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 r(r,l={}){if(!r||"string"!=typeof r)return"";const{fence_plugin:i,inline_styles:a=!1}=l,c=function(t,n){return function(o,r=""){if(t){const e=n[o];return e||r?` style="${r?e?`${e};${r}`:r:e}"`:""}return` class="${e}${o}"`}}(a,o);function p(e){return e.replace(/[&<>"']/g,e=>n[e])}function g(e,t=!1){if(!e)return"";if(t)return e;const n=e.trim(),o=n.toLowerCase(),r=["javascript:","vbscript:","data:"];for(const e of r)if(o.startsWith(e))return"data:"===e&&o.startsWith("data:image/")?n:"#";return n}let f=r;const d=[],h=[];f=f.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm,(e,n,o,r)=>{const l=`${t}${d.length}§`,s=o?o.trim():"";return i&&"function"==typeof i?d.push({lang:s,code:r.trimEnd(),custom:!0}):d.push({lang:s,code:p(r.trimEnd()),custom:!1}),l}),f=f.replace(/`([^`]+)`/g,(e,t)=>{const n=`§IC${h.length}§`;return h.push(p(t)),n}),f=p(f),f=function(e,t){const n=e.split("\n"),o=[];let r=!1,l=[];for(let e=0;e<n.length;e++){const i=n[e].trim();if(i.includes("|")&&(i.startsWith("|")||/[^\\|]/.test(i)))r||(r=!0,l=[]),l.push(i);else{if(r){const e=s(l,t);e?o.push(e):o.push(...l),r=!1,l=[]}o.push(n[e])}}if(r&&l.length>0){const e=s(l,t);e?o.push(e):o.push(...l)}return o.join("\n")}(f,c),f=f.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm,(e,t,n)=>{const o=t.length;return`<h${o}${c("h"+o)}>${n}</h${o}>`}),f=f.replace(/^>\s+(.+)$/gm,`<blockquote${c("blockquote")}>$1</blockquote>`),f=f.replace(/<\/blockquote>\n<blockquote>/g,"\n"),f=f.replace(/^---+$/gm,`<hr${c("hr")}>`),f=function(t,n,o){const r=t.split("\n"),l=[];let s=[];for(let t=0;t<r.length;t++){const i=r[t],a=i.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);if(a){const[,t,r,i]=a,c=Math.floor(t.length/2),p=/^\d+\./.test(r),g=p?"ol":"ul";let f=i,d="";const h=i.match(/^\[([x ])\]\s+(.*)$/i);if(h&&!p){const[,t,n]=h,r="x"===t.toLowerCase();f=`<input type="checkbox"${o?' style="margin-right:.5em"':` class="${e}task-checkbox"`}${r?" checked":""} disabled> ${n}`,d=o?' style="list-style:none"':` class="${e}task-item"`}for(;s.length>c+1;){const e=s.pop();l.push(`</${e.type}>`)}if(s.length===c)s.push({type:g,level:c}),l.push(`<${g}${n(g)}>`);else if(s.length===c+1){const e=s[s.length-1];e.type!==g&&(l.push(`</${e.type}>`),s.pop(),s.push({type:g,level:c}),l.push(`<${g}${n(g)}>`))}const u=d||n("li");l.push(`<li${u}>${f}</li>`)}else{for(;s.length>0;){const e=s.pop();l.push(`</${e.type}>`)}l.push(i)}}for(;s.length>0;){const e=s.pop();l.push(`</${e.type}>`)}return l.join("\n")}(f,c,a),f=f.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(e,t,n)=>{const o=g(n,l.allow_unsafe_urls);return`<img${c("img")} src="${o}" alt="${t}">`}),f=f.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>{const o=g(n,l.allow_unsafe_urls),r=/^https?:\/\//i.test(o)?' rel="noopener noreferrer"':"";return`<a${c("a")} href="${o}"${r}>${t}</a>`}),f=f.replace(/(^|\s)(https?:\/\/[^\s<]+)/g,(e,t,n)=>{const o=g(n,l.allow_unsafe_urls);return`${t}<a${c("a")} href="${o}" rel="noopener noreferrer">${n}</a>`});[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"]].forEach(([e,t])=>{f=f.replace(e,`<${t}${c(t)}>$1</${t}>`)}),f=f.replace(/ $/gm,`<br${c("br")}>`),f=f.replace(/\n\n+/g,"</p><p>"),f="<p>"+f+"</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])=>{f=f.replace(e,t)}),d.forEach((e,n)=>{let o;if(e.custom&&i){if(o=i(e.code,e.lang),void 0===o){const t=!a&&e.lang?` class="language-${e.lang}"`:"",n=a?c("code"):t;o=`<pre${c("pre")}><code${n}>${p(e.code)}</code></pre>`}}else{const t=!a&&e.lang?` class="language-${e.lang}"`:"",n=a?c("code"):t;o=`<pre${c("pre")}><code${n}>${e.code}</code></pre>`}const r=`${t}${n}§`;f=f.replace(r,o)}),h.forEach((e,t)=>{const n=`§IC${t}§`;f=f.replace(n,`<code${c("code")}>${e}</code>`)}),f.trim()}function l(e,t){return[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"],[/`([^`]+)`/g,"code"]].forEach(([n,o])=>{e=e.replace(n,`<${o}${t(o)}>$1</${o}>`)}),e}function s(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 o=e.slice(0,n),r=e.slice(n+1),s=e[n].trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(e=>{const t=e.trim();return t.startsWith(":")&&t.endsWith(":")?"center":t.endsWith(":")?"right":"left"});let i=`<table${t("table")}>\n`;return o.length>0&&(i+=`<thead${t("thead")}>\n`,o.forEach(e=>{i+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const o=s[n]&&"left"!==s[n]?`text-align:${s[n]}`:"",r=l(e.trim(),t);i+=`<th${t("th",o)}>${r}</th>\n`}),i+="</tr>\n"}),i+="</thead>\n"),r.length>0&&(i+=`<tbody${t("tbody")}>\n`,r.forEach(e=>{i+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const o=s[n]&&"left"!==s[n]?`text-align:${s[n]}`:"",r=l(e.trim(),t);i+=`<td${t("td",o)}>${r}</td>\n`}),i+="</tr>\n"}),i+="</tbody>\n"),i+="</table>",i}r.emitStyles=function(e="quikdown-",t="light"){const n=o,r={"#f4f4f4":"#2a2a2a","#f0f0f0":"#2a2a2a","#f2f2f2":"#2a2a2a","#ddd":"#3a3a3a","#06c":"#6db3f2",_textColor:"#e0e0e0"},l={_textColor:"#333"};let s="";for(const[o,i]of Object.entries(n))if(i){let n=i;if("dark"===t&&r){for(const[e,t]of Object.entries(r))e.startsWith("_")||(n=n.replace(new RegExp(e,"g"),t));["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(o)&&(n+=`;color:${r._textColor}`)}else if("light"===t&&l){["h1","h2","h3","h4","h5","h6","td","li","blockquote"].includes(o)&&(n+=`;color:${l._textColor}`)}s+=`.${e}${o} { ${n} }\n`}return s},r.configure=function(e){return function(t){return r(t,e)}},r.version="1.0.3","undefined"!=typeof module&&module.exports&&(module.exports=r),"undefined"!=typeof window&&(window.quikdown=r);export{r as default};
|
|
8
8
|
//# sourceMappingURL=quikdown.esm.min.js.map
|