Skip to content

Commit c8c0549

Browse files
committed
refactor: migrate to Vite + TypeScript with enhanced features
- Replace CDN dependencies with npm packages (mermaid, html2canvas) - Add TypeScript for type safety - Add resizable preview area with drag handle - Add 300 DPI high-resolution PNG export - Add detailed error display for syntax errors - Add GitHub Actions workflow for automatic deployment - Add toast notifications for user feedback 🤖 Generated with [Qoder][https://qoder.com]
1 parent a77ddeb commit c8c0549

11 files changed

Lines changed: 3274 additions & 226 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Deploy to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
pull_request:
7+
branches: ['main']
8+
9+
permissions:
10+
contents: read
11+
pages: write
12+
id-token: write
13+
14+
concurrency:
15+
group: 'pages'
16+
cancel-in-progress: true
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Setup Node
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: '20'
29+
cache: 'npm'
30+
31+
- name: Install dependencies
32+
run: npm ci
33+
34+
- name: Build
35+
run: npm run build
36+
37+
- name: Upload artifact
38+
uses: actions/upload-pages-artifact@v3
39+
with:
40+
path: ./dist
41+
42+
deploy:
43+
environment:
44+
name: github-pages
45+
url: ${{ steps.deployment.outputs.page_url }}
46+
runs-on: ubuntu-latest
47+
needs: build
48+
steps:
49+
- name: Deploy to GitHub Pages
50+
id: deployment
51+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Dependencies
2+
node_modules/
3+
4+
# Build output
5+
dist/
6+
7+
# IDE
8+
.idea/
9+
.vscode/
10+
*.swp
11+
*.swo
12+
13+
# OS
14+
.DS_Store
15+
Thumbs.db
16+
17+
# Logs
18+
*.log
19+
npm-debug.log*
20+
21+
# Environment
22+
.env
23+
.env.local
24+
.env.*.local
25+
26+
# Cache
27+
.cache/
28+
.parcel-cache/
29+
30+
# TypeScript
31+
*.tsbuildinfo

index.html

Lines changed: 36 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -4,247 +4,57 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Mermaid Parser</title>
7-
<style>
8-
* {
9-
box-sizing: border-box;
10-
}
11-
12-
body {
13-
margin: 0;
14-
padding: 20px;
15-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16-
background: #f5f5f5;
17-
min-height: 100vh;
18-
}
19-
20-
.container {
21-
max-width: 900px;
22-
margin: 0 auto;
23-
}
24-
25-
.input-section {
26-
margin-bottom: 20px;
27-
}
28-
29-
.textarea-wrapper {
30-
display: flex;
31-
gap: 10px;
32-
align-items: flex-start;
33-
}
34-
35-
textarea {
36-
flex: 1;
37-
min-height: 150px;
38-
padding: 12px;
39-
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
40-
font-size: 14px;
41-
border: 1px solid #ddd;
42-
border-radius: 6px;
43-
resize: vertical;
44-
}
45-
46-
textarea:focus {
47-
outline: none;
48-
border-color: #4a90d9;
49-
}
50-
51-
.preview-section {
52-
background: white;
53-
border: 1px solid #ddd;
54-
border-radius: 6px;
55-
min-height: 200px;
56-
padding: 20px;
57-
display: flex;
58-
justify-content: center;
59-
align-items: center;
60-
position: relative;
61-
}
62-
63-
.preview-section .mermaid {
64-
background: white;
65-
}
66-
67-
.preview-section.error {
68-
color: #d32f2f;
69-
font-size: 14px;
70-
}
71-
72-
.btn {
73-
padding: 10px 20px;
74-
font-size: 14px;
75-
border: none;
76-
border-radius: 6px;
77-
cursor: pointer;
78-
transition: background 0.2s;
79-
}
80-
81-
.btn-primary {
82-
background: #4a90d9;
83-
color: white;
84-
}
85-
86-
.btn-primary:hover {
87-
background: #3a7bc8;
88-
}
89-
90-
.btn-secondary {
91-
background: #6c757d;
92-
color: white;
93-
}
94-
95-
.btn-secondary:hover {
96-
background: #5a6268;
97-
}
98-
99-
.btn:disabled {
100-
opacity: 0.5;
101-
cursor: not-allowed;
102-
}
103-
104-
.actions {
105-
display: flex;
106-
justify-content: flex-end;
107-
margin-top: 10px;
108-
}
109-
110-
.placeholder-text {
111-
color: #999;
112-
text-align: center;
113-
}
114-
</style>
1157
</head>
1168
<body>
1179
<div class="container">
11810
<div class="input-section">
11911
<div class="textarea-wrapper">
12012
<textarea id="input" placeholder="Paste your Mermaid syntax here..."></textarea>
121-
<button class="btn btn-primary" id="copyText">Copy Text</button>
13+
<button class="btn btn-primary" id="copyText">
14+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
15+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
16+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
17+
</svg>
18+
Copy Text
19+
</button>
12220
</div>
12321
</div>
12422

125-
<div class="preview-section" id="preview">
126-
<div id="diagram" class="mermaid"></div>
127-
<span class="placeholder-text" id="placeholder">Diagram preview will appear here</span>
23+
<div class="preview-wrapper">
24+
<div class="resize-handle" id="resizeHandle" title="Drag to resize"></div>
25+
<div class="preview-section" id="preview">
26+
<div id="diagram"></div>
27+
<span class="placeholder-text" id="placeholder">Diagram preview will appear here</span>
28+
</div>
29+
</div>
30+
31+
<div id="errorPanel" class="error-panel">
32+
<div class="error-title">
33+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
34+
<circle cx="12" cy="12" r="10"></circle>
35+
<line x1="12" y1="8" x2="12" y2="12"></line>
36+
<line x1="12" y1="16" x2="12.01" y2="16"></line>
37+
</svg>
38+
Syntax Error
39+
</div>
40+
<pre class="error-content" id="errorContent"></pre>
41+
<div class="error-location" id="errorLocation"></div>
12842
</div>
12943

13044
<div class="actions">
131-
<button class="btn btn-secondary" id="copyImage">Copy Image</button>
45+
<button class="btn btn-secondary" id="copyImage">
46+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
47+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
48+
<circle cx="8.5" cy="8.5" r="1.5"></circle>
49+
<polyline points="21 15 16 10 5 21"></polyline>
50+
</svg>
51+
Copy Image (300 DPI)
52+
</button>
13253
</div>
13354
</div>
13455

135-
<script src="https://cdn.jsdelivr.net/npm/mermaid@11.14.0/dist/mermaid.min.js"></script>
136-
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
137-
138-
<script>
139-
mermaid.initialize({
140-
startOnLoad: false,
141-
securityLevel: 'loose',
142-
theme: 'default'
143-
});
144-
145-
const input = document.getElementById('input');
146-
const preview = document.getElementById('preview');
147-
const diagram = document.getElementById('diagram');
148-
const placeholder = document.getElementById('placeholder');
149-
const copyTextBtn = document.getElementById('copyText');
150-
const copyImageBtn = document.getElementById('copyImage');
151-
152-
let debounceTimer;
153-
154-
async function render() {
155-
const code = input.value.trim();
156-
157-
if (!code) {
158-
diagram.innerHTML = '';
159-
diagram.classList.remove('mermaid');
160-
placeholder.style.display = 'block';
161-
preview.classList.remove('error');
162-
return;
163-
}
164-
165-
placeholder.style.display = 'none';
166-
diagram.classList.add('mermaid');
167-
168-
try {
169-
const id = 'mermaid-' + Date.now();
170-
const { svg } = await mermaid.render(id, code);
171-
diagram.innerHTML = svg;
172-
preview.classList.remove('error');
173-
} catch (error) {
174-
diagram.innerHTML = '';
175-
diagram.classList.remove('mermaid');
176-
preview.classList.add('error');
177-
placeholder.style.display = 'block';
178-
placeholder.textContent = 'Syntax error: ' + error.message;
179-
}
180-
}
181-
182-
input.addEventListener('input', () => {
183-
clearTimeout(debounceTimer);
184-
debounceTimer = setTimeout(render, 300);
185-
});
186-
187-
copyTextBtn.addEventListener('click', async () => {
188-
const text = input.value;
189-
if (!text) return;
190-
191-
try {
192-
await navigator.clipboard.writeText(text);
193-
copyTextBtn.textContent = 'Copied!';
194-
setTimeout(() => {
195-
copyTextBtn.textContent = 'Copy Text';
196-
}, 1500);
197-
} catch (err) {
198-
console.error('Failed to copy text:', err);
199-
}
200-
});
201-
202-
copyImageBtn.addEventListener('click', async () => {
203-
const svgElement = diagram.querySelector('svg');
204-
if (!svgElement) {
205-
alert('No diagram to copy. Please render a diagram first.');
206-
return;
207-
}
208-
209-
copyImageBtn.disabled = true;
210-
copyImageBtn.textContent = 'Processing...';
211-
212-
try {
213-
const canvas = await html2canvas(diagram, {
214-
backgroundColor: '#ffffff',
215-
scale: 2
216-
});
217-
218-
canvas.toBlob(async (blob) => {
219-
if (blob) {
220-
try {
221-
await navigator.clipboard.write([
222-
new ClipboardItem({ 'image/png': blob })
223-
]);
224-
copyImageBtn.textContent = 'Copied!';
225-
} catch (err) {
226-
const url = URL.createObjectURL(blob);
227-
const a = document.createElement('a');
228-
a.href = url;
229-
a.download = 'diagram.png';
230-
a.click();
231-
URL.revokeObjectURL(url);
232-
copyImageBtn.textContent = 'Downloaded!';
233-
}
234-
}
235-
setTimeout(() => {
236-
copyImageBtn.textContent = 'Copy Image';
237-
copyImageBtn.disabled = false;
238-
}, 1500);
239-
}, 'image/png');
240-
} catch (err) {
241-
console.error('Failed to copy image:', err);
242-
copyImageBtn.textContent = 'Copy Image';
243-
copyImageBtn.disabled = false;
244-
}
245-
});
56+
<div id="toast" class="toast"></div>
24657

247-
render();
248-
</script>
58+
<script type="module" src="/src/main.ts"></script>
24959
</body>
25060
</html>

0 commit comments

Comments
 (0)