quasar-ui-danx 0.5.1 → 0.5.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.
Files changed (38) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/dist/danx.es.js +13869 -12976
  3. package/dist/danx.es.js.map +1 -1
  4. package/dist/danx.umd.js +159 -151
  5. package/dist/danx.umd.js.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/package.json +1 -1
  8. package/src/components/Utility/Buttons/ActionButton.vue +15 -5
  9. package/src/components/Utility/Code/CodeViewer.vue +10 -2
  10. package/src/components/Utility/Code/CodeViewerFooter.vue +2 -0
  11. package/src/components/Utility/Code/MarkdownContent.vue +31 -163
  12. package/src/components/Utility/Files/FilePreview.vue +2 -2
  13. package/src/components/Utility/Markdown/MarkdownEditor.vue +7 -2
  14. package/src/components/Utility/Markdown/MarkdownEditorContent.vue +69 -8
  15. package/src/components/Utility/Widgets/LabelPillWidget.vue +20 -0
  16. package/src/composables/markdown/features/useCodeBlocks.spec.ts +59 -33
  17. package/src/composables/markdown/features/useLinks.spec.ts +29 -10
  18. package/src/composables/markdown/useMarkdownEditor.ts +16 -7
  19. package/src/composables/useCodeFormat.ts +17 -10
  20. package/src/composables/useCodeViewerCollapse.ts +7 -0
  21. package/src/composables/useFileNavigation.ts +5 -1
  22. package/src/helpers/formats/highlightCSS.ts +236 -0
  23. package/src/helpers/formats/highlightHTML.ts +483 -0
  24. package/src/helpers/formats/highlightJavaScript.ts +346 -0
  25. package/src/helpers/formats/highlightSyntax.ts +15 -4
  26. package/src/helpers/formats/index.ts +3 -0
  27. package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +42 -4
  28. package/src/helpers/formats/markdown/linePatterns.spec.ts +7 -4
  29. package/src/helpers/formats/markdown/parseInline.ts +26 -13
  30. package/src/helpers/routes.ts +3 -1
  31. package/src/styles/danx.scss +3 -3
  32. package/src/styles/index.scss +5 -5
  33. package/src/styles/themes/danx/code.scss +257 -1
  34. package/src/styles/themes/danx/index.scss +10 -10
  35. package/src/styles/themes/danx/markdown.scss +81 -0
  36. package/src/test/highlighters.test.ts +153 -0
  37. package/src/types/widgets.d.ts +2 -2
  38. package/vite.config.js +5 -1
