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.umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
|
*/
|
|
@@ -10,11 +10,6 @@
|
|
|
10
10
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.quikdown = factory());
|
|
11
11
|
})(this, (function () { 'use strict';
|
|
12
12
|
|
|
13
|
-
// Auto-generated version file - DO NOT EDIT MANUALLY
|
|
14
|
-
// This file is automatically updated by tools/updateVersion.js
|
|
15
|
-
|
|
16
|
-
const quikdownVersion = "1.0.2";
|
|
17
|
-
|
|
18
13
|
/**
|
|
19
14
|
* quikdown - A minimal markdown parser optimized for chat/LLM output
|
|
20
15
|
* Supports tables, code blocks, lists, and common formatting
|
|
@@ -26,64 +21,71 @@
|
|
|
26
21
|
* @returns {string} - The rendered HTML
|
|
27
22
|
*/
|
|
28
23
|
|
|
24
|
+
// Version will be injected at build time
|
|
25
|
+
const quikdownVersion = '1.0.3';
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const { fence_plugin, inline_styles = false } = options;
|
|
27
|
+
// Constants for reuse
|
|
28
|
+
const CLASS_PREFIX = 'quikdown-';
|
|
29
|
+
const PLACEHOLDER_CB = '§CB';
|
|
30
|
+
const PLACEHOLDER_IC = '§IC';
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
h1: 'font-size: 2em; font-weight: 600; margin: 0.67em 0; text-align: left',
|
|
40
|
-
h2: 'font-size: 1.5em; font-weight: 600; margin: 0.83em 0',
|
|
41
|
-
h3: 'font-size: 1.25em; font-weight: 600; margin: 1em 0',
|
|
42
|
-
h4: 'font-size: 1em; font-weight: 600; margin: 1.33em 0',
|
|
43
|
-
h5: 'font-size: 0.875em; font-weight: 600; margin: 1.67em 0',
|
|
44
|
-
h6: 'font-size: 0.85em; font-weight: 600; margin: 2em 0',
|
|
45
|
-
pre: 'background: #f4f4f4; padding: 10px; border-radius: 4px; overflow-x: auto; margin: 1em 0',
|
|
46
|
-
code: 'background: #f0f0f0; padding: 2px 4px; border-radius: 3px; font-family: monospace',
|
|
47
|
-
blockquote: 'border-left: 4px solid #ddd; margin-left: 0; padding-left: 1em',
|
|
48
|
-
table: 'border-collapse: collapse; width: 100%; margin: 1em 0',
|
|
49
|
-
thead: '',
|
|
50
|
-
tbody: '',
|
|
51
|
-
tr: '',
|
|
52
|
-
th: 'border: 1px solid #ddd; padding: 8px; background-color: #f2f2f2; font-weight: bold; text-align: left',
|
|
53
|
-
td: 'border: 1px solid #ddd; padding: 8px; text-align: left',
|
|
54
|
-
hr: 'border: none; border-top: 1px solid #ddd; margin: 1em 0',
|
|
55
|
-
img: 'max-width: 100%; height: auto',
|
|
56
|
-
a: 'color: #0066cc; text-decoration: underline',
|
|
57
|
-
strong: 'font-weight: bold',
|
|
58
|
-
em: 'font-style: italic',
|
|
59
|
-
del: 'text-decoration: line-through',
|
|
60
|
-
ul: 'margin: 0.5em 0; padding-left: 2em',
|
|
61
|
-
ol: 'margin: 0.5em 0; padding-left: 2em',
|
|
62
|
-
li: 'margin: 0.25em 0',
|
|
63
|
-
br: ''
|
|
64
|
-
};
|
|
32
|
+
// Escape map at module level
|
|
33
|
+
const ESC_MAP = {'&':'&','<':'<','>':'>','"':'"',"'":'''};
|
|
65
34
|
|
|
66
|
-
|
|
67
|
-
|
|
35
|
+
// Single source of truth for all style definitions - optimized
|
|
36
|
+
const QUIKDOWN_STYLES = {
|
|
37
|
+
h1: 'font-size:2em;font-weight:600;margin:.67em 0;text-align:left',
|
|
38
|
+
h2: 'font-size:1.5em;font-weight:600;margin:.83em 0',
|
|
39
|
+
h3: 'font-size:1.25em;font-weight:600;margin:1em 0',
|
|
40
|
+
h4: 'font-size:1em;font-weight:600;margin:1.33em 0',
|
|
41
|
+
h5: 'font-size:.875em;font-weight:600;margin:1.67em 0',
|
|
42
|
+
h6: 'font-size:.85em;font-weight:600;margin:2em 0',
|
|
43
|
+
pre: 'background:#f4f4f4;padding:10px;border-radius:4px;overflow-x:auto;margin:1em 0',
|
|
44
|
+
code: 'background:#f0f0f0;padding:2px 4px;border-radius:3px;font-family:monospace',
|
|
45
|
+
blockquote: 'border-left:4px solid #ddd;margin-left:0;padding-left:1em',
|
|
46
|
+
table: 'border-collapse:collapse;width:100%;margin:1em 0',
|
|
47
|
+
th: 'border:1px solid #ddd;padding:8px;background-color:#f2f2f2;font-weight:bold;text-align:left',
|
|
48
|
+
td: 'border:1px solid #ddd;padding:8px;text-align:left',
|
|
49
|
+
hr: 'border:none;border-top:1px solid #ddd;margin:1em 0',
|
|
50
|
+
img: 'max-width:100%;height:auto',
|
|
51
|
+
a: 'color:#06c;text-decoration:underline',
|
|
52
|
+
strong: 'font-weight:bold',
|
|
53
|
+
em: 'font-style:italic',
|
|
54
|
+
del: 'text-decoration:line-through',
|
|
55
|
+
ul: 'margin:.5em 0;padding-left:2em',
|
|
56
|
+
ol: 'margin:.5em 0;padding-left:2em',
|
|
57
|
+
li: 'margin:.25em 0',
|
|
58
|
+
// Task list specific styles
|
|
59
|
+
'task-item': 'list-style:none',
|
|
60
|
+
'task-checkbox': 'margin-right:.5em'
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Factory function to create getAttr for a given context
|
|
64
|
+
function createGetAttr(inline_styles, styles) {
|
|
65
|
+
return function(tag, additionalStyle = '') {
|
|
68
66
|
if (inline_styles) {
|
|
69
|
-
const style = styles[tag]
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
const style = styles[tag];
|
|
68
|
+
if (!style && !additionalStyle) return '';
|
|
69
|
+
const fullStyle = additionalStyle ? (style ? `${style};${additionalStyle}` : additionalStyle) : style;
|
|
70
|
+
return ` style="${fullStyle}"`;
|
|
72
71
|
} else {
|
|
73
|
-
return ` class="
|
|
72
|
+
return ` class="${CLASS_PREFIX}${tag}"`;
|
|
74
73
|
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function quikdown(markdown, options = {}) {
|
|
78
|
+
if (!markdown || typeof markdown !== 'string') {
|
|
79
|
+
return '';
|
|
75
80
|
}
|
|
81
|
+
|
|
82
|
+
const { fence_plugin, inline_styles = false } = options;
|
|
83
|
+
const styles = QUIKDOWN_STYLES; // Use module-level styles
|
|
84
|
+
const getAttr = createGetAttr(inline_styles, styles); // Create getAttr once
|
|
76
85
|
|
|
77
86
|
// Escape HTML entities to prevent XSS
|
|
78
87
|
function escapeHtml(text) {
|
|
79
|
-
|
|
80
|
-
'&': '&',
|
|
81
|
-
'<': '<',
|
|
82
|
-
'>': '>',
|
|
83
|
-
'"': '"',
|
|
84
|
-
"'": '''
|
|
85
|
-
};
|
|
86
|
-
return text.replace(/[&<>"']/g, m => map[m]);
|
|
88
|
+
return text.replace(/[&<>"']/g, m => ESC_MAP[m]);
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
// Sanitize URLs to prevent XSS attacks
|
|
@@ -93,7 +95,6 @@
|
|
|
93
95
|
// If unsafe URLs are explicitly allowed, return as-is
|
|
94
96
|
if (allowUnsafe) return url;
|
|
95
97
|
|
|
96
|
-
// Trim and lowercase for checking
|
|
97
98
|
const trimmedUrl = url.trim();
|
|
98
99
|
const lowerUrl = trimmedUrl.toLowerCase();
|
|
99
100
|
|
|
@@ -125,7 +126,7 @@
|
|
|
125
126
|
// Match paired fences - ``` with ``` and ~~~ with ~~~
|
|
126
127
|
// Fence must be at start of line
|
|
127
128
|
html = html.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm, (match, fence, lang, code) => {
|
|
128
|
-
const placeholder =
|
|
129
|
+
const placeholder = `${PLACEHOLDER_CB}${codeBlocks.length}§`;
|
|
129
130
|
|
|
130
131
|
// Trim the language specification
|
|
131
132
|
const langTrimmed = lang ? lang.trim() : '';
|
|
@@ -149,7 +150,7 @@
|
|
|
149
150
|
|
|
150
151
|
// Extract inline code
|
|
151
152
|
html = html.replace(/`([^`]+)`/g, (match, code) => {
|
|
152
|
-
const placeholder =
|
|
153
|
+
const placeholder = `${PLACEHOLDER_IC}${inlineCodes.length}§`;
|
|
153
154
|
inlineCodes.push(escapeHtml(code));
|
|
154
155
|
return placeholder;
|
|
155
156
|
});
|
|
@@ -160,7 +161,7 @@
|
|
|
160
161
|
// Phase 2: Process block elements
|
|
161
162
|
|
|
162
163
|
// Process tables
|
|
163
|
-
html = processTable(html,
|
|
164
|
+
html = processTable(html, getAttr);
|
|
164
165
|
|
|
165
166
|
// Process headings (supports optional trailing #'s)
|
|
166
167
|
html = html.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm, (match, hashes, content) => {
|
|
@@ -177,7 +178,7 @@
|
|
|
177
178
|
html = html.replace(/^---+$/gm, `<hr${getAttr('hr')}>`);
|
|
178
179
|
|
|
179
180
|
// Process lists
|
|
180
|
-
html = processLists(html,
|
|
181
|
+
html = processLists(html, getAttr, inline_styles);
|
|
181
182
|
|
|
182
183
|
// Phase 3: Process inline elements
|
|
183
184
|
|
|
@@ -202,16 +203,18 @@
|
|
|
202
203
|
return `${prefix}<a${getAttr('a')} href="${sanitizedUrl}" rel="noopener noreferrer">${url}</a>`;
|
|
203
204
|
});
|
|
204
205
|
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
206
|
+
// Process inline formatting (bold, italic, strikethrough)
|
|
207
|
+
const inlinePatterns = [
|
|
208
|
+
[/\*\*(.+?)\*\*/g, 'strong'],
|
|
209
|
+
[/__(.+?)__/g, 'strong'],
|
|
210
|
+
[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em'],
|
|
211
|
+
[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],
|
|
212
|
+
[/~~(.+?)~~/g, 'del']
|
|
213
|
+
];
|
|
214
|
+
|
|
215
|
+
inlinePatterns.forEach(([pattern, tag]) => {
|
|
216
|
+
html = html.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);
|
|
217
|
+
});
|
|
215
218
|
|
|
216
219
|
// Line breaks (two spaces at end of line)
|
|
217
220
|
html = html.replace(/ $/gm, `<br${getAttr('br')}>`);
|
|
@@ -220,21 +223,26 @@
|
|
|
220
223
|
html = html.replace(/\n\n+/g, '</p><p>');
|
|
221
224
|
html = '<p>' + html + '</p>';
|
|
222
225
|
|
|
223
|
-
// Clean up empty paragraphs and unwrap block elements
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
226
|
+
// Clean up empty paragraphs and unwrap block elements
|
|
227
|
+
const cleanupPatterns = [
|
|
228
|
+
[/<p><\/p>/g, ''],
|
|
229
|
+
[/<p>(<h[1-6][^>]*>)/g, '$1'],
|
|
230
|
+
[/(<\/h[1-6]>)<\/p>/g, '$1'],
|
|
231
|
+
[/<p>(<blockquote[^>]*>)/g, '$1'],
|
|
232
|
+
[/(<\/blockquote>)<\/p>/g, '$1'],
|
|
233
|
+
[/<p>(<ul[^>]*>|<ol[^>]*>)/g, '$1'],
|
|
234
|
+
[/(<\/ul>|<\/ol>)<\/p>/g, '$1'],
|
|
235
|
+
[/<p>(<hr[^>]*>)<\/p>/g, '$1'],
|
|
236
|
+
[/<p>(<table[^>]*>)/g, '$1'],
|
|
237
|
+
[/(<\/table>)<\/p>/g, '$1'],
|
|
238
|
+
[/<p>(<pre[^>]*>)/g, '$1'],
|
|
239
|
+
[/(<\/pre>)<\/p>/g, '$1'],
|
|
240
|
+
[new RegExp(`<p>(${PLACEHOLDER_CB}\\d+§)<\/p>`, 'g'), '$1']
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
cleanupPatterns.forEach(([pattern, replacement]) => {
|
|
244
|
+
html = html.replace(pattern, replacement);
|
|
245
|
+
});
|
|
238
246
|
|
|
239
247
|
// Phase 4: Restore code blocks and inline code
|
|
240
248
|
|
|
@@ -258,13 +266,13 @@
|
|
|
258
266
|
replacement = `<pre${getAttr('pre')}><code${codeAttr}>${block.code}</code></pre>`;
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
const placeholder =
|
|
269
|
+
const placeholder = `${PLACEHOLDER_CB}${i}§`;
|
|
262
270
|
html = html.replace(placeholder, replacement);
|
|
263
271
|
});
|
|
264
272
|
|
|
265
273
|
// Restore inline code
|
|
266
274
|
inlineCodes.forEach((code, i) => {
|
|
267
|
-
const placeholder =
|
|
275
|
+
const placeholder = `${PLACEHOLDER_IC}${i}§`;
|
|
268
276
|
html = html.replace(placeholder, `<code${getAttr('code')}>${code}</code>`);
|
|
269
277
|
});
|
|
270
278
|
|
|
@@ -274,31 +282,21 @@
|
|
|
274
282
|
/**
|
|
275
283
|
* Process inline markdown formatting
|
|
276
284
|
*/
|
|
277
|
-
function processInlineMarkdown(text,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
// Process italic (must not match bold markers)
|
|
294
|
-
text = text.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, `<em${getAttr('em')}>$1</em>`);
|
|
295
|
-
text = text.replace(/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, `<em${getAttr('em')}>$1</em>`);
|
|
296
|
-
|
|
297
|
-
// Process strikethrough
|
|
298
|
-
text = text.replace(/~~(.+?)~~/g, `<del${getAttr('del')}>$1</del>`);
|
|
299
|
-
|
|
300
|
-
// Process inline code
|
|
301
|
-
text = text.replace(/`([^`]+)`/g, `<code${getAttr('code')}>$1</code>`);
|
|
285
|
+
function processInlineMarkdown(text, getAttr) {
|
|
286
|
+
|
|
287
|
+
// Process inline formatting patterns
|
|
288
|
+
const patterns = [
|
|
289
|
+
[/\*\*(.+?)\*\*/g, 'strong'],
|
|
290
|
+
[/__(.+?)__/g, 'strong'],
|
|
291
|
+
[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, 'em'],
|
|
292
|
+
[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, 'em'],
|
|
293
|
+
[/~~(.+?)~~/g, 'del'],
|
|
294
|
+
[/`([^`]+)`/g, 'code']
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
patterns.forEach(([pattern, tag]) => {
|
|
298
|
+
text = text.replace(pattern, `<${tag}${getAttr(tag)}>$1</${tag}>`);
|
|
299
|
+
});
|
|
302
300
|
|
|
303
301
|
return text;
|
|
304
302
|
}
|
|
@@ -306,7 +304,7 @@
|
|
|
306
304
|
/**
|
|
307
305
|
* Process markdown tables
|
|
308
306
|
*/
|
|
309
|
-
function processTable(text,
|
|
307
|
+
function processTable(text, getAttr) {
|
|
310
308
|
const lines = text.split('\n');
|
|
311
309
|
const result = [];
|
|
312
310
|
let inTable = false;
|
|
@@ -326,7 +324,7 @@
|
|
|
326
324
|
// Not a table line
|
|
327
325
|
if (inTable) {
|
|
328
326
|
// Process the accumulated table
|
|
329
|
-
const tableHtml = buildTable(tableLines,
|
|
327
|
+
const tableHtml = buildTable(tableLines, getAttr);
|
|
330
328
|
if (tableHtml) {
|
|
331
329
|
result.push(tableHtml);
|
|
332
330
|
} else {
|
|
@@ -342,7 +340,7 @@
|
|
|
342
340
|
|
|
343
341
|
// Handle table at end of text
|
|
344
342
|
if (inTable && tableLines.length > 0) {
|
|
345
|
-
const tableHtml = buildTable(tableLines,
|
|
343
|
+
const tableHtml = buildTable(tableLines, getAttr);
|
|
346
344
|
if (tableHtml) {
|
|
347
345
|
result.push(tableHtml);
|
|
348
346
|
} else {
|
|
@@ -356,17 +354,7 @@
|
|
|
356
354
|
/**
|
|
357
355
|
* Build an HTML table from markdown table lines
|
|
358
356
|
*/
|
|
359
|
-
function buildTable(lines,
|
|
360
|
-
// Helper to get attributes
|
|
361
|
-
function getAttr(tag, additionalStyle = '') {
|
|
362
|
-
if (inline_styles) {
|
|
363
|
-
const style = styles[tag] || '';
|
|
364
|
-
const fullStyle = additionalStyle ? `${style}; ${additionalStyle}` : style;
|
|
365
|
-
return fullStyle ? ` style="${fullStyle}"` : '';
|
|
366
|
-
} else {
|
|
367
|
-
return ` class="quikdown-${tag}"`;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
357
|
+
function buildTable(lines, getAttr) {
|
|
370
358
|
|
|
371
359
|
if (lines.length < 2) return null;
|
|
372
360
|
|
|
@@ -406,8 +394,8 @@
|
|
|
406
394
|
// Handle pipes at start/end or not
|
|
407
395
|
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
|
|
408
396
|
cells.forEach((cell, i) => {
|
|
409
|
-
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align
|
|
410
|
-
const processedCell = processInlineMarkdown(cell.trim(),
|
|
397
|
+
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';
|
|
398
|
+
const processedCell = processInlineMarkdown(cell.trim(), getAttr);
|
|
411
399
|
html += `<th${getAttr('th', alignStyle)}>${processedCell}</th>\n`;
|
|
412
400
|
});
|
|
413
401
|
html += '</tr>\n';
|
|
@@ -423,8 +411,8 @@
|
|
|
423
411
|
// Handle pipes at start/end or not
|
|
424
412
|
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|');
|
|
425
413
|
cells.forEach((cell, i) => {
|
|
426
|
-
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align
|
|
427
|
-
const processedCell = processInlineMarkdown(cell.trim(),
|
|
414
|
+
const alignStyle = alignments[i] && alignments[i] !== 'left' ? `text-align:${alignments[i]}` : '';
|
|
415
|
+
const processedCell = processInlineMarkdown(cell.trim(), getAttr);
|
|
428
416
|
html += `<td${getAttr('td', alignStyle)}>${processedCell}</td>\n`;
|
|
429
417
|
});
|
|
430
418
|
html += '</tr>\n';
|
|
@@ -439,17 +427,7 @@
|
|
|
439
427
|
/**
|
|
440
428
|
* Process markdown lists (ordered and unordered)
|
|
441
429
|
*/
|
|
442
|
-
function processLists(text,
|
|
443
|
-
// Helper to get attributes
|
|
444
|
-
function getAttr(tag, additionalStyle = '') {
|
|
445
|
-
if (inline_styles) {
|
|
446
|
-
const style = styles[tag] || '';
|
|
447
|
-
const fullStyle = additionalStyle ? `${style}; ${additionalStyle}` : style;
|
|
448
|
-
return fullStyle ? ` style="${fullStyle}"` : '';
|
|
449
|
-
} else {
|
|
450
|
-
return ` class="quikdown-${tag}"`;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
430
|
+
function processLists(text, getAttr, inline_styles) {
|
|
453
431
|
|
|
454
432
|
const lines = text.split('\n');
|
|
455
433
|
const result = [];
|
|
@@ -473,10 +451,10 @@
|
|
|
473
451
|
const [, checked, taskContent] = taskMatch;
|
|
474
452
|
const isChecked = checked.toLowerCase() === 'x';
|
|
475
453
|
const checkboxAttr = inline_styles
|
|
476
|
-
? ' style="margin-right
|
|
477
|
-
:
|
|
454
|
+
? ' style="margin-right:.5em"'
|
|
455
|
+
: ` class="${CLASS_PREFIX}task-checkbox"`;
|
|
478
456
|
listItemContent = `<input type="checkbox"${checkboxAttr}${isChecked ? ' checked' : ''} disabled> ${taskContent}`;
|
|
479
|
-
taskListClass = inline_styles ? ' style="list-style:
|
|
457
|
+
taskListClass = inline_styles ? ' style="list-style:none"' : ` class="${CLASS_PREFIX}task-item"`;
|
|
480
458
|
}
|
|
481
459
|
|
|
482
460
|
// Close deeper levels
|
|
@@ -524,39 +502,56 @@
|
|
|
524
502
|
|
|
525
503
|
/**
|
|
526
504
|
* Emit CSS styles for quikdown elements
|
|
505
|
+
* @param {string} prefix - Optional class prefix (default: 'quikdown-')
|
|
506
|
+
* @param {string} theme - Optional theme: 'light' (default) or 'dark'
|
|
527
507
|
* @returns {string} CSS string with quikdown styles
|
|
528
508
|
*/
|
|
529
|
-
quikdown.emitStyles = function() {
|
|
530
|
-
const styles =
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
a: 'color: #0066cc; text-decoration: underline',
|
|
546
|
-
strong: 'font-weight: bold',
|
|
547
|
-
em: 'font-style: italic',
|
|
548
|
-
del: 'text-decoration: line-through',
|
|
549
|
-
ul: 'margin: 0.5em 0; padding-left: 2em',
|
|
550
|
-
ol: 'margin: 0.5em 0; padding-left: 2em',
|
|
551
|
-
li: 'margin: 0.25em 0',
|
|
552
|
-
'task-item': 'list-style: none',
|
|
553
|
-
'task-checkbox': 'margin-right: 0.5em'
|
|
509
|
+
quikdown.emitStyles = function(prefix = 'quikdown-', theme = 'light') {
|
|
510
|
+
const styles = QUIKDOWN_STYLES;
|
|
511
|
+
|
|
512
|
+
// Define theme color overrides
|
|
513
|
+
const themeOverrides = {
|
|
514
|
+
dark: {
|
|
515
|
+
'#f4f4f4': '#2a2a2a', // pre background
|
|
516
|
+
'#f0f0f0': '#2a2a2a', // code background
|
|
517
|
+
'#f2f2f2': '#2a2a2a', // th background
|
|
518
|
+
'#ddd': '#3a3a3a', // borders
|
|
519
|
+
'#06c': '#6db3f2', // links
|
|
520
|
+
_textColor: '#e0e0e0'
|
|
521
|
+
},
|
|
522
|
+
light: {
|
|
523
|
+
_textColor: '#333' // Explicit text color for light theme
|
|
524
|
+
}
|
|
554
525
|
};
|
|
555
526
|
|
|
556
527
|
let css = '';
|
|
557
528
|
for (const [tag, style] of Object.entries(styles)) {
|
|
558
529
|
if (style) {
|
|
559
|
-
|
|
530
|
+
let themedStyle = style;
|
|
531
|
+
|
|
532
|
+
// Apply theme overrides if dark theme
|
|
533
|
+
if (theme === 'dark' && themeOverrides.dark) {
|
|
534
|
+
// Replace colors
|
|
535
|
+
for (const [oldColor, newColor] of Object.entries(themeOverrides.dark)) {
|
|
536
|
+
if (!oldColor.startsWith('_')) {
|
|
537
|
+
themedStyle = themedStyle.replace(new RegExp(oldColor, 'g'), newColor);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Add text color for certain elements in dark theme
|
|
542
|
+
const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];
|
|
543
|
+
if (needsTextColor.includes(tag)) {
|
|
544
|
+
themedStyle += `;color:${themeOverrides.dark._textColor}`;
|
|
545
|
+
}
|
|
546
|
+
} else if (theme === 'light' && themeOverrides.light) {
|
|
547
|
+
// Add explicit text color for light theme elements too
|
|
548
|
+
const needsTextColor = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'li', 'blockquote'];
|
|
549
|
+
if (needsTextColor.includes(tag)) {
|
|
550
|
+
themedStyle += `;color:${themeOverrides.light._textColor}`;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
css += `.${prefix}${tag} { ${themedStyle} }\n`;
|
|
560
555
|
}
|
|
561
556
|
}
|
|
562
557
|
|
package/dist/quikdown.umd.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
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).quikdown=t()}(this,function(){"use strict";
|
|
7
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).quikdown=t()}(this,function(){"use strict";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:s,inline_styles:c=!1}=l,a=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}"`}}(c,o);function p(e){return e.replace(/[&<>"']/g,e=>n[e])}function f(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 g=r;const d=[],u=[];g=g.replace(/^(```|~~~)([^\n]*)\n([\s\S]*?)^\1$/gm,(e,n,o,r)=>{const l=`${t}${d.length}§`,i=o?o.trim():"";return s&&"function"==typeof s?d.push({lang:i,code:r.trimEnd(),custom:!0}):d.push({lang:i,code:p(r.trimEnd()),custom:!1}),l}),g=g.replace(/`([^`]+)`/g,(e,t)=>{const n=`§IC${u.length}§`;return u.push(p(t)),n}),g=p(g),g=function(e,t){const n=e.split("\n"),o=[];let r=!1,l=[];for(let e=0;e<n.length;e++){const s=n[e].trim();if(s.includes("|")&&(s.startsWith("|")||/[^\\|]/.test(s)))r||(r=!0,l=[]),l.push(s);else{if(r){const e=i(l,t);e?o.push(e):o.push(...l),r=!1,l=[]}o.push(n[e])}}if(r&&l.length>0){const e=i(l,t);e?o.push(e):o.push(...l)}return o.join("\n")}(g,a),g=g.replace(/^(#{1,6})\s+(.+?)\s*#*$/gm,(e,t,n)=>{const o=t.length;return`<h${o}${a("h"+o)}>${n}</h${o}>`}),g=g.replace(/^>\s+(.+)$/gm,`<blockquote${a("blockquote")}>$1</blockquote>`),g=g.replace(/<\/blockquote>\n<blockquote>/g,"\n"),g=g.replace(/^---+$/gm,`<hr${a("hr")}>`),g=function(t,n,o){const r=t.split("\n"),l=[];let i=[];for(let t=0;t<r.length;t++){const s=r[t],c=s.match(/^(\s*)([*\-+]|\d+\.)\s+(.+)$/);if(c){const[,t,r,s]=c,a=Math.floor(t.length/2),p=/^\d+\./.test(r),f=p?"ol":"ul";let g=s,d="";const u=s.match(/^\[([x ])\]\s+(.*)$/i);if(u&&!p){const[,t,n]=u,r="x"===t.toLowerCase();g=`<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(;i.length>a+1;){const e=i.pop();l.push(`</${e.type}>`)}if(i.length===a)i.push({type:f,level:a}),l.push(`<${f}${n(f)}>`);else if(i.length===a+1){const e=i[i.length-1];e.type!==f&&(l.push(`</${e.type}>`),i.pop(),i.push({type:f,level:a}),l.push(`<${f}${n(f)}>`))}const h=d||n("li");l.push(`<li${h}>${g}</li>`)}else{for(;i.length>0;){const e=i.pop();l.push(`</${e.type}>`)}l.push(s)}}for(;i.length>0;){const e=i.pop();l.push(`</${e.type}>`)}return l.join("\n")}(g,a,c),g=g.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,(e,t,n)=>{const o=f(n,l.allow_unsafe_urls);return`<img${a("img")} src="${o}" alt="${t}">`}),g=g.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>{const o=f(n,l.allow_unsafe_urls),r=/^https?:\/\//i.test(o)?' rel="noopener noreferrer"':"";return`<a${a("a")} href="${o}"${r}>${t}</a>`}),g=g.replace(/(^|\s)(https?:\/\/[^\s<]+)/g,(e,t,n)=>{const o=f(n,l.allow_unsafe_urls);return`${t}<a${a("a")} href="${o}" rel="noopener noreferrer">${n}</a>`});[[/\*\*(.+?)\*\*/g,"strong"],[/__(.+?)__/g,"strong"],[/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g,"em"],[/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g,"em"],[/~~(.+?)~~/g,"del"]].forEach(([e,t])=>{g=g.replace(e,`<${t}${a(t)}>$1</${t}>`)}),g=g.replace(/ $/gm,`<br${a("br")}>`),g=g.replace(/\n\n+/g,"</p><p>"),g="<p>"+g+"</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])=>{g=g.replace(e,t)}),d.forEach((e,n)=>{let o;if(e.custom&&s){if(o=s(e.code,e.lang),void 0===o){const t=!c&&e.lang?` class="language-${e.lang}"`:"",n=c?a("code"):t;o=`<pre${a("pre")}><code${n}>${p(e.code)}</code></pre>`}}else{const t=!c&&e.lang?` class="language-${e.lang}"`:"",n=c?a("code"):t;o=`<pre${a("pre")}><code${n}>${e.code}</code></pre>`}const r=`${t}${n}§`;g=g.replace(r,o)}),u.forEach((e,t)=>{const n=`§IC${t}§`;g=g.replace(n,`<code${a("code")}>${e}</code>`)}),g.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 i(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),i=e[n].trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(e=>{const t=e.trim();return t.startsWith(":")&&t.endsWith(":")?"center":t.endsWith(":")?"right":"left"});let s=`<table${t("table")}>\n`;return o.length>0&&(s+=`<thead${t("thead")}>\n`,o.forEach(e=>{s+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const o=i[n]&&"left"!==i[n]?`text-align:${i[n]}`:"",r=l(e.trim(),t);s+=`<th${t("th",o)}>${r}</th>\n`}),s+="</tr>\n"}),s+="</thead>\n"),r.length>0&&(s+=`<tbody${t("tbody")}>\n`,r.forEach(e=>{s+=`<tr${t("tr")}>\n`;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").forEach((e,n)=>{const o=i[n]&&"left"!==i[n]?`text-align:${i[n]}`:"",r=l(e.trim(),t);s+=`<td${t("td",o)}>${r}</td>\n`}),s+="</tr>\n"}),s+="</tbody>\n"),s+="</table>",s}return 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 i="";for(const[o,s]of Object.entries(n))if(s){let n=s;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}`)}i+=`.${e}${o} { ${n} }\n`}return i},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),r});
|
|
8
8
|
//# sourceMappingURL=quikdown.umd.min.js.map
|