Skip to content

Commit a4ca440

Browse files
committed
Add explainer demo page
1 parent aa5a941 commit a4ca440

File tree

4 files changed

+477
-1
lines changed

4 files changed

+477
-1
lines changed

demo/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Demo
2+
3+
This directory contains a demo HTML file that showcases the Python Friendly Error Messages library.
4+
5+
The demo shows how the library transforms common Python error traces (from both Skulpt and Pyodide runtimes) into friendly, beginner-friendly explanations. It includes examples of:
6+
7+
- **NameError**: Undefined variables
8+
- **SyntaxError**: Missing colons and syntax issues
9+
- **AttributeError**: Wrong method names (e.g., `.push()` instead of `.append()`)
10+
- **TypeError**: Type mismatches
11+
- **UnboundLocalError**: Variables used before assignment
12+
- **IndexError**: List index out of range
13+
- **KeyError**: Dictionary key not found
14+
- **ZeroDivisionError**: Division by zero
15+
16+
Each example shows:
17+
1. The original Python traceback
18+
2. The code that caused the error
19+
3. The friendly explanation generated by the library
20+
21+
## Usage
22+
23+
1. Make sure the project is built:
24+
```bash
25+
npm run build:all
26+
```
27+
28+
2. Open `index.html` in a web browser. You can:
29+
- Simply open the file directly in your browser
30+
- Or use a local web server (recommended) by executing the following command in the root directory of the project:
31+
```bash
32+
# Using Python
33+
python -m http.server 8000
34+
35+
# Using NodeJS
36+
npx http-server -p 8000
37+
```
38+
Then navigate to `http://localhost:8000/demo/`
39+

demo/index.html

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Python Friendly Error Messages - Demo</title>
8+
<link rel="stylesheet" href="styles.css">
9+
</head>
10+
11+
<body>
12+
<div class="container">
13+
<h1>Python Friendly Error Messages - Demo</h1>
14+
15+
<div id="demo-container" class="loading">
16+
Loading demo...
17+
</div>
18+
</div>
19+
20+
<script type="module">
21+
import { explain, loadCopydeck, registerAdapter, loadCopydeckFor, skulptAdapter, pyodideAdapter } from '../dist/index.browser.js';
22+
23+
// example traces with corresponding code
24+
const examples = [
25+
{
26+
title: "NameError - Undefined Variable",
27+
runtime: "skulpt",
28+
code: `print("Hello")
29+
print(kittens)`,
30+
trace: `Traceback (most recent call last):
31+
File "main.py", line 2, in <module>
32+
NameError: name 'kittens' is not defined`
33+
},
34+
{
35+
title: "SyntaxError - Missing Colon",
36+
runtime: "skulpt",
37+
code: `for i in range(3)
38+
print(i)`,
39+
trace: `Traceback (most recent call last):
40+
File "main.py", line 1
41+
for i in range(3)
42+
^
43+
SyntaxError: invalid syntax`
44+
},
45+
{
46+
title: "AttributeError - Using .push() on List",
47+
runtime: "pyodide",
48+
code: `items = []
49+
items.push(3)`,
50+
trace: `Traceback (most recent call last):
51+
File "main.py", line 2, in <module>
52+
items.push(3)
53+
AttributeError: 'list' object has no attribute 'push'`
54+
},
55+
{
56+
title: "TypeError - Adding String and Number",
57+
runtime: "pyodide",
58+
code: `age = 10
59+
message = "I am " + age + " years old"`,
60+
trace: `Traceback (most recent call last):
61+
File "main.py", line 2, in <module>
62+
message = "I am " + age + " years old"
63+
TypeError: can only concatenate str (not "int") to str`
64+
},
65+
{
66+
title: "NameError - Variable Used Before Assignment",
67+
runtime: "skulpt",
68+
code: `def calculate():
69+
result = x + 5
70+
x = 10
71+
return result
72+
73+
calculate()`,
74+
trace: `Traceback (most recent call last):
75+
File "main.py", line 5, in <module>
76+
calculate()
77+
File "main.py", line 2, in calculate
78+
UnboundLocalError: local variable 'x' referenced before assignment`
79+
},
80+
{
81+
title: "IndexError - List Index Out of Range",
82+
runtime: "pyodide",
83+
code: `numbers = [1, 2, 3]
84+
print(numbers[5])`,
85+
trace: `Traceback (most recent call last):
86+
File "main.py", line 2, in <module>
87+
print(numbers[5])
88+
IndexError: list index out of range`
89+
},
90+
{
91+
title: "KeyError - Dictionary Key Not Found",
92+
runtime: "skulpt",
93+
code: `person = {"name": "Alice", "age": 30}
94+
print(person["city"])`,
95+
trace: `Traceback (most recent call last):
96+
File "main.py", line 2, in <module>
97+
print(person["city"])
98+
KeyError: 'city'`
99+
},
100+
{
101+
title: "ZeroDivisionError - Division by Zero",
102+
runtime: "pyodide",
103+
code: `result = 10 / 0
104+
print(result)`,
105+
trace: `Traceback (most recent call last):
106+
File "main.py", line 1, in <module>
107+
result = 10 / 0
108+
ZeroDivisionError: division by zero`
109+
}
110+
];
111+
112+
async function initDemo() {
113+
const container = document.getElementById('demo-container');
114+
115+
try {
116+
await loadCopydeckFor('en');
117+
registerAdapter('skulpt', skulptAdapter);
118+
registerAdapter('pyodide', pyodideAdapter);
119+
120+
let html = '<div class="demo-grid">';
121+
122+
for (const example of examples) {
123+
try {
124+
const result = explain({
125+
error: example.trace,
126+
code: example.code,
127+
audience: 'beginner',
128+
verbosity: 'standard'
129+
});
130+
131+
html += `
132+
<div class="demo-card">
133+
<h2>
134+
${example.title}
135+
<span class="runtime-badge ${example.runtime}">${example.runtime}</span>
136+
</h2>
137+
138+
<div class="demo-card-content">
139+
<div class="trace-section">
140+
<h3>Original Trace</h3>
141+
<div class="trace-raw">${escapeHtml(example.trace)}</div>
142+
</div>
143+
144+
<div class="trace-section">
145+
<h3>Code</h3>
146+
<div class="code-example">${escapeHtml(example.code)}</div>
147+
</div>
148+
149+
<div class="explanation">
150+
<h3>Friendly Explanation</h3>
151+
${result.html}
152+
</div>
153+
</div>
154+
</div>
155+
`;
156+
} catch (err) {
157+
html += `
158+
<div class="demo-card">
159+
<h2>${example.title}</h2>
160+
<div class="error">Error processing: ${escapeHtml(err.message)}</div>
161+
</div>
162+
`;
163+
}
164+
}
165+
166+
html += '</div>';
167+
container.innerHTML = html;
168+
169+
// click handlers for collapsible cards
170+
const cards = container.querySelectorAll('.demo-card');
171+
cards.forEach(card => {
172+
const h2 = card.querySelector('h2');
173+
if (h2) {
174+
h2.addEventListener('click', () => {
175+
card.classList.toggle('expanded');
176+
});
177+
}
178+
});
179+
} catch (err) {
180+
container.innerHTML = `<div class="error">Failed to load demo: ${escapeHtml(err.message)}</div>`;
181+
}
182+
}
183+
184+
function escapeHtml(text) {
185+
const div = document.createElement('div');
186+
div.textContent = text;
187+
return div.innerHTML;
188+
}
189+
190+
initDemo();
191+
</script>
192+
</body>
193+
194+
</html>

0 commit comments

Comments
 (0)