Skip to content

Commit bbc59c1

Browse files
omiqcursoragent
andauthored
Feature/rgc basic html tutorial 8fd6 (#16)
* Add browser getting-started tutorial with eight WASM embeds Introduce web/tutorial.html with incremental lessons (hello, PRINT, variables, INPUT, IF, FOR, REM, playground) using RgcBasicTutorialEmbed. Extend wasm_tutorial_embed_test to require tutorial.html and smoke-test the first lesson after Run. Co-authored-by: Chris Garrett <chris@chrisg.com> * Document web/tutorial.html in README and tutorial-embedding guide Co-authored-by: Chris Garrett <chris@chrisg.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 0d59ec5 commit bbc59c1

4 files changed

Lines changed: 260 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ make basic-wasm-modular
515515
make basic-wasm-canvas
516516
```
517517

518-
Produces `web/basic.js` and `web/basic.wasm` (Asyncify-enabled), **`web/basic-modular.js`** / **`web/basic-modular.wasm`** for **`tutorial-embed.js`**, and optionally `web/basic-canvas.js` / `web/basic-canvas.wasm` plus **`web/canvas.html`** for a Canvas 2D PETSCII screen. Serve `web/` over HTTP (e.g. `cd web && python3 -m http.server 8080`) and open in a browser. The terminal demo uses an inline **INPUT** field and keyboard routing for **GET** / **INKEY$**; see `web/README.md` for details.
518+
Produces `web/basic.js` and `web/basic.wasm` (Asyncify-enabled), **`web/basic-modular.js`** / **`web/basic-modular.wasm`** for **`tutorial-embed.js`**, and optionally `web/basic-canvas.js` / `web/basic-canvas.wasm` plus **`web/canvas.html`** for a Canvas 2D PETSCII screen. After **`make basic-wasm-modular`**, open **`web/tutorial.html`** for a step-by-step **getting started** page with multiple embedded interpreters (see also **`docs/tutorial-embedding.md`**). Serve `web/` over HTTP (e.g. `cd web && python3 -m http.server 8080`) and open in a browser. The terminal demo uses an inline **INPUT** field and keyboard routing for **GET** / **INKEY$**; see `web/README.md` for details.
519519

520520
**PETSCII canvas** (no Raylib): `make basic-wasm-canvas` produces `web/basic-canvas.js`, `web/basic-canvas.wasm`, and `web/canvas.html`. The page refreshes the canvas during `SLEEP` and loops via a shared RGBA framebuffer (PETSCII, `SCREEN 1` bitmap, and **`LOADSPRITE`/`DRAWSPRITE`** like **basic-gfx**). Try **`examples/gfx_canvas_demo.bas`**: paste into the canvas page, use **Upload to VFS** to add **`gfx_canvas_demo.png`** (same file is copied to **`web/gfx_canvas_demo.png`** for local fetch tests), then Run.
521521

docs/tutorial-embedding.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This produces:
1919
| `web/tutorial-embed.js` | Helper that mounts UI + one WASM instance per container |
2020
| `web/vfs-helpers.js` | Shared **upload/download** helpers for `Module.FS` (loaded automatically by tutorial embeds) |
2121
| `web/tutorial-example.html` | Minimal page with **two** embeds (smoke test reference) |
22+
| `web/tutorial.html` | **Getting started** walkthrough with **eight** lesson embeds (serve `web/` over HTTP) |
2223

2324
Serve the `web/` directory over **HTTP** (WASM is blocked on many browsers for `file://`).
2425

tests/wasm_tutorial_embed_test.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ def run() -> None:
2727

2828

2929
def main() -> int:
30-
for name in ("basic-modular.js", "basic-modular.wasm", "tutorial-embed.js", "tutorial-example.html"):
30+
for name in (
31+
"basic-modular.js",
32+
"basic-modular.wasm",
33+
"tutorial-embed.js",
34+
"tutorial-example.html",
35+
"tutorial.html",
36+
):
3137
if not (WEB / name).is_file():
3238
print(f"error: web/{name} not found; run: make basic-wasm-modular", file=sys.stderr)
3339
return 1
@@ -83,6 +89,36 @@ def main() -> int:
8389
timeout=120000,
8490
)
8591
browser.close()
92+
93+
# Full getting-started page: eight independent embeds
94+
with sync_playwright() as p:
95+
browser = p.chromium.launch(headless=True)
96+
page = browser.new_page(viewport={"width": 1100, "height": 900})
97+
page.goto(
98+
f"http://127.0.0.1:{port}/tutorial.html",
99+
wait_until="networkidle",
100+
timeout=120000,
101+
)
102+
page.wait_for_function(
103+
"() => document.querySelectorAll('.rgc-tutorial-embed').length === 8",
104+
timeout=120000,
105+
)
106+
page.wait_for_function(
107+
"""() => {
108+
const b = document.querySelector('#tut-hello .rgc-tutorial-toolbar button');
109+
return b && !b.disabled && b.textContent === 'Run';
110+
}""",
111+
timeout=120000,
112+
)
113+
page.evaluate(
114+
"document.querySelector('#tut-hello .rgc-tutorial-toolbar button').click()"
115+
)
116+
page.wait_for_function(
117+
"() => (document.querySelector('#tut-hello .rgc-tutorial-output').textContent || '')"
118+
".includes('HELLO FROM RGC-BASIC')",
119+
timeout=120000,
120+
)
121+
browser.close()
86122
finally:
87123
httpd.shutdown()
88124
httpd.server_close()

web/tutorial.html

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>RGC-BASIC — Getting started (browser tutorial)</title>
7+
<style>
8+
:root {
9+
--text: #1a1a1a;
10+
--muted: #555;
11+
--border: #ccc;
12+
--bg: #fafafa;
13+
--accent: #2472c8;
14+
}
15+
* { box-sizing: border-box; }
16+
body {
17+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
18+
line-height: 1.55;
19+
color: var(--text);
20+
max-width: 52rem;
21+
margin: 0 auto;
22+
padding: 1.25rem 1rem 3rem;
23+
background: var(--bg);
24+
}
25+
h1 { font-size: 1.65rem; margin-top: 0; }
26+
h2 {
27+
font-size: 1.2rem;
28+
margin-top: 2rem;
29+
padding-top: 0.5rem;
30+
border-top: 1px solid var(--border);
31+
}
32+
h2:first-of-type { border-top: none; margin-top: 1rem; }
33+
p, li { color: var(--text); }
34+
.lead { font-size: 1.05rem; color: var(--muted); }
35+
.note {
36+
font-size: 0.9rem;
37+
color: var(--muted);
38+
border-left: 3px solid var(--accent);
39+
padding: 0.5rem 0 0.5rem 0.75rem;
40+
margin: 1rem 0;
41+
background: #fff;
42+
}
43+
code, kbd {
44+
font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
45+
font-size: 0.92em;
46+
background: #eee;
47+
padding: 0.12em 0.35em;
48+
border-radius: 4px;
49+
}
50+
nav.toc {
51+
background: #fff;
52+
border: 1px solid var(--border);
53+
border-radius: 8px;
54+
padding: 0.75rem 1rem;
55+
margin: 1.25rem 0;
56+
}
57+
nav.toc ul { margin: 0.35rem 0 0; padding-left: 1.25rem; }
58+
nav.toc a { color: var(--accent); }
59+
.rgc-tutorial-embed {
60+
border: 1px solid var(--border);
61+
border-radius: 8px;
62+
padding: 0.75rem;
63+
margin: 1rem 0;
64+
background: #fff;
65+
}
66+
.rgc-tutorial-toolbar button { margin-right: 0.5rem; margin-top: 0.25rem; }
67+
footer {
68+
margin-top: 2.5rem;
69+
padding-top: 1rem;
70+
border-top: 1px solid var(--border);
71+
font-size: 0.9rem;
72+
color: var(--muted);
73+
}
74+
</style>
75+
</head>
76+
<body>
77+
<h1>Getting started with RGC-BASIC</h1>
78+
<p class="lead">
79+
<strong>RGC-BASIC</strong> (Retro Game Coders BASIC) is a modern interpreter with classic Commodore-style syntax.
80+
This page walks through the basics. Each box below is a real interpreter running in your browser (WebAssembly).
81+
</p>
82+
<div class="note">
83+
<strong>Local use:</strong> from the project root, run <code>make basic-wasm-modular</code>, then serve the
84+
<code>web/</code> folder over HTTP (WASM does not load from <code>file://</code> in most browsers).
85+
Example: <code>cd web &amp;&amp; python3 -m http.server 8080</code> — then open this file’s URL.
86+
</div>
87+
88+
<nav class="toc" aria-label="Lesson outline">
89+
<strong>Lessons</strong>
90+
<ul>
91+
<li><a href="#hello">Hello, world</a></li>
92+
<li><a href="#print">PRINT and expressions</a></li>
93+
<li><a href="#variables">Variables</a></li>
94+
<li><a href="#input">INPUT</a></li>
95+
<li><a href="#if">Decisions (IF)</a></li>
96+
<li><a href="#for">FOR … NEXT loops</a></li>
97+
<li><a href="#rem">REM and END</a></li>
98+
<li><a href="#next">What to try next</a></li>
99+
</ul>
100+
</nav>
101+
102+
<h2 id="hello">1. Hello, world</h2>
103+
<p>
104+
Programs use line numbers (like <code>10</code>, <code>20</code>) or can be written without them — the interpreter accepts both.
105+
<code>PRINT</code> writes to the screen. <code>END</code> stops the program. Press <strong>Run</strong> to execute.
106+
</p>
107+
<div id="tut-hello"></div>
108+
109+
<h2 id="print">2. PRINT and expressions</h2>
110+
<p>
111+
Use a semicolon (<code>;</code>) to print items without a newline between them. Commas (<code>,</code>) move to the next print “zone” (tab-like).
112+
Arithmetic uses <code>+</code> <code>-</code> <code>*</code> <code>/</code> with normal precedence.
113+
</p>
114+
<div id="tut-print"></div>
115+
116+
<h2 id="variables">3. Variables</h2>
117+
<p>
118+
Numeric variables are letters or words (e.g. <code>A</code>, <code>SCORE</code>). String variables end with <code>$</code>.
119+
<code>LET</code> is optional: <code>LET X = 1</code> and <code>X = 1</code> are the same.
120+
</p>
121+
<div id="tut-vars"></div>
122+
123+
<h2 id="input">4. INPUT</h2>
124+
<p>
125+
<code>INPUT</code> reads what the user types. After you click <strong>Run</strong>, a prompt appears under the output — type a value and press Enter.
126+
String variables end with <code>$</code>; numeric variables do not.
127+
</p>
128+
<div id="tut-input"></div>
129+
130+
<h2 id="if">5. Decisions (IF)</h2>
131+
<p>
132+
<code>IF condition THEN …</code> runs the rest of the line when the condition is true. Here, one <code>INPUT</code> is followed by several one-line <code>IF</code>s.
133+
(RGC-BASIC also supports multi-line <code>IF … THEN … ELSE … END IF</code> — see the examples in the repo.)
134+
</p>
135+
<div id="tut-if"></div>
136+
137+
<h2 id="for">6. FOR … NEXT loops</h2>
138+
<p>
139+
<code>FOR</code> repeats a block until <code>NEXT</code>. The loop variable counts from the starting value through the limit (step defaults to 1).
140+
</p>
141+
<div id="tut-for"></div>
142+
143+
<h2 id="rem">7. REM and END</h2>
144+
<p>
145+
<code>REM</code> starts a comment (everything after it on that line is ignored). An apostrophe <code>'</code> also starts a comment.
146+
Always finish interactive examples with <code>END</code> or <code>STOP</code> when you are done.
147+
</p>
148+
<div id="tut-rem"></div>
149+
150+
<h2 id="next">8. What to try next</h2>
151+
<p>
152+
On your computer, install the release or build from source, then run <code>./basic examples/hello.bas</code> (or any <code>.bas</code> under <code>examples/</code>).
153+
The repository <code>README.md</code> lists statements, functions, PETSCII colour tokens in strings, file I/O, and more.
154+
For embedding interpreters in your own pages, see <code>docs/tutorial-embedding.md</code>.
155+
</p>
156+
<p>
157+
Edit the program below and experiment — for example, add a second <code>PRINT</code> or change the message.
158+
</p>
159+
<div id="tut-playground"></div>
160+
161+
<footer>
162+
RGC-BASIC browser build: terminal-style output with PETSCII/ANSI colouring (same defaults as the full tutorial embed).
163+
Graphics demos use <code>basic-gfx</code> or the canvas WASM demo separately; this page focuses on core language basics.
164+
</footer>
165+
166+
<script src="tutorial-embed.js"></script>
167+
<script>
168+
(function () {
169+
var progs = {
170+
hello:
171+
'10 PRINT "HELLO FROM RGC-BASIC"\n' +
172+
'20 END\n',
173+
print:
174+
'10 PRINT "ONE";"TWO"\n' +
175+
'20 PRINT 2 + 3 * 4\n' +
176+
'30 PRINT\n' +
177+
'40 END\n',
178+
vars:
179+
'10 LET A = 10\n' +
180+
'20 B = 20\n' +
181+
'30 PRINT "A ="; A, "B ="; B\n' +
182+
'40 PRINT "SUM ="; A + B\n' +
183+
'50 END\n',
184+
input:
185+
'10 INPUT "YOUR NAME"; N$\n' +
186+
'20 PRINT "HI,"; N$\n' +
187+
'30 END\n',
188+
ifstmt:
189+
'10 INPUT "ENTER A NUMBER"; X\n' +
190+
'20 IF X > 0 THEN PRINT "POSITIVE"\n' +
191+
'30 IF X < 0 THEN PRINT "NEGATIVE"\n' +
192+
'40 IF X = 0 THEN PRINT "ZERO"\n' +
193+
'50 END\n',
194+
forloop:
195+
'10 FOR I = 1 TO 5\n' +
196+
'20 PRINT I\n' +
197+
'30 NEXT I\n' +
198+
'40 END\n',
199+
rem:
200+
'10 REM THIS LINE IS A COMMENT\n' +
201+
"20 ' APOSTROPHE COMMENT: REST OF LINE IS IGNORED\n" +
202+
'30 PRINT "SEE README FOR THE FULL LANGUAGE LIST"\n' +
203+
'40 END\n',
204+
playground:
205+
'10 REM YOUR TURN — EDIT AND RUN\n' +
206+
'20 PRINT "HELLO FROM MY PROGRAM"\n' +
207+
'30 END\n'
208+
};
209+
210+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-hello'), { program: progs.hello });
211+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-print'), { program: progs.print });
212+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-vars'), { program: progs.vars });
213+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-input'), { program: progs.input });
214+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-if'), { program: progs.ifstmt });
215+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-for'), { program: progs.forloop });
216+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-rem'), { program: progs.rem });
217+
RgcBasicTutorialEmbed.mount(document.getElementById('tut-playground'), { program: progs.playground });
218+
})();
219+
</script>
220+
</body>
221+
</html>

0 commit comments

Comments
 (0)