quasar-ui-danx 0.4.99 → 0.5.0

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 (34) hide show
  1. package/dist/danx.es.js +12988 -12452
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +103 -101
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +4 -2
  7. package/scripts/publish.sh +76 -0
  8. package/src/components/Utility/Code/MarkdownContent.vue +160 -6
  9. package/src/helpers/formats/index.ts +1 -1
  10. package/src/helpers/formats/markdown/escapeHtml.ts +15 -0
  11. package/src/helpers/formats/markdown/escapeSequences.ts +60 -0
  12. package/src/helpers/formats/markdown/index.ts +85 -0
  13. package/src/helpers/formats/markdown/parseInline.ts +124 -0
  14. package/src/helpers/formats/markdown/render/index.ts +92 -0
  15. package/src/helpers/formats/markdown/render/renderFootnotes.ts +30 -0
  16. package/src/helpers/formats/markdown/render/renderList.ts +69 -0
  17. package/src/helpers/formats/markdown/render/renderTable.ts +38 -0
  18. package/src/helpers/formats/markdown/state.ts +58 -0
  19. package/src/helpers/formats/markdown/tokenize/extractDefinitions.ts +39 -0
  20. package/src/helpers/formats/markdown/tokenize/index.ts +139 -0
  21. package/src/helpers/formats/markdown/tokenize/parseBlockquote.ts +34 -0
  22. package/src/helpers/formats/markdown/tokenize/parseCodeBlock.ts +85 -0
  23. package/src/helpers/formats/markdown/tokenize/parseDefinitionList.ts +88 -0
  24. package/src/helpers/formats/markdown/tokenize/parseHeading.ts +65 -0
  25. package/src/helpers/formats/markdown/tokenize/parseHorizontalRule.ts +22 -0
  26. package/src/helpers/formats/markdown/tokenize/parseList.ts +119 -0
  27. package/src/helpers/formats/markdown/tokenize/parseParagraph.ts +59 -0
  28. package/src/helpers/formats/markdown/tokenize/parseTable.ts +70 -0
  29. package/src/helpers/formats/markdown/tokenize/parseTaskList.ts +47 -0
  30. package/src/helpers/formats/markdown/tokenize/utils.ts +25 -0
  31. package/src/helpers/formats/markdown/types.ts +63 -0
  32. package/src/styles/danx.scss +1 -0
  33. package/src/styles/themes/danx/markdown.scss +96 -0
  34. package/src/helpers/formats/renderMarkdown.ts +0 -338
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Heading parser
3
+ * Handles both ATX-style (#) and setext-style (=== or ---) headings
4
+ */
5
+
6
+ import type { ParseResult } from "../types";
7
+
8
+ /**
9
+ * Parse an ATX-style heading (# through ######)
10
+ */
11
+ export function parseAtxHeading(line: string, index: number): ParseResult | null {
12
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
13
+
14
+ if (!headingMatch) {
15
+ return null;
16
+ }
17
+
18
+ return {
19
+ token: {
20
+ type: "heading",
21
+ level: headingMatch[1].length,
22
+ content: headingMatch[2]
23
+ },
24
+ endIndex: index + 1
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Parse a setext-style heading (text followed by === or ---)
30
+ */
31
+ export function parseSetextHeading(lines: string[], index: number): ParseResult | null {
32
+ if (index + 1 >= lines.length) {
33
+ return null;
34
+ }
35
+
36
+ const trimmedLine = lines[index].trim();
37
+ const nextLine = lines[index + 1].trim();
38
+
39
+ // Level 1: ===
40
+ if (/^=+$/.test(nextLine) && trimmedLine.length > 0) {
41
+ return {
42
+ token: {
43
+ type: "heading",
44
+ level: 1,
45
+ content: trimmedLine
46
+ },
47
+ endIndex: index + 2
48
+ };
49
+ }
50
+
51
+ // Level 2: ---
52
+ // Must not be a list item (starts with - followed by space)
53
+ if (/^-+$/.test(nextLine) && trimmedLine.length > 0 && !/^[-*+]\s+/.test(trimmedLine)) {
54
+ return {
55
+ token: {
56
+ type: "heading",
57
+ level: 2,
58
+ content: trimmedLine
59
+ },
60
+ endIndex: index + 2
61
+ };
62
+ }
63
+
64
+ return null;
65
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Horizontal rule parser
3
+ * Handles ---, ***, ___ patterns
4
+ */
5
+
6
+ import type { ParseResult } from "../types";
7
+
8
+ /**
9
+ * Parse a horizontal rule
10
+ */
11
+ export function parseHorizontalRule(line: string, index: number): ParseResult | null {
12
+ const trimmedLine = line.trim();
13
+
14
+ if (!/^(-{3,}|\*{3,}|_{3,})$/.test(trimmedLine)) {
15
+ return null;
16
+ }
17
+
18
+ return {
19
+ token: { type: "hr" },
20
+ endIndex: index + 1
21
+ };
22
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Unified list parser for ordered and unordered lists
3
+ * DRY implementation handling both list types with nested content support
4
+ */
5
+
6
+ import type { BlockToken, ListItem } from "../types";
7
+ import { getIndent } from "./utils";
8
+
9
+ type ListType = "ul" | "ol";
10
+
11
+ interface ListParseResult {
12
+ tokens: BlockToken[];
13
+ endIndex: number;
14
+ }
15
+
16
+ /**
17
+ * Detect if a line is a list item and return its type and content
18
+ */
19
+ function detectListItem(trimmed: string): { type: ListType; content: string; start?: number } | null {
20
+ // Unordered: -, *, +
21
+ const ulMatch = trimmed.match(/^[-*+]\s+(.*)$/);
22
+ if (ulMatch) {
23
+ return { type: "ul", content: ulMatch[1] };
24
+ }
25
+
26
+ // Ordered: 1., 2., etc.
27
+ const olMatch = trimmed.match(/^(\d+)\.\s+(.*)$/);
28
+ if (olMatch) {
29
+ return { type: "ol", content: olMatch[2], start: parseInt(olMatch[1], 10) };
30
+ }
31
+
32
+ return null;
33
+ }
34
+
35
+ /**
36
+ * Parse a list starting at current position, supporting nested lists
37
+ * Unified implementation for both ul and ol
38
+ */
39
+ export function parseList(lines: string[], startIndex: number, baseIndent: number): ListParseResult {
40
+ const tokens: BlockToken[] = [];
41
+ let i = startIndex;
42
+
43
+ while (i < lines.length) {
44
+ const line = lines[i];
45
+ const trimmed = line.trim();
46
+ const indent = getIndent(line);
47
+
48
+ // Empty line - could be end of list or spacing within
49
+ if (!trimmed) {
50
+ i++;
51
+ continue;
52
+ }
53
+
54
+ // If indent is less than base, we've exited this list level
55
+ if (indent < baseIndent && trimmed) {
56
+ break;
57
+ }
58
+
59
+ const itemInfo = detectListItem(trimmed);
60
+
61
+ // Check if this is a list item at our indent level
62
+ if (itemInfo && indent === baseIndent) {
63
+ // Collect all items of the same type
64
+ const items: ListItem[] = [];
65
+ const listType = itemInfo.type;
66
+ const startNum = itemInfo.start || 1;
67
+
68
+ while (i < lines.length) {
69
+ const itemLine = lines[i];
70
+ const itemTrimmed = itemLine.trim();
71
+ const itemIndent = getIndent(itemLine);
72
+
73
+ if (!itemTrimmed) {
74
+ i++;
75
+ continue;
76
+ }
77
+
78
+ if (itemIndent < baseIndent) break;
79
+
80
+ const currentItem = detectListItem(itemTrimmed);
81
+
82
+ // Must be same list type and at base indent
83
+ if (currentItem && currentItem.type === listType && itemIndent === baseIndent) {
84
+ const content = currentItem.content;
85
+ i++;
86
+
87
+ // Check for nested content (items indented more than base)
88
+ const nestedIndent = baseIndent + 2;
89
+ const nested = parseList(lines, i, nestedIndent);
90
+
91
+ items.push({
92
+ content,
93
+ children: nested.tokens.length > 0 ? nested.tokens : undefined
94
+ });
95
+ i = nested.endIndex;
96
+ } else if (itemIndent > baseIndent) {
97
+ // This is nested content, let recursion handle it
98
+ break;
99
+ } else {
100
+ break;
101
+ }
102
+ }
103
+
104
+ if (items.length > 0) {
105
+ if (listType === "ul") {
106
+ tokens.push({ type: "ul", items });
107
+ } else {
108
+ tokens.push({ type: "ol", items, start: startNum });
109
+ }
110
+ }
111
+ continue;
112
+ }
113
+
114
+ // Not a list item at this level
115
+ break;
116
+ }
117
+
118
+ return { tokens, endIndex: i };
119
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Paragraph parser
3
+ * Collects consecutive non-block lines into paragraphs
4
+ */
5
+
6
+ import type { ParseResult } from "../types";
7
+
8
+ /**
9
+ * Check if a line starts a block-level element
10
+ */
11
+ function isBlockStarter(trimmed: string): boolean {
12
+ return (
13
+ trimmed.startsWith("#") ||
14
+ trimmed.startsWith("```") ||
15
+ trimmed.startsWith(">") ||
16
+ /^[-*+]\s+/.test(trimmed) ||
17
+ /^\d+\.\s+/.test(trimmed) ||
18
+ /^(-{3,}|\*{3,}|_{3,})$/.test(trimmed)
19
+ );
20
+ }
21
+
22
+ /**
23
+ * Parse a paragraph (consecutive non-empty, non-block lines)
24
+ */
25
+ export function parseParagraph(lines: string[], index: number): ParseResult | null {
26
+ const paragraphLines: string[] = [];
27
+ let i = index;
28
+
29
+ while (i < lines.length) {
30
+ const pLine = lines[i];
31
+ const pTrimmed = pLine.trim();
32
+
33
+ // Stop on empty line or block-level element
34
+ if (!pTrimmed) {
35
+ i++;
36
+ break;
37
+ }
38
+
39
+ // Check for block-level starters
40
+ if (isBlockStarter(pTrimmed)) {
41
+ break;
42
+ }
43
+
44
+ paragraphLines.push(pLine);
45
+ i++;
46
+ }
47
+
48
+ if (paragraphLines.length === 0) {
49
+ return null;
50
+ }
51
+
52
+ return {
53
+ token: {
54
+ type: "paragraph",
55
+ content: paragraphLines.join("\n")
56
+ },
57
+ endIndex: i
58
+ };
59
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Table parser
3
+ * Handles markdown tables with alignment support
4
+ */
5
+
6
+ import type { TableAlignment, ParseResult } from "../types";
7
+ import { parsePipeRow } from "./utils";
8
+
9
+ /**
10
+ * Parse a markdown table
11
+ * Format: | col | col | followed by |---|---| separator
12
+ */
13
+ export function parseTable(lines: string[], index: number): ParseResult | null {
14
+ const trimmedLine = lines[index].trim();
15
+
16
+ // Must start with | or contain |
17
+ if (!trimmedLine.startsWith("|") && !trimmedLine.includes(" | ")) {
18
+ return null;
19
+ }
20
+
21
+ // Check if next line is a separator
22
+ if (index + 1 >= lines.length) {
23
+ return null;
24
+ }
25
+
26
+ const nextLine = lines[index + 1].trim();
27
+
28
+ // Separator pattern: |---|---| or |:---|---:| etc
29
+ if (!/^\|?[\s:]*-+[\s:]*(\|[\s:]*-+[\s:]*)+\|?$/.test(nextLine)) {
30
+ return null;
31
+ }
32
+
33
+ // Parse header row
34
+ const headers = parsePipeRow(trimmedLine);
35
+
36
+ // Parse separator to get alignments
37
+ const separatorCells = parsePipeRow(nextLine);
38
+ const alignments: TableAlignment[] = separatorCells.map(cell => {
39
+ const trimmed = cell.trim();
40
+ const leftColon = trimmed.startsWith(":");
41
+ const rightColon = trimmed.endsWith(":");
42
+ if (leftColon && rightColon) return "center";
43
+ if (rightColon) return "right";
44
+ if (leftColon) return "left";
45
+ return null;
46
+ });
47
+
48
+ // Collect body rows
49
+ const rows: string[][] = [];
50
+ let i = index + 2; // Skip header and separator
51
+
52
+ while (i < lines.length) {
53
+ const rowLine = lines[i].trim();
54
+ if (!rowLine || (!rowLine.startsWith("|") && !rowLine.includes(" | "))) {
55
+ break;
56
+ }
57
+ rows.push(parsePipeRow(rowLine));
58
+ i++;
59
+ }
60
+
61
+ return {
62
+ token: {
63
+ type: "table",
64
+ headers,
65
+ alignments,
66
+ rows
67
+ },
68
+ endIndex: i
69
+ };
70
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Task list parser
3
+ * Handles - [ ] and - [x] style task items
4
+ */
5
+
6
+ import type { ParseResult } from "../types";
7
+
8
+ /**
9
+ * Parse a task list section
10
+ */
11
+ export function parseTaskList(lines: string[], index: number): ParseResult | null {
12
+ const trimmedLine = lines[index].trim();
13
+ const taskListMatch = trimmedLine.match(/^[-*+]\s+\[([ xX])\]\s+(.*)$/);
14
+
15
+ if (!taskListMatch) {
16
+ return null;
17
+ }
18
+
19
+ const items: Array<{ checked: boolean; content: string }> = [];
20
+ let i = index;
21
+
22
+ while (i < lines.length) {
23
+ const taskLine = lines[i].trim();
24
+ const itemMatch = taskLine.match(/^[-*+]\s+\[([ xX])\]\s+(.*)$/);
25
+
26
+ if (itemMatch) {
27
+ items.push({
28
+ checked: itemMatch[1].toLowerCase() === "x",
29
+ content: itemMatch[2]
30
+ });
31
+ i++;
32
+ } else if (taskLine === "") {
33
+ i++;
34
+ const nextNonEmpty = lines.slice(i).find((l) => l.trim() !== "");
35
+ if (!nextNonEmpty || !/^[-*+]\s+\[([ xX])\]/.test(nextNonEmpty.trim())) {
36
+ break;
37
+ }
38
+ } else {
39
+ break;
40
+ }
41
+ }
42
+
43
+ return {
44
+ token: { type: "task_list", items },
45
+ endIndex: i
46
+ };
47
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared utilities for block tokenizers
3
+ */
4
+
5
+ /**
6
+ * Get indentation level of a line (count leading spaces/tabs)
7
+ * Tabs are counted as 2 spaces for indentation purposes
8
+ */
9
+ export function getIndent(line: string): number {
10
+ const match = line.match(/^(\s*)/);
11
+ if (!match) return 0;
12
+ // Count tabs as 2 spaces for indentation purposes
13
+ return match[1].replace(/\t/g, " ").length;
14
+ }
15
+
16
+ /**
17
+ * Parse a pipe-delimited table row into cells
18
+ */
19
+ export function parsePipeRow(line: string): string[] {
20
+ // Remove leading/trailing pipes and split
21
+ let trimmed = line.trim();
22
+ if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
23
+ if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
24
+ return trimmed.split("|").map(cell => cell.trim());
25
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Markdown parser and renderer type definitions
3
+ */
4
+
5
+ /**
6
+ * Options for markdown rendering
7
+ */
8
+ export interface MarkdownRenderOptions {
9
+ sanitize?: boolean; // XSS protection (default: true)
10
+ preserveState?: boolean; // Don't reset link refs and footnotes (for nested rendering)
11
+ }
12
+
13
+ /**
14
+ * Link reference for reference-style links
15
+ */
16
+ export interface LinkReference {
17
+ url: string;
18
+ title?: string;
19
+ }
20
+
21
+ /**
22
+ * Footnote definition for footnote references
23
+ */
24
+ export interface FootnoteDefinition {
25
+ content: string;
26
+ index: number; // For numbered display
27
+ }
28
+
29
+ /**
30
+ * List item type supporting nested content
31
+ */
32
+ export interface ListItem {
33
+ content: string;
34
+ children?: BlockToken[]; // Nested lists or other block content
35
+ }
36
+
37
+ /**
38
+ * Token types for block-level parsing
39
+ */
40
+ export type BlockToken =
41
+ | { type: "heading"; level: number; content: string }
42
+ | { type: "code_block"; language: string; content: string }
43
+ | { type: "blockquote"; content: string }
44
+ | { type: "ul"; items: ListItem[] }
45
+ | { type: "ol"; items: ListItem[]; start: number }
46
+ | { type: "task_list"; items: Array<{ checked: boolean; content: string }> }
47
+ | { type: "table"; headers: string[]; alignments: TableAlignment[]; rows: string[][] }
48
+ | { type: "dl"; items: Array<{ term: string; definitions: string[] }> }
49
+ | { type: "hr" }
50
+ | { type: "paragraph"; content: string };
51
+
52
+ /**
53
+ * Table column alignment
54
+ */
55
+ export type TableAlignment = "left" | "center" | "right" | null;
56
+
57
+ /**
58
+ * Result from a block parser function
59
+ */
60
+ export interface ParseResult {
61
+ token: BlockToken;
62
+ endIndex: number;
63
+ }
@@ -1,5 +1,6 @@
1
1
  @import "themes/danx/scrollbar";
2
2
  @import "themes/danx/code";
3
+ @import "themes/danx/markdown";
3
4
 
4
5
  .dx-action-table {
5
6
  .dx-column-shrink {
@@ -94,6 +94,24 @@
94
94
  list-style-type: decimal;
95
95
  }
96
96
 
97
+ // Task lists
98
+ .task-list {
99
+ list-style: none;
100
+ padding-left: 0;
101
+
102
+ .task-list-item {
103
+ display: flex;
104
+ align-items: flex-start;
105
+ gap: 0.5em;
106
+ margin: 0.25em 0;
107
+
108
+ input[type="checkbox"] {
109
+ margin-top: 0.3em;
110
+ cursor: default;
111
+ }
112
+ }
113
+ }
114
+
97
115
  // Links
98
116
  a {
99
117
  color: #60a5fa; // blue-400
@@ -121,6 +139,32 @@
121
139
  strong { font-weight: 600; }
122
140
  em { font-style: italic; }
123
141
 
142
+ // Strikethrough
143
+ del {
144
+ text-decoration: line-through;
145
+ opacity: 0.7;
146
+ }
147
+
148
+ // Highlight
149
+ mark {
150
+ background: rgba(250, 204, 21, 0.4); // yellow-400 with opacity
151
+ padding: 0.1em 0.2em;
152
+ border-radius: 2px;
153
+ }
154
+
155
+ // Subscript/Superscript
156
+ sub, sup {
157
+ font-size: 0.75em;
158
+ }
159
+
160
+ sub {
161
+ vertical-align: sub;
162
+ }
163
+
164
+ sup {
165
+ vertical-align: super;
166
+ }
167
+
124
168
  // Tables
125
169
  table {
126
170
  border-collapse: collapse;
@@ -142,4 +186,56 @@
142
186
  background: rgba(255, 255, 255, 0.05);
143
187
  }
144
188
  }
189
+
190
+ // Definition lists
191
+ dl {
192
+ margin: 1em 0;
193
+ }
194
+
195
+ dt {
196
+ font-weight: 600;
197
+ margin-top: 1em;
198
+
199
+ &:first-child {
200
+ margin-top: 0;
201
+ }
202
+ }
203
+
204
+ dd {
205
+ margin-left: 2em;
206
+ margin-top: 0.25em;
207
+ }
208
+
209
+ // Footnotes
210
+ .footnote-ref {
211
+ font-size: 0.75em;
212
+ vertical-align: super;
213
+ line-height: 0;
214
+
215
+ a {
216
+ text-decoration: none;
217
+ }
218
+ }
219
+
220
+ .footnotes {
221
+ margin-top: 2em;
222
+ font-size: 0.875em;
223
+
224
+ hr {
225
+ margin-bottom: 1em;
226
+ }
227
+
228
+ .footnote-list {
229
+ padding-left: 1.5em;
230
+ }
231
+
232
+ .footnote-item {
233
+ margin: 0.5em 0;
234
+ }
235
+
236
+ .footnote-backref {
237
+ margin-left: 0.5em;
238
+ text-decoration: none;
239
+ }
240
+ }
145
241
  }