quasar-ui-danx 0.4.99 → 0.5.1

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 (90) hide show
  1. package/dist/danx.es.js +17884 -12732
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +192 -118
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +11 -2
  7. package/scripts/publish.sh +76 -0
  8. package/src/components/Utility/Code/CodeViewer.vue +31 -14
  9. package/src/components/Utility/Code/CodeViewerCollapsed.vue +2 -0
  10. package/src/components/Utility/Code/CodeViewerFooter.vue +1 -1
  11. package/src/components/Utility/Code/LanguageBadge.vue +278 -5
  12. package/src/components/Utility/Code/MarkdownContent.vue +160 -6
  13. package/src/components/Utility/Code/index.ts +3 -0
  14. package/src/components/Utility/Markdown/ContextMenu.vue +314 -0
  15. package/src/components/Utility/Markdown/HotkeyHelpPopover.vue +259 -0
  16. package/src/components/Utility/Markdown/LineTypeMenu.vue +226 -0
  17. package/src/components/Utility/Markdown/LinkPopover.vue +331 -0
  18. package/src/components/Utility/Markdown/MarkdownEditor.vue +228 -0
  19. package/src/components/Utility/Markdown/MarkdownEditorContent.vue +235 -0
  20. package/src/components/Utility/Markdown/MarkdownEditorFooter.vue +50 -0
  21. package/src/components/Utility/Markdown/TablePopover.vue +420 -0
  22. package/src/components/Utility/Markdown/index.ts +11 -0
  23. package/src/components/Utility/Markdown/types.ts +27 -0
  24. package/src/components/Utility/index.ts +1 -0
  25. package/src/composables/index.ts +1 -0
  26. package/src/composables/markdown/features/useBlockquotes.spec.ts +428 -0
  27. package/src/composables/markdown/features/useBlockquotes.ts +248 -0
  28. package/src/composables/markdown/features/useCodeBlockManager.ts +369 -0
  29. package/src/composables/markdown/features/useCodeBlocks.spec.ts +779 -0
  30. package/src/composables/markdown/features/useCodeBlocks.ts +774 -0
  31. package/src/composables/markdown/features/useContextMenu.ts +444 -0
  32. package/src/composables/markdown/features/useFocusTracking.ts +116 -0
  33. package/src/composables/markdown/features/useHeadings.spec.ts +834 -0
  34. package/src/composables/markdown/features/useHeadings.ts +290 -0
  35. package/src/composables/markdown/features/useInlineFormatting.spec.ts +705 -0
  36. package/src/composables/markdown/features/useInlineFormatting.ts +402 -0
  37. package/src/composables/markdown/features/useLineTypeMenu.ts +285 -0
  38. package/src/composables/markdown/features/useLinks.spec.ts +369 -0
  39. package/src/composables/markdown/features/useLinks.ts +374 -0
  40. package/src/composables/markdown/features/useLists.spec.ts +834 -0
  41. package/src/composables/markdown/features/useLists.ts +747 -0
  42. package/src/composables/markdown/features/usePopoverManager.ts +181 -0
  43. package/src/composables/markdown/features/useTables.spec.ts +1601 -0
  44. package/src/composables/markdown/features/useTables.ts +1107 -0
  45. package/src/composables/markdown/index.ts +16 -0
  46. package/src/composables/markdown/useMarkdownEditor.spec.ts +332 -0
  47. package/src/composables/markdown/useMarkdownEditor.ts +1068 -0
  48. package/src/composables/markdown/useMarkdownHotkeys.spec.ts +791 -0
  49. package/src/composables/markdown/useMarkdownHotkeys.ts +266 -0
  50. package/src/composables/markdown/useMarkdownSelection.ts +219 -0
  51. package/src/composables/markdown/useMarkdownSync.ts +549 -0
  52. package/src/composables/useCodeViewerEditor.spec.ts +655 -0
  53. package/src/composables/useCodeViewerEditor.ts +174 -20
  54. package/src/helpers/formats/index.ts +1 -1
  55. package/src/helpers/formats/markdown/escapeHtml.ts +15 -0
  56. package/src/helpers/formats/markdown/escapeSequences.ts +60 -0
  57. package/src/helpers/formats/markdown/htmlToMarkdown/convertHeadings.ts +41 -0
  58. package/src/helpers/formats/markdown/htmlToMarkdown/index.spec.ts +489 -0
  59. package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +412 -0
  60. package/src/helpers/formats/markdown/index.ts +92 -0
  61. package/src/helpers/formats/markdown/linePatterns.spec.ts +495 -0
  62. package/src/helpers/formats/markdown/linePatterns.ts +172 -0
  63. package/src/helpers/formats/markdown/parseInline.ts +124 -0
  64. package/src/helpers/formats/markdown/render/index.ts +92 -0
  65. package/src/helpers/formats/markdown/render/renderFootnotes.ts +30 -0
  66. package/src/helpers/formats/markdown/render/renderList.ts +69 -0
  67. package/src/helpers/formats/markdown/render/renderTable.ts +38 -0
  68. package/src/helpers/formats/markdown/state.ts +58 -0
  69. package/src/helpers/formats/markdown/tokenize/extractDefinitions.ts +39 -0
  70. package/src/helpers/formats/markdown/tokenize/index.ts +139 -0
  71. package/src/helpers/formats/markdown/tokenize/parseBlockquote.ts +34 -0
  72. package/src/helpers/formats/markdown/tokenize/parseCodeBlock.ts +85 -0
  73. package/src/helpers/formats/markdown/tokenize/parseDefinitionList.ts +88 -0
  74. package/src/helpers/formats/markdown/tokenize/parseHeading.ts +65 -0
  75. package/src/helpers/formats/markdown/tokenize/parseHorizontalRule.ts +22 -0
  76. package/src/helpers/formats/markdown/tokenize/parseList.ts +119 -0
  77. package/src/helpers/formats/markdown/tokenize/parseParagraph.ts +59 -0
  78. package/src/helpers/formats/markdown/tokenize/parseTable.ts +70 -0
  79. package/src/helpers/formats/markdown/tokenize/parseTaskList.ts +47 -0
  80. package/src/helpers/formats/markdown/tokenize/utils.ts +25 -0
  81. package/src/helpers/formats/markdown/types.ts +63 -0
  82. package/src/styles/danx.scss +1 -0
  83. package/src/styles/themes/danx/markdown.scss +96 -0
  84. package/src/test/helpers/editorTestUtils.spec.ts +296 -0
  85. package/src/test/helpers/editorTestUtils.ts +253 -0
  86. package/src/test/helpers/index.ts +1 -0
  87. package/src/test/setup.test.ts +12 -0
  88. package/src/test/setup.ts +12 -0
  89. package/vitest.config.ts +19 -0
  90. package/src/helpers/formats/renderMarkdown.ts +0 -338
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Code block parser
3
+ * Handles both fenced (```) and indented (4 spaces) code blocks
4
+ */
5
+
6
+ import type { ParseResult } from "../types";
7
+
8
+ /**
9
+ * Parse a fenced code block (``` ... ```)
10
+ */
11
+ export function parseFencedCodeBlock(lines: string[], index: number): ParseResult | null {
12
+ const trimmedLine = lines[index].trim();
13
+
14
+ if (!trimmedLine.startsWith("```")) {
15
+ return null;
16
+ }
17
+
18
+ const language = trimmedLine.slice(3).trim();
19
+ const contentLines: string[] = [];
20
+ let i = index + 1;
21
+
22
+ while (i < lines.length && !lines[i].trim().startsWith("```")) {
23
+ contentLines.push(lines[i]);
24
+ i++;
25
+ }
26
+
27
+ // Skip closing ```
28
+ if (i < lines.length) i++;
29
+
30
+ return {
31
+ token: {
32
+ type: "code_block",
33
+ language,
34
+ content: contentLines.join("\n")
35
+ },
36
+ endIndex: i
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Parse an indented code block (4+ spaces or tab at start)
42
+ */
43
+ export function parseIndentedCodeBlock(lines: string[], index: number): ParseResult | null {
44
+ const line = lines[index];
45
+
46
+ if (!/^( {4}|\t)/.test(line)) {
47
+ return null;
48
+ }
49
+
50
+ const contentLines: string[] = [];
51
+ let i = index;
52
+
53
+ while (i < lines.length) {
54
+ const codeLine = lines[i];
55
+ if (/^( {4}|\t)/.test(codeLine)) {
56
+ // Remove the 4 spaces or tab prefix
57
+ contentLines.push(codeLine.replace(/^( {4}|\t)/, ""));
58
+ i++;
59
+ } else if (codeLine.trim() === "") {
60
+ // Empty lines within indented block are kept
61
+ contentLines.push("");
62
+ i++;
63
+ } else {
64
+ break;
65
+ }
66
+ }
67
+
68
+ // Remove trailing empty lines
69
+ while (contentLines.length > 0 && contentLines[contentLines.length - 1] === "") {
70
+ contentLines.pop();
71
+ }
72
+
73
+ if (contentLines.length === 0) {
74
+ return null;
75
+ }
76
+
77
+ return {
78
+ token: {
79
+ type: "code_block",
80
+ language: "",
81
+ content: contentLines.join("\n")
82
+ },
83
+ endIndex: i
84
+ };
85
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Definition list parser
3
+ * Handles term/definition pairs with : prefix
4
+ */
5
+
6
+ import type { ParseResult } from "../types";
7
+
8
+ /**
9
+ * Parse a definition list section
10
+ * Format: Term\n: Definition
11
+ */
12
+ export function parseDefinitionList(lines: string[], index: number): ParseResult | null {
13
+ const trimmedLine = lines[index].trim();
14
+
15
+ // Check if current line could be a term (non-empty, doesn't start with special chars)
16
+ // and next line starts with `: `
17
+ if (
18
+ !trimmedLine ||
19
+ trimmedLine.startsWith(":") ||
20
+ /^[-*+#>\d]/.test(trimmedLine) ||
21
+ index + 1 >= lines.length
22
+ ) {
23
+ return null;
24
+ }
25
+
26
+ const nextLine = lines[index + 1].trim();
27
+ if (!nextLine.startsWith(": ")) {
28
+ return null;
29
+ }
30
+
31
+ // This is a definition list
32
+ const items: Array<{ term: string; definitions: string[] }> = [];
33
+ let i = index;
34
+
35
+ while (i < lines.length) {
36
+ const termLine = lines[i].trim();
37
+
38
+ // Empty line ends the definition list
39
+ if (!termLine) {
40
+ i++;
41
+ // Check if there's another term after empty line
42
+ if (i < lines.length && lines[i].trim() && !lines[i].trim().startsWith(":")) {
43
+ const afterEmpty = lines[i + 1]?.trim();
44
+ if (afterEmpty?.startsWith(": ")) {
45
+ continue; // Another term-definition pair follows
46
+ }
47
+ }
48
+ break;
49
+ }
50
+
51
+ // If line starts with :, it's a definition for previous term
52
+ if (termLine.startsWith(": ")) {
53
+ // Add to last item's definitions if exists
54
+ if (items.length > 0) {
55
+ items[items.length - 1].definitions.push(termLine.slice(2));
56
+ }
57
+ i++;
58
+ continue;
59
+ }
60
+
61
+ // Check if this could be a new term
62
+ if (!termLine.startsWith(":") && i + 1 < lines.length) {
63
+ const nextDef = lines[i + 1].trim();
64
+ if (nextDef.startsWith(": ")) {
65
+ // New term
66
+ items.push({
67
+ term: termLine,
68
+ definitions: []
69
+ });
70
+ i++;
71
+ continue;
72
+ }
73
+ }
74
+
75
+ // Not part of definition list anymore
76
+ break;
77
+ }
78
+
79
+ // Must have items with at least one definition
80
+ if (items.length === 0 || !items.some(item => item.definitions.length > 0)) {
81
+ return null;
82
+ }
83
+
84
+ return {
85
+ token: { type: "dl", items },
86
+ endIndex: i
87
+ };
88
+ }
@@ -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 {