quasar-ui-danx 0.5.0 → 0.5.2
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/.claude/settings.local.json +8 -0
- package/dist/danx.es.js +16119 -10641
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +202 -123
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +8 -1
- package/src/components/Utility/Buttons/ActionButton.vue +15 -5
- package/src/components/Utility/Code/CodeViewer.vue +41 -16
- package/src/components/Utility/Code/CodeViewerCollapsed.vue +2 -0
- package/src/components/Utility/Code/CodeViewerFooter.vue +3 -1
- package/src/components/Utility/Code/LanguageBadge.vue +278 -5
- package/src/components/Utility/Code/MarkdownContent.vue +31 -163
- package/src/components/Utility/Code/index.ts +3 -0
- package/src/components/Utility/Markdown/ContextMenu.vue +314 -0
- package/src/components/Utility/Markdown/HotkeyHelpPopover.vue +259 -0
- package/src/components/Utility/Markdown/LineTypeMenu.vue +226 -0
- package/src/components/Utility/Markdown/LinkPopover.vue +331 -0
- package/src/components/Utility/Markdown/MarkdownEditor.vue +233 -0
- package/src/components/Utility/Markdown/MarkdownEditorContent.vue +296 -0
- package/src/components/Utility/Markdown/MarkdownEditorFooter.vue +50 -0
- package/src/components/Utility/Markdown/TablePopover.vue +420 -0
- package/src/components/Utility/Markdown/index.ts +11 -0
- package/src/components/Utility/Markdown/types.ts +27 -0
- package/src/components/Utility/Widgets/LabelPillWidget.vue +20 -0
- package/src/components/Utility/index.ts +1 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/markdown/features/useBlockquotes.spec.ts +428 -0
- package/src/composables/markdown/features/useBlockquotes.ts +248 -0
- package/src/composables/markdown/features/useCodeBlockManager.ts +369 -0
- package/src/composables/markdown/features/useCodeBlocks.spec.ts +805 -0
- package/src/composables/markdown/features/useCodeBlocks.ts +774 -0
- package/src/composables/markdown/features/useContextMenu.ts +444 -0
- package/src/composables/markdown/features/useFocusTracking.ts +116 -0
- package/src/composables/markdown/features/useHeadings.spec.ts +834 -0
- package/src/composables/markdown/features/useHeadings.ts +290 -0
- package/src/composables/markdown/features/useInlineFormatting.spec.ts +705 -0
- package/src/composables/markdown/features/useInlineFormatting.ts +402 -0
- package/src/composables/markdown/features/useLineTypeMenu.ts +285 -0
- package/src/composables/markdown/features/useLinks.spec.ts +388 -0
- package/src/composables/markdown/features/useLinks.ts +374 -0
- package/src/composables/markdown/features/useLists.spec.ts +834 -0
- package/src/composables/markdown/features/useLists.ts +747 -0
- package/src/composables/markdown/features/usePopoverManager.ts +181 -0
- package/src/composables/markdown/features/useTables.spec.ts +1601 -0
- package/src/composables/markdown/features/useTables.ts +1107 -0
- package/src/composables/markdown/index.ts +16 -0
- package/src/composables/markdown/useMarkdownEditor.spec.ts +332 -0
- package/src/composables/markdown/useMarkdownEditor.ts +1077 -0
- package/src/composables/markdown/useMarkdownHotkeys.spec.ts +791 -0
- package/src/composables/markdown/useMarkdownHotkeys.ts +266 -0
- package/src/composables/markdown/useMarkdownSelection.ts +219 -0
- package/src/composables/markdown/useMarkdownSync.ts +549 -0
- package/src/composables/useCodeFormat.ts +17 -10
- package/src/composables/useCodeViewerEditor.spec.ts +655 -0
- package/src/composables/useCodeViewerEditor.ts +174 -20
- package/src/helpers/formats/highlightCSS.ts +236 -0
- package/src/helpers/formats/highlightHTML.ts +483 -0
- package/src/helpers/formats/highlightJavaScript.ts +346 -0
- package/src/helpers/formats/highlightSyntax.ts +15 -4
- package/src/helpers/formats/index.ts +3 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/convertHeadings.ts +41 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/index.spec.ts +489 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +425 -0
- package/src/helpers/formats/markdown/index.ts +7 -0
- package/src/helpers/formats/markdown/linePatterns.spec.ts +498 -0
- package/src/helpers/formats/markdown/linePatterns.ts +172 -0
- package/src/styles/danx.scss +3 -3
- package/src/styles/index.scss +5 -5
- package/src/styles/themes/danx/code.scss +257 -1
- package/src/styles/themes/danx/index.scss +10 -10
- package/src/styles/themes/danx/markdown.scss +59 -0
- package/src/test/helpers/editorTestUtils.spec.ts +296 -0
- package/src/test/helpers/editorTestUtils.ts +253 -0
- package/src/test/helpers/index.ts +1 -0
- package/src/test/highlighters.test.ts +153 -0
- package/src/test/setup.test.ts +12 -0
- package/src/test/setup.ts +12 -0
- package/src/types/widgets.d.ts +2 -2
- package/vite.config.js +5 -1
- package/vitest.config.ts +19 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight syntax highlighting for JavaScript
|
|
3
|
+
* Returns HTML string with syntax highlighting spans
|
|
4
|
+
* Uses character-by-character tokenization for accurate parsing
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Escape HTML entities to prevent XSS
|
|
9
|
+
*/
|
|
10
|
+
function escapeHtml(text: string): string {
|
|
11
|
+
return text
|
|
12
|
+
.replace(/&/g, "&")
|
|
13
|
+
.replace(/</g, "<")
|
|
14
|
+
.replace(/>/g, ">")
|
|
15
|
+
.replace(/"/g, """)
|
|
16
|
+
.replace(/'/g, "'");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* JavaScript keywords that should be highlighted
|
|
21
|
+
*/
|
|
22
|
+
const JS_KEYWORDS = new Set([
|
|
23
|
+
// Declarations
|
|
24
|
+
"const", "let", "var", "function", "class", "extends", "static",
|
|
25
|
+
// Control flow
|
|
26
|
+
"if", "else", "switch", "case", "default", "for", "while", "do",
|
|
27
|
+
"break", "continue", "return", "throw", "try", "catch", "finally",
|
|
28
|
+
// Operators/values
|
|
29
|
+
"new", "delete", "typeof", "instanceof", "in", "of", "void",
|
|
30
|
+
// Async
|
|
31
|
+
"async", "await", "yield",
|
|
32
|
+
// Module
|
|
33
|
+
"import", "export", "from", "as",
|
|
34
|
+
// Other
|
|
35
|
+
"this", "super", "debugger", "with"
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* JavaScript built-in values
|
|
40
|
+
*/
|
|
41
|
+
const JS_BUILTINS = new Set([
|
|
42
|
+
"true", "false", "null", "undefined", "NaN", "Infinity"
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if character can start an identifier
|
|
47
|
+
*/
|
|
48
|
+
function isIdentifierStart(char: string): boolean {
|
|
49
|
+
return /[a-zA-Z_$]/.test(char);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if character can be part of an identifier
|
|
54
|
+
*/
|
|
55
|
+
function isIdentifierPart(char: string): boolean {
|
|
56
|
+
return /[a-zA-Z0-9_$]/.test(char);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if a character could precede a regex literal
|
|
61
|
+
* Regex can appear after: ( [ { , ; : ! & | = + - * / ? ~ ^ %
|
|
62
|
+
* or at the start of a statement (after newline, return, etc.)
|
|
63
|
+
*/
|
|
64
|
+
function canPrecedeRegex(lastToken: string): boolean {
|
|
65
|
+
if (!lastToken) return true;
|
|
66
|
+
const operators = ["(", "[", "{", ",", ";", ":", "!", "&", "|", "=", "+", "-", "*", "/", "?", "~", "^", "%", "<", ">"];
|
|
67
|
+
const keywords = ["return", "throw", "case", "delete", "void", "typeof", "instanceof", "in", "of", "new"];
|
|
68
|
+
return operators.includes(lastToken) || keywords.includes(lastToken);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Highlight JavaScript syntax by tokenizing character-by-character
|
|
73
|
+
*/
|
|
74
|
+
export function highlightJavaScript(code: string): string {
|
|
75
|
+
if (!code) return "";
|
|
76
|
+
|
|
77
|
+
const result: string[] = [];
|
|
78
|
+
let i = 0;
|
|
79
|
+
let lastToken = ""; // Track last significant token for regex detection
|
|
80
|
+
|
|
81
|
+
while (i < code.length) {
|
|
82
|
+
const char = code[i];
|
|
83
|
+
|
|
84
|
+
// Handle single-line comments: // ...
|
|
85
|
+
if (char === "/" && code[i + 1] === "/") {
|
|
86
|
+
const startIndex = i;
|
|
87
|
+
i += 2; // Skip //
|
|
88
|
+
|
|
89
|
+
// Find end of line
|
|
90
|
+
while (i < code.length && code[i] !== "\n") {
|
|
91
|
+
i++;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const comment = code.slice(startIndex, i);
|
|
95
|
+
result.push(`<span class="syntax-comment">${escapeHtml(comment)}</span>`);
|
|
96
|
+
lastToken = "";
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Handle multi-line comments: /* ... */
|
|
101
|
+
if (char === "/" && code[i + 1] === "*") {
|
|
102
|
+
const startIndex = i;
|
|
103
|
+
i += 2; // Skip /*
|
|
104
|
+
|
|
105
|
+
// Find closing */
|
|
106
|
+
while (i < code.length) {
|
|
107
|
+
if (code[i] === "*" && code[i + 1] === "/") {
|
|
108
|
+
i += 2; // Include */
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
i++;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const comment = code.slice(startIndex, i);
|
|
115
|
+
result.push(`<span class="syntax-comment">${escapeHtml(comment)}</span>`);
|
|
116
|
+
lastToken = "";
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handle template literals: `...`
|
|
121
|
+
if (char === "`") {
|
|
122
|
+
const startIndex = i;
|
|
123
|
+
i++; // Skip opening backtick
|
|
124
|
+
|
|
125
|
+
// Find closing backtick, handling escape sequences and ${} expressions
|
|
126
|
+
while (i < code.length) {
|
|
127
|
+
if (code[i] === "\\" && i + 1 < code.length) {
|
|
128
|
+
i += 2; // Skip escaped character
|
|
129
|
+
} else if (code[i] === "$" && code[i + 1] === "{") {
|
|
130
|
+
// Template expression - for now, just include it in the string
|
|
131
|
+
// A more sophisticated approach would recursively highlight the expression
|
|
132
|
+
let braceDepth = 1;
|
|
133
|
+
i += 2; // Skip ${
|
|
134
|
+
while (i < code.length && braceDepth > 0) {
|
|
135
|
+
if (code[i] === "{") braceDepth++;
|
|
136
|
+
else if (code[i] === "}") braceDepth--;
|
|
137
|
+
i++;
|
|
138
|
+
}
|
|
139
|
+
} else if (code[i] === "`") {
|
|
140
|
+
i++; // Include closing backtick
|
|
141
|
+
break;
|
|
142
|
+
} else {
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const str = code.slice(startIndex, i);
|
|
148
|
+
result.push(`<span class="syntax-template">${escapeHtml(str)}</span>`);
|
|
149
|
+
lastToken = "string";
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Handle strings (single or double quoted)
|
|
154
|
+
if (char === '"' || char === "'") {
|
|
155
|
+
const quoteChar = char;
|
|
156
|
+
const startIndex = i;
|
|
157
|
+
i++; // Skip opening quote
|
|
158
|
+
|
|
159
|
+
// Find closing quote, handling escape sequences
|
|
160
|
+
while (i < code.length) {
|
|
161
|
+
if (code[i] === "\\" && i + 1 < code.length) {
|
|
162
|
+
i += 2; // Skip escaped character
|
|
163
|
+
} else if (code[i] === quoteChar) {
|
|
164
|
+
i++; // Include closing quote
|
|
165
|
+
break;
|
|
166
|
+
} else if (code[i] === "\n") {
|
|
167
|
+
// Unterminated string - stop at newline
|
|
168
|
+
break;
|
|
169
|
+
} else {
|
|
170
|
+
i++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const str = code.slice(startIndex, i);
|
|
175
|
+
result.push(`<span class="syntax-string">${escapeHtml(str)}</span>`);
|
|
176
|
+
lastToken = "string";
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Handle regex literals: /pattern/flags
|
|
181
|
+
if (char === "/" && canPrecedeRegex(lastToken)) {
|
|
182
|
+
// Check if this looks like a regex (not division)
|
|
183
|
+
// A regex must be followed by at least one character that's not / or *
|
|
184
|
+
if (code[i + 1] && code[i + 1] !== "/" && code[i + 1] !== "*") {
|
|
185
|
+
const startIndex = i;
|
|
186
|
+
i++; // Skip opening /
|
|
187
|
+
|
|
188
|
+
// Find closing / handling escape sequences and character classes
|
|
189
|
+
let inCharClass = false;
|
|
190
|
+
while (i < code.length) {
|
|
191
|
+
if (code[i] === "\\" && i + 1 < code.length) {
|
|
192
|
+
i += 2; // Skip escaped character
|
|
193
|
+
} else if (code[i] === "[" && !inCharClass) {
|
|
194
|
+
inCharClass = true;
|
|
195
|
+
i++;
|
|
196
|
+
} else if (code[i] === "]" && inCharClass) {
|
|
197
|
+
inCharClass = false;
|
|
198
|
+
i++;
|
|
199
|
+
} else if (code[i] === "/" && !inCharClass) {
|
|
200
|
+
i++; // Include closing /
|
|
201
|
+
// Include flags (g, i, m, s, u, y, d)
|
|
202
|
+
while (i < code.length && /[gimsuy]/.test(code[i])) {
|
|
203
|
+
i++;
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
} else if (code[i] === "\n") {
|
|
207
|
+
// Unterminated regex - stop at newline
|
|
208
|
+
break;
|
|
209
|
+
} else {
|
|
210
|
+
i++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const regex = code.slice(startIndex, i);
|
|
215
|
+
result.push(`<span class="syntax-regex">${escapeHtml(regex)}</span>`);
|
|
216
|
+
lastToken = "regex";
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Handle numbers
|
|
222
|
+
if (/\d/.test(char) || (char === "." && /\d/.test(code[i + 1] || ""))) {
|
|
223
|
+
const startIndex = i;
|
|
224
|
+
|
|
225
|
+
// Check for hex, octal, binary
|
|
226
|
+
if (char === "0" && code[i + 1]) {
|
|
227
|
+
const next = code[i + 1].toLowerCase();
|
|
228
|
+
if (next === "x") {
|
|
229
|
+
// Hex: 0x[0-9a-f]+
|
|
230
|
+
i += 2;
|
|
231
|
+
while (i < code.length && /[0-9a-fA-F_]/.test(code[i])) i++;
|
|
232
|
+
} else if (next === "b") {
|
|
233
|
+
// Binary: 0b[01]+
|
|
234
|
+
i += 2;
|
|
235
|
+
while (i < code.length && /[01_]/.test(code[i])) i++;
|
|
236
|
+
} else if (next === "o") {
|
|
237
|
+
// Octal: 0o[0-7]+
|
|
238
|
+
i += 2;
|
|
239
|
+
while (i < code.length && /[0-7_]/.test(code[i])) i++;
|
|
240
|
+
} else {
|
|
241
|
+
// Regular number or legacy octal
|
|
242
|
+
i++;
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
i++;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Continue with decimal part
|
|
249
|
+
while (i < code.length && /[\d_]/.test(code[i])) i++;
|
|
250
|
+
|
|
251
|
+
// Decimal point
|
|
252
|
+
if (code[i] === "." && /\d/.test(code[i + 1] || "")) {
|
|
253
|
+
i++;
|
|
254
|
+
while (i < code.length && /[\d_]/.test(code[i])) i++;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Exponent
|
|
258
|
+
if ((code[i] === "e" || code[i] === "E") && /[\d+-]/.test(code[i + 1] || "")) {
|
|
259
|
+
i++;
|
|
260
|
+
if (code[i] === "+" || code[i] === "-") i++;
|
|
261
|
+
while (i < code.length && /[\d_]/.test(code[i])) i++;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// BigInt suffix
|
|
265
|
+
if (code[i] === "n") i++;
|
|
266
|
+
|
|
267
|
+
const num = code.slice(startIndex, i);
|
|
268
|
+
result.push(`<span class="syntax-number">${escapeHtml(num)}</span>`);
|
|
269
|
+
lastToken = "number";
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Handle identifiers and keywords
|
|
274
|
+
if (isIdentifierStart(char)) {
|
|
275
|
+
const startIndex = i;
|
|
276
|
+
while (i < code.length && isIdentifierPart(code[i])) {
|
|
277
|
+
i++;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const identifier = code.slice(startIndex, i);
|
|
281
|
+
|
|
282
|
+
if (JS_KEYWORDS.has(identifier)) {
|
|
283
|
+
result.push(`<span class="syntax-keyword">${escapeHtml(identifier)}</span>`);
|
|
284
|
+
lastToken = identifier;
|
|
285
|
+
} else if (JS_BUILTINS.has(identifier)) {
|
|
286
|
+
if (identifier === "true" || identifier === "false") {
|
|
287
|
+
result.push(`<span class="syntax-boolean">${escapeHtml(identifier)}</span>`);
|
|
288
|
+
} else {
|
|
289
|
+
result.push(`<span class="syntax-null">${escapeHtml(identifier)}</span>`);
|
|
290
|
+
}
|
|
291
|
+
lastToken = identifier;
|
|
292
|
+
} else {
|
|
293
|
+
// Regular identifier
|
|
294
|
+
result.push(escapeHtml(identifier));
|
|
295
|
+
lastToken = "identifier";
|
|
296
|
+
}
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Handle operators
|
|
301
|
+
const operators = ["===", "!==", "==", "!=", "<=", ">=", "&&", "||", "??",
|
|
302
|
+
"++", "--", "+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=",
|
|
303
|
+
"<<=", ">>=", ">>>=", "=>", "...", "**", "<<", ">>", ">>>",
|
|
304
|
+
"+", "-", "*", "/", "%", "&", "|", "^", "~", "!", "<", ">", "=", "?", ":"
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
let matchedOp = "";
|
|
308
|
+
for (const op of operators) {
|
|
309
|
+
if (code.slice(i, i + op.length) === op) {
|
|
310
|
+
if (op.length > matchedOp.length) {
|
|
311
|
+
matchedOp = op;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (matchedOp) {
|
|
317
|
+
result.push(`<span class="syntax-operator">${escapeHtml(matchedOp)}</span>`);
|
|
318
|
+
lastToken = matchedOp;
|
|
319
|
+
i += matchedOp.length;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Handle punctuation
|
|
324
|
+
if (/[{}()\[\];,.]/.test(char)) {
|
|
325
|
+
result.push(`<span class="syntax-punctuation">${escapeHtml(char)}</span>`);
|
|
326
|
+
lastToken = char;
|
|
327
|
+
i++;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Handle whitespace
|
|
332
|
+
if (/\s/.test(char)) {
|
|
333
|
+
result.push(escapeHtml(char));
|
|
334
|
+
// Don't reset lastToken for whitespace
|
|
335
|
+
i++;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Default: just escape and add
|
|
340
|
+
result.push(escapeHtml(char));
|
|
341
|
+
lastToken = char;
|
|
342
|
+
i++;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return result.join("");
|
|
346
|
+
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lightweight syntax highlighting for JSON and
|
|
2
|
+
* Lightweight syntax highlighting for JSON, YAML, HTML, CSS, and JavaScript
|
|
3
3
|
* Returns HTML string with syntax highlighting spans
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import { highlightCSS } from "./highlightCSS";
|
|
7
|
+
import { highlightJavaScript } from "./highlightJavaScript";
|
|
8
|
+
import { highlightHTML } from "./highlightHTML";
|
|
9
|
+
|
|
10
|
+
export type HighlightFormat = "json" | "yaml" | "text" | "markdown" | "html" | "css" | "javascript";
|
|
7
11
|
|
|
8
12
|
export interface HighlightOptions {
|
|
9
13
|
format: HighlightFormat;
|
|
@@ -25,7 +29,7 @@ function escapeHtml(text: string): string {
|
|
|
25
29
|
* Highlight JSON syntax by tokenizing first, then applying highlights
|
|
26
30
|
* This prevents issues with regex replacing content inside already-matched strings
|
|
27
31
|
*/
|
|
28
|
-
|
|
32
|
+
function highlightJSON(code: string): string {
|
|
29
33
|
if (!code) return "";
|
|
30
34
|
|
|
31
35
|
const result: string[] = [];
|
|
@@ -156,7 +160,7 @@ function getIndentLevel(line: string): number {
|
|
|
156
160
|
/**
|
|
157
161
|
* Highlight YAML syntax with multi-line string support
|
|
158
162
|
*/
|
|
159
|
-
|
|
163
|
+
function highlightYAML(code: string): string {
|
|
160
164
|
if (!code) return "";
|
|
161
165
|
|
|
162
166
|
const lines = code.split("\n");
|
|
@@ -320,7 +324,14 @@ export function highlightSyntax(code: string, options: HighlightOptions): string
|
|
|
320
324
|
return highlightJSON(code);
|
|
321
325
|
case "yaml":
|
|
322
326
|
return highlightYAML(code);
|
|
327
|
+
case "html":
|
|
328
|
+
return highlightHTML(code);
|
|
329
|
+
case "css":
|
|
330
|
+
return highlightCSS(code);
|
|
331
|
+
case "javascript":
|
|
332
|
+
return highlightJavaScript(code);
|
|
323
333
|
case "text":
|
|
334
|
+
case "markdown":
|
|
324
335
|
default:
|
|
325
336
|
return escapeHtml(code);
|
|
326
337
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML heading to markdown converter
|
|
3
|
+
* Converts h1-h6 elements to markdown # syntax
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if an element is a heading element (h1-h6)
|
|
8
|
+
*/
|
|
9
|
+
export function isHeadingElement(element: Element): boolean {
|
|
10
|
+
const tagName = element.tagName.toLowerCase();
|
|
11
|
+
return /^h[1-6]$/.test(tagName);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get the heading level from an element (1-6, or 0 if not a heading)
|
|
16
|
+
*/
|
|
17
|
+
export function getHeadingLevel(element: Element): number {
|
|
18
|
+
const tagName = element.tagName.toLowerCase();
|
|
19
|
+
const match = tagName.match(/^h([1-6])$/);
|
|
20
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert an HTML heading element to markdown
|
|
25
|
+
* @param element - The heading element (h1-h6)
|
|
26
|
+
* @returns Markdown string with appropriate # prefix
|
|
27
|
+
*/
|
|
28
|
+
export function convertHeading(element: Element): string {
|
|
29
|
+
const level = getHeadingLevel(element);
|
|
30
|
+
if (level === 0) {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const content = element.textContent?.trim() || "";
|
|
35
|
+
if (!content) {
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const prefix = "#".repeat(level);
|
|
40
|
+
return `${prefix} ${content}\n\n`;
|
|
41
|
+
}
|