Skip to content

Commit 5140542

Browse files
staredclaude
andcommitted
Reorganize file structure: move parser to utils, inline prismCustom
- Move parser.ts to utils/parser.ts for consistency with other utilities - Inline prismCustom.ts into MarkdownEditor.vue (only consumer) - Update all import paths (11 files) - Root src/ now contains only entry points (App.vue, main.ts) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 38038e3 commit 5140542

File tree

13 files changed

+217
-239
lines changed

13 files changed

+217
-239
lines changed

scripts/validate-content.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
22
// Build-time content validation script
33
import { readFileSync, readdirSync } from 'fs';
4-
import { parseContent } from '../src/parser.js';
4+
import { parseContent } from '../src/utils/parser.js';
55

66
console.log('Validating equation files...');
77

src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import { ref, computed } from 'vue'
3838
import type { ColorScheme } from './export'
3939
import { defaultScheme } from './utils/colorSchemes'
40-
import { parseContent } from './parser'
40+
import { parseContent } from './utils/parser'
4141
4242
// Components
4343
import CentralPanel from './components/CentralPanel.vue'

src/components/CentralPanel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
<script setup lang="ts">
3535
import { ref, computed, watch } from 'vue'
36-
import type { ParsedContent } from '../parser'
36+
import type { ParsedContent } from '../utils/parser'
3737
import type { ColorScheme } from '../export'
3838
import { getTermColor as getColor } from '../utils/colorSchemes'
3939

src/components/MarkdownEditor.vue

