Skip to content

Commit e443f88

Browse files
authored
feat: add edit code blocks and move previous button to right for better ux (#43)
1 parent 602775f commit e443f88

2 files changed

Lines changed: 85 additions & 11 deletions

File tree

static/base.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,28 @@ td {
310310
transform: translateY(0);
311311
}
312312

313+
.pseudo-editor {
314+
width: 100%;
315+
background-color: transparent;
316+
color: var(--text-color);
317+
font-family: 'Consolas', 'Monaco', monospace;
318+
font-size: 0.95rem;
319+
line-height: 1.6;
320+
border: none;
321+
outline: none;
322+
resize: vertical;
323+
min-height: 100px;
324+
}
325+
326+
.pseudo-error {
327+
font-family: 'Consolas', 'Monaco', monospace;
328+
font-size: 0.9rem;
329+
background-color: rgba(217, 70, 62, 0.1);
330+
padding: 0.5rem;
331+
border-radius: 4px;
332+
border-left: 4px solid var(--accent-color);
333+
}
334+
313335
.pseudo-runner .indicator {
314336
color: var(--accent-color);
315337
font-weight: bold;

static/index.js

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ export function parse(input) {
2727
}
2828

2929
document.querySelectorAll('pre').forEach((pre) => {
30-
const code = pre.textContent.trim();
30+
let code = pre.textContent.trim();
3131

3232
let ast;
3333
try {
3434
ast = parse(code);
3535
} catch {
36-
return;
36+
return; // Don't process non-stepcode pre blocks
3737
}
3838

39-
const lines = code.split('\n');
39+
let lines = code.split('\n');
4040
let state = null;
4141
let running = false;
4242
let history = [];
@@ -45,21 +45,39 @@ document.querySelectorAll('pre').forEach((pre) => {
4545
wrapper.className = 'pseudo-runner';
4646
pre.replaceWith(wrapper);
4747

48+
const editor = document.createElement('textarea');
49+
editor.className = 'pseudo-editor';
50+
editor.value = code;
51+
editor.spellcheck = false;
52+
wrapper.appendChild(editor);
53+
4854
const display = document.createElement('pre');
55+
display.style.display = 'none'; // Initially hidden, shows when running
4956
wrapper.appendChild(display);
5057

5158
const controls = document.createElement('div');
5259
controls.className = 'pseudo-runner-controls';
5360
wrapper.appendChild(controls);
5461

62+
const btn = document.createElement('button');
63+
btn.textContent = '▶ Run';
64+
controls.appendChild(btn);
65+
5566
const prevBtn = document.createElement('button');
5667
prevBtn.textContent = '⏮ Previous';
5768
prevBtn.style.display = 'none';
5869
controls.appendChild(prevBtn);
5970

60-
const btn = document.createElement('button');
61-
btn.textContent = '▶ Run';
62-
controls.appendChild(btn);
71+
const editBtn = document.createElement('button');
72+
editBtn.textContent = '✏️ Edit';
73+
editBtn.style.display = 'none';
74+
controls.appendChild(editBtn);
75+
76+
const errorDisplay = document.createElement('div');
77+
errorDisplay.className = 'pseudo-error';
78+
errorDisplay.style.color = 'var(--accent-color)';
79+
errorDisplay.style.display = 'none';
80+
wrapper.appendChild(errorDisplay);
6381

6482
function formatVars(variables) {
6583
const vars = Object.entries(variables || {})
@@ -70,6 +88,17 @@ document.querySelectorAll('pre').forEach((pre) => {
7088
}
7189

7290
function render() {
91+
if (!running) {
92+
editor.style.display = 'block';
93+
display.style.display = 'none';
94+
editBtn.style.display = 'none';
95+
return;
96+
}
97+
98+
editor.style.display = 'none';
99+
display.style.display = 'block';
100+
editBtn.style.display = 'flex';
101+
73102
const isReturning = state?.variables?.['<return>'] !== undefined;
74103
const rows = [];
75104

@@ -94,14 +123,14 @@ document.querySelectorAll('pre').forEach((pre) => {
94123
if (!state) {
95124
btn.textContent = '▶ Run';
96125
running = false;
126+
render(); // switch back to editor
97127
} else if (state.nextLine === null) {
98128
const vars = formatVars(state.variables);
99129
rows.push(`<span class="indicator">&gt;&gt;&gt; ${vars}</span>`);
100130
btn.textContent = '↺ Restart';
101-
running = false;
131+
// running remains true, waiting for user to restart or edit
102132
} else {
103133
btn.textContent = '⏭ Next';
104-
running = true;
105134
}
106135

107136
prevBtn.style.display = history.length > 0 ? 'flex' : 'none';
@@ -110,9 +139,22 @@ document.querySelectorAll('pre').forEach((pre) => {
110139
}
111140

112141
btn.addEventListener('click', () => {
113-
if (!running || !state || state.nextLine === null) {
114-
state = step(ast, null);
115-
history = [];
142+
errorDisplay.style.display = 'none';
143+
if (!running || (!state && btn.textContent === '▶ Run') || (state && state.nextLine === null)) {
144+
// Start or Restart
145+
code = editor.value;
146+
lines = code.split('\n');
147+
try {
148+
ast = parse(code);
149+
state = step(ast, null);
150+
history = [];
151+
running = true;
152+
} catch (e) {
153+
errorDisplay.textContent = e.message;
154+
errorDisplay.style.display = 'block';
155+
running = false;
156+
return;
157+
}
116158
} else {
117159
history.push(state);
118160
state = step(ast, state);
@@ -127,5 +169,15 @@ document.querySelectorAll('pre').forEach((pre) => {
127169
}
128170
});
129171

172+
editBtn.addEventListener('click', () => {
173+
running = false;
174+
state = null;
175+
history = [];
176+
btn.textContent = '▶ Run';
177+
render();
178+
});
179+
180+
// initial render as editor
181+
editor.style.height = lines.length * 1.6 + 2 + 'em'; // approx height
130182
render();
131183
});

0 commit comments

Comments
 (0)