@@ -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, "&lt;")
14
+ .replace(/>/g, "&gt;")
15
+ .replace(/"/g, "&quot;")
16
+ .replace(/'/g, "&#039;");
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 YAML
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
- export type HighlightFormat = "json" | "yaml" | "text" | "markdown";
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
- export function highlightJSON(code: string): string {
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
- export function highlightYAML(code: string): string {
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
  }
@@ -1,5 +1,8 @@
1
1
  export * from "./datetime";
2
2
  export * from "./highlightSyntax";
3
+ export * from "./highlightCSS";
4
+ export * from "./highlightJavaScript";
5
+ export * from "./highlightHTML";
3
6
  export * from "./numbers";
4
7
  export * from "./parsers";
5
8
  export * from "./markdown";
@@ -41,6 +41,19 @@ function processInlineContent(element: Element): string {
41
41
  } else if (child.nodeType === Node.ELEMENT_NODE) {
42
42
  const el = child as Element;
43
43
  const tagName = el.tagName.toLowerCase();
44
+
45
+ // Handle color-preview spans - skip the swatch, return only the hex code text
46
+ if (tagName === "span" && el.classList.contains("color-preview")) {
47
+ // Get text content directly, skipping the color-swatch span
48
+ parts.push(el.textContent || "");
49
+ continue;
50
+ }
51
+
52
+ // Skip color-swatch spans entirely (they're purely decorative)
53
+ if (tagName === "span" && el.classList.contains("color-swatch")) {
54
+ continue;
55
+ }
56
+
44
57
  const content = processInlineContent(el);
45
58
 
46
59
  // Skip empty formatting elements
@@ -364,11 +377,36 @@ function processNode(node: Node): string {
364
377
  break;
365
378
  }
366
379
 
367
- // Divs and other containers - just process children
368
- case "div":
369
- case "span":
370
- parts.push(processNode(element));
380
+ // Divs - check for code block wrapper first
381
+ case "div": {
382
+ // Handle code block wrapper structure
383
+ if (element.hasAttribute("data-code-block-id")) {
384
+ const mountPoint = element.querySelector(".code-viewer-mount-point");
385
+ const content = mountPoint?.getAttribute("data-content") || "";
386
+ const language = mountPoint?.getAttribute("data-language") || "";
387
+ parts.push(`\`\`\`${language}\n${content}\n\`\`\`\n\n`);
388
+ } else {
389
+ parts.push(processNode(element));
390
+ }
371
391
  break;
392
+ }
393
+
394
+ // Spans - handle special cases first
395
+ case "span": {
396
+ // Color preview: return only the hex color text
397
+ if (element.classList.contains("color-preview")) {
398
+ parts.push(element.textContent || "");
399
+ }
400
+ // Color swatch: skip entirely (purely decorative)
401
+ else if (element.classList.contains("color-swatch")) {
402
+ // Skip - don't output anything
403
+ }
404
+ // Default: process children
405
+ else {
406
+ parts.push(processNode(element));
407
+ }
408
+ break;
409
+ }
372
410
 
373
411
  // Tables
374
412
  case "table":
@@ -204,8 +204,10 @@ describe('linePatterns', () => {
204
204
  });
205
205
 
206
206
  describe('detectCodeFenceStart', () => {
207
- it('detects code fence without language', () => {
208
- expect(detectCodeFenceStart('```')).toEqual({ language: '' });
207
+ it('returns null for code fence without language (requires language identifier)', () => {
208
+ // Implementation intentionally requires at least one character in language identifier
209
+ // to avoid triggering on just "```" before user finishes typing the language
210
+ expect(detectCodeFenceStart('```')).toBeNull();
209
211
  });
210
212
 
211
213
  it('detects code fence with javascript language', () => {
@@ -426,8 +428,9 @@ describe('linePatterns', () => {
426
428
  });
427
429
 
428
430
  describe('code block patterns', () => {
429
- it('detects code block without language', () => {
430
- expect(detectLinePattern('```')).toEqual({ type: 'code-block', language: '' });
431
+ it('returns null for code block without language (requires language identifier)', () => {
432
+ // Implementation intentionally requires language identifier
433
+ expect(detectLinePattern('```')).toBeNull();
431
434
  });
432
435
 
433
436
  it('detects code block with language', () => {
@@ -28,13 +28,26 @@ export function parseInline(text: string, sanitize: boolean = true): string {
28
28
  // 2. HARD LINE BREAKS - Two trailing spaces + newline becomes <br />
29
29
  result = result.replace(/ {2,}\n/g, "<br />\n");
30
30
 
31
- // 3. AUTOLINKS - Must be before regular link parsing
31
+ // 3. HEX COLOR PREVIEW - Display color swatch before hex color codes
32
+ // Match 6-digit hex colors first (more specific), then 3-digit
33
+ // Use word boundary to avoid matching inside URLs or other contexts
34
+ // The pattern must NOT match 4, 5, 7+ digit hex codes
35
+ result = result.replace(
36
+ /(?<![&\w])#([0-9a-fA-F]{6})(?![0-9a-fA-F])/g,
37
+ '<span class="color-preview"><span class="color-swatch" style="background-color: #$1"></span>#$1</span>'
38
+ );
39
+ result = result.replace(
40
+ /(?<![&\w])#([0-9a-fA-F]{3})(?![0-9a-fA-F])/g,
41
+ '<span class="color-preview"><span class="color-swatch" style="background-color: #$1"></span>#$1</span>'
42
+ );
43
+
44
+ // 4. AUTOLINKS - Must be before regular link parsing
32
45
  // URL autolinks: <https://example.com>
33
46
  result = result.replace(/&lt;(https?:\/\/[^&]+)&gt;/g, '<a href="$1">$1</a>');
34
47
  // Email autolinks: <user@example.com>
35
48
  result = result.replace(/&lt;([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})&gt;/g, '<a href="mailto:$1">$1</a>');
36
49
 
37
- // 4. FOOTNOTE REFERENCES: [^id]
50
+ // 5. FOOTNOTE REFERENCES: [^id]
38
51
  // Must be before regular link/image parsing to avoid conflicts
39
52
  result = result.replace(/\[\^([^\]]+)\]/g, (match, fnId) => {
40
53
  const fn = currentFootnotes[fnId];
@@ -44,19 +57,19 @@ export function parseInline(text: string, sanitize: boolean = true): string {
44
57
  return match; // Keep original if footnote not defined
45
58
  });
46
59
 
47
- // 5. IMAGES: ![alt](url) - must be before links
60
+ // 6. IMAGES: ![alt](url) - must be before links
48
61
  result = result.replace(
49
62
  /!\[([^\]]*)\]\(([^)]+)\)/g,
50
63
  '<img src="$2" alt="$1" />'
51
64
  );
52
65
 
53
- // 6. INLINE LINKS: [text](url)
66
+ // 7. INLINE LINKS: [text](url)
54
67
  result = result.replace(
55
68
  /\[([^\]]+)\]\(([^)]+)\)/g,
56
69
  '<a href="$2">$1</a>'
57
70
  );
58
71
 
59
- // 7. REFERENCE-STYLE LINKS - Process after regular links
72
+ // 8. REFERENCE-STYLE LINKS - Process after regular links
60
73
  // Full reference: [text][ref-id]
61
74
  result = result.replace(/\[([^\]]+)\]\[([^\]]+)\]/g, (match, text, refId) => {
62
75
  const ref = currentLinkRefs[refId.toLowerCase()];
@@ -88,30 +101,30 @@ export function parseInline(text: string, sanitize: boolean = true): string {
88
101
  return match;
89
102
  });
90
103
 
91
- // 8. INLINE CODE: `code`
104
+ // 9. INLINE CODE: `code`
92
105
  result = result.replace(/`([^`]+)`/g, "<code>$1</code>");
93
106
 
94
- // 9. STRIKETHROUGH: ~~text~~ - Must be before subscript (single tilde)
107
+ // 10. STRIKETHROUGH: ~~text~~ - Must be before subscript (single tilde)
95
108
  result = result.replace(/~~([^~]+)~~/g, "<del>$1</del>");
96
109
 
97
- // 10. HIGHLIGHT: ==text==
110
+ // 11. HIGHLIGHT: ==text==
98
111
  result = result.replace(/==([^=]+)==/g, "<mark>$1</mark>");
99
112
 
100
- // 11. SUPERSCRIPT: X^2^ - Must be before subscript
113
+ // 12. SUPERSCRIPT: X^2^ - Must be before subscript
101
114
  result = result.replace(/\^([^\^]+)\^/g, "<sup>$1</sup>");
102
115
 
103
- // 12. SUBSCRIPT: H~2~O - Single tilde, use negative lookbehind/lookahead to avoid ~~
116
+ // 13. SUBSCRIPT: H~2~O - Single tilde, use negative lookbehind/lookahead to avoid ~~
104
117
  result = result.replace(/(?<!~)~([^~]+)~(?!~)/g, "<sub>$1</sub>");
105
118
 
106
- // 13. BOLD + ITALIC: ***text*** or ___text___
119
+ // 14. BOLD + ITALIC: ***text*** or ___text___
107
120
  result = result.replace(/\*\*\*([^*]+)\*\*\*/g, "<strong><em>$1</em></strong>");
108
121
  result = result.replace(/___([^_]+)___/g, "<strong><em>$1</em></strong>");
109
122
 
110
- // 14. BOLD: **text** or __text__
123
+ // 15. BOLD: **text** or __text__
111
124
  result = result.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
112
125
  result = result.replace(/__([^_]+)__/g, "<strong>$1</strong>");
113
126
 
114
- // 15. ITALIC: *text* or _text_ (but not inside words for underscores)
127
+ // 16. ITALIC: *text* or _text_ (but not inside words for underscores)
115
128
  // For asterisks, match any single asterisk pairs
116
129
  result = result.replace(/\*([^*]+)\*/g, "<em>$1</em>");
117
130
  // For underscores, only match at word boundaries
@@ -40,7 +40,9 @@ export function useActionRoutes(baseUrl: string, extend?: object): ListControlsR
40
40
  ...options,
41
41
  ignoreAbort: true
42
42
  };
43
- fields && (options.params = { fields });
43
+ if (fields) {
44
+ options.params = { ...options.params, fields };
45
+ }
44
46
  const item = await request.get(`${baseUrl}/${target.id}/details`, options);
45
47
  return storeObject(item);
46
48
  },
@@ -1,6 +1,6 @@
1
- @import "themes/danx/scrollbar";
2
- @import "themes/danx/code";
3
- @import "themes/danx/markdown";
1
+ @use "themes/danx/scrollbar";
2
+ @use "themes/danx/code";
3
+ @use "themes/danx/markdown";
4
4
 
5
5
  .dx-action-table {
6
6
  .dx-column-shrink {
@@ -1,8 +1,8 @@
1
+ @use "quasar-reset";
2
+ @use "general";
3
+ @use "transitions";
4
+ @use "danx";
5
+
1
6
  @tailwind base;
2
7
  @tailwind components;
3
8
  @tailwind utilities;
4
-
5
- @import "quasar-reset";
6
- @import "general";
7
- @import "transitions";
8
- @import "danx";