Lines changed: 206 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,214 @@
66
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
77
import { CodeJar } from 'codejar'
88
import Prism from 'prismjs'
9-
import '../prismCustom'
10-
import { applyTermColors, markErrors } from '../prismCustom'
11-
import { parseContent } from '../parser'
9+
import { parseContent } from '../utils/parser'
1210
import type { ColorScheme } from '../export'
1311
12+
// ============================================================================
13+
// Custom Prism language definition for interactive equation markdown
14+
// ============================================================================
15+
16+
Prism.languages.eqmd = {
17+
// LaTeX \mark[classname]{content}
18+
// Handles nested braces up to 2 levels deep (sufficient for \frac{\partial\psi}{\partial t})
19+
'latex-mark': {
20+
pattern: /\\mark\[[^\]]+\]\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})*\}/g,
21+
inside: {
22+
'mark-class': {
23+
pattern: /\[[^\]]+\]/,
24+
lookbehind: true,
25+
},
26+
'mark-content': {
27+
pattern: /\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})*\}/,
28+
lookbehind: true,
29+
},
30+
'keyword': /\\mark/,
31+
}
32+
},
33+
34+
// Markdown [text]{.classname}
35+
'md-ref': {
36+
pattern: /\[[^\]]+\]\{\.[^\}]+\}/g,
37+
inside: {
38+
'ref-text': {
39+
pattern: /\[[^\]]+\]/,
40+
lookbehind: true,
41+
},
42+
'ref-class': {
43+
pattern: /\{\.[^\}]+\}/,
44+
lookbehind: true,
45+
}
46+
}
47+
},
48+
49+
// Heading with class: ## .classname
50+
'heading-class': {
51+
pattern: /^##\s+\.[a-z][a-z0-9-]*/gm,
52+
inside: {
53+
'class-name': /\.[a-z][a-z0-9-]*/,
54+
'punctuation': /^##/,
55+
}
56+
},
57+
58+
// Regular markdown heading
59+
'heading': {
60+
pattern: /^##\s+[^.\n][^\n]*/gm,
61+
inside: {
62+
'punctuation': /^##/,
63+
}
64+
},
65+
66+
// LaTeX display math $$...$$
67+
'latex-display': {
68+
pattern: /\$\$[\s\S]+?\$\$/g,
69+
inside: {
70+
'punctuation': /\$\$/,
71+
}
72+
},
73+
74+
// LaTeX inline math $...$
75+
'latex-inline': {
76+
pattern: /\$[^\$\n]+\$/g,
77+
inside: {
78+
'punctuation': /\$/,
79+
}
80+
},
81+
82+
// Bold **text**
83+
'bold': {
84+
pattern: /\*\*[^*]+\*\*/g,
85+
inside: {
86+
'punctuation': /\*\*/,
87+
}
88+
},
89+
90+
// Italic *text*
91+
'italic': {
92+
pattern: /\*[^*]+\*/g,
93+
inside: {
94+
'punctuation': /\*/,
95+
}
96+
},
97+
}
98+
99+
// Extract ONLY terms from equation section ($$...$$)
100+
function extractEquationTerms(markdown: string): string[] {
101+
const terms: string[] = []
102+
const seenTerms = new Set<string>()
103+
104+
// Extract equation block (everything between $$...$$)
105+
const equationMatch = markdown.match(/\$\$([\s\S]*?)\$\$/)
106+
if (!equationMatch) return terms
107+
108+
const equationContent = equationMatch[1]
109+
110+
// Extract \mark[classname] in order of appearance
111+
const markPattern = /\\mark\[([^\]]+)\]/g
112+
let match
113+
while ((match = markPattern.exec(equationContent)) !== null) {
114+
const term = match[1]
115+
if (!seenTerms.has(term)) {
116+
terms.push(term)
117+
seenTerms.add(term)
118+
}
119+
}
120+
121+
return terms
122+
}
123+
124+
// Helper to mark tokens with errors based on a condition
125+
function markTokensWithError(
126+
element: HTMLElement,
127+
selector: string,
128+
pattern: RegExp,
129+
shouldMarkError: (term: string) => boolean
130+
) {
131+
element.querySelectorAll(selector).forEach(el => {
132+
const text = el.textContent || ''
133+
const match = text.match(pattern)
134+
if (match && shouldMarkError(match[1])) {
135+
el.classList.add('has-error')
136+
}
137+
})
138+
}
139+
140+
// Mark errors in editor with subtle underlines
141+
function markErrors(
142+
element: HTMLElement,
143+
markdown: string,
144+
errors: string[]
145+
) {
146+
// Remove any existing error markings
147+
element.querySelectorAll('.has-error').forEach(el => {
148+
el.classList.remove('has-error')
149+
})
150+
151+
// Get equation terms (source of truth)
152+
const equationTerms = new Set(extractEquationTerms(markdown))
153+
154+
// Track terms with errors
155+
const termsWithoutDefinitions = new Set<string>()
156+
157+
errors.forEach(error => {
158+
const termMatch = error.match(/Term "([^"]+)"/)
159+
if (!termMatch) return
160+
const term = termMatch[1]
161+
162+
if (error.includes('has no definition')) {
163+
termsWithoutDefinitions.add(term)
164+
}
165+
})
166+
167+
// Mark each token type with appropriate error condition
168+
markTokensWithError(element, '.token.latex-mark', /\\mark\[([^\]]+)\]/, term => termsWithoutDefinitions.has(term))
169+
markTokensWithError(element, '.token.md-ref', /\{\.([^\}]+)\}/, term => !equationTerms.has(term))
170+
markTokensWithError(element, '.token.heading-class', /##\s+\.([a-z][a-z0-9-]*)/, term => !equationTerms.has(term))
171+
}
172+
173+
// Helper to apply colors to tokens matching a pattern
174+
function applyColorToTokens(
175+
element: HTMLElement,
176+
selector: string,
177+
pattern: RegExp,
178+
termOrder: string[],
179+
colors: string[]
180+
) {
181+
element.querySelectorAll(selector).forEach(el => {
182+
const text = el.textContent || ''
183+
const match = text.match(pattern)
184+
if (match) {
185+
const className = match[1]
186+
const colorIndex = termOrder.indexOf(className)
187+
if (colorIndex >= 0 && colors[colorIndex]) {
188+
(el as HTMLElement).style.setProperty('color', colors[colorIndex], 'important')
189+
}
190+
}
191+
})
192+
}
193+
194+
// Apply term colors to highlighted code based on dynamic markdown content
195+
function applyTermColors(
196+
element: HTMLElement,
197+
markdown: string,
198+
colors: string[]
199+
) {
200+
const termOrder = extractEquationTerms(markdown)
201+
202+
// Reset all colors to default
203+
element.querySelectorAll('.token.latex-mark, .token.md-ref, .token.heading-class').forEach(el => {
204+
(el as HTMLElement).style.removeProperty('color')
205+
})
206+
207+
// Apply colors to each token type
208+
applyColorToTokens(element, '.token.latex-mark', /\\mark\[([^\]]+)\]/, termOrder, colors)
209+
applyColorToTokens(element, '.token.md-ref', /\{\.([^\}]+)\}/, termOrder, colors)
210+
applyColorToTokens(element, '.token.heading-class', /##\s+\.([a-z][a-z0-9-]*)/, termOrder, colors)
211+
}
212+
213+
// ============================================================================
214+
// Component logic
215+
// ============================================================================
216+
14217
const props = defineProps<{
15218
modelValue: string
16219
colors: ColorScheme

src/components/controls/EquationSelector.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
<script setup lang="ts">
1818
import { ref, onMounted } from 'vue'
19-
import { parseContent } from '../../parser'
19+
import { parseContent } from '../../utils/parser'
2020
2121
interface EquationInfo {
2222
id: string

src/components/controls/ExportControls.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
<script setup lang="ts">
2222
import { ref, computed } from 'vue'
23-
import { parseContent } from '../../parser'
23+
import { parseContent } from '../../utils/parser'
2424
import type { ColorScheme, ExportFormat } from '../../export'
2525
2626
const props = defineProps<{

src/export/beamerExport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Beamer export module with TikZ arrows (presentation format)
22
// Based on texample.net/tikz/examples/beamer-arrows/ pattern
33

4-
import type { ParsedContent } from '../parser';
4+
import type { ParsedContent } from '../utils/parser';
55
import type { ColorScheme } from '.';
66
import { transformHtmlClass } from '../utils/latex';
77
import { convertHtmlDescription } from './htmlConverter';

src/export/htmlExport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// HTML export module with server-side KaTeX rendering
22
// Exports standalone HTML documents with internal CSS and interactive hover
33

4-
import type { ParsedContent } from '../parser';
4+
import type { ParsedContent } from '../utils/parser';
55
import type { ColorScheme } from '.';
66
import katex from 'katex';
77
import { transformHtmlClass } from '../utils/latex';

src/export/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Export module - types, dispatcher, and re-exports
22

3-
import type { ParsedContent } from '../parser';
3+
import type { ParsedContent } from '../utils/parser';
44
import { exportToHTML } from './htmlExport';
55
import { exportToLaTeX } from './latexExport';
66
import { exportToBeamer } from './beamerExport';

src/export/latexExport.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// LaTeX export module for complete documents with xcolor
22
// Non-interactive: colors defined in preamble, applied with \textcolor
33

4-
import type { ParsedContent } from '../parser';
4+
import type { ParsedContent } from '../utils/parser';
55
import type { ColorScheme } from '.';
66
import { transformHtmlClass } from '../utils/latex';
77
import { convertHtmlDescription } from './htmlConverter';

0 commit comments

Comments
 (0)