Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
969b20e
feat: build complete Mini Marty virtual robot programming environment
tzone85 Mar 3, 2026
6b18776
docs: production-ready design spec
tzone85 Jun 2, 2026
f428a31
docs(plan): production-ready implementation plan (49 tasks, 3 phases)
tzone85 Jun 2, 2026
874ebe3
chore: add zod, web-vitals, axe, vercel/analytics deps
tzone85 Jun 2, 2026
ff38343
feat(obs): add Logger/ErrorReporter ports + console + memory adapters
tzone85 Jun 2, 2026
746a2a0
feat(obs): provider + useObservability hook
tzone85 Jun 2, 2026
d27cc7c
feat(obs): Sentry reporter adapter (injectable client)
tzone85 Jun 2, 2026
86831fc
feat(analytics): port + noop + vercel adapters + provider
tzone85 Jun 2, 2026
6cdc0b5
feat(flags): env-driven feature flags
tzone85 Jun 2, 2026
d17c1dd
feat(system): ErrorBoundary with injectable reporter
tzone85 Jun 2, 2026
1970d97
feat(a11y): SkipLink component
tzone85 Jun 2, 2026
2cb2d96
feat(a11y): SkipLink + main landmark in AppShell
tzone85 Jun 2, 2026
aa882d7
feat(a11y): Header nav label, aria-current, focus rings
tzone85 Jun 2, 2026
dc7ada8
feat(a11y): Sidebar aria-label + focus rings
tzone85 Jun 2, 2026
12ffccc
feat(a11y): ThemeToggle aria-pressed + announcing label
tzone85 Jun 2, 2026
ae27d0e
feat(theme): respect prefers-color-scheme on first visit
tzone85 Jun 2, 2026
82e1ddf
test: provide matchMedia polyfill in jsdom setup
tzone85 Jun 2, 2026
0d25e4a
feat(storage): Zod-validated localStorage wrapper + schemas
tzone85 Jun 2, 2026
1fcabec
feat(marty): Clock abstraction + RealClock + FakeClock
tzone85 Jun 2, 2026
da7349d
refactor(marty): CommandQueue uses Clock + promise chain
tzone85 Jun 2, 2026
00a4c75
test(marty): VirtualMarty command + sensor coverage
tzone85 Jun 2, 2026
1eb8d0c
test(marty): MartyEventEmitter coverage
tzone85 Jun 2, 2026
bfed30f
refactor(pyodide): split into loader+registry+events (SRP)
tzone85 Jun 2, 2026
7b00ba7
test(pyodide): python-executor coverage
tzone85 Jun 2, 2026
3f4fe9f
test(pyodide): fix import strip assertion to target user code section
tzone85 Jun 2, 2026
3e0a604
test(pyodide): martypy-module coverage
tzone85 Jun 2, 2026
47962c0
test(scene): AnimationPlayer + interpolation coverage
tzone85 Jun 2, 2026
fd7d2ae
test(lib): navigation + sidebar-config coverage
tzone85 Jun 2, 2026
ea52e33
feat(app): wire observability, analytics, error boundary into layout
tzone85 Jun 2, 2026
b123a19
feat(app): route error.tsx, not-found.tsx, global-error.tsx
tzone85 Jun 2, 2026
bcc67e6
feat(seo): sitemap, robots, PWA manifest, placeholder icons
tzone85 Jun 2, 2026
a5ef162
fix(seo): use valid 'maskable' purpose value in PWA manifest
tzone85 Jun 2, 2026
e390de6
feat(security): CSP middleware + Vercel config
tzone85 Jun 2, 2026
2939e19
perf(scene): pause on tab hidden + respect prefers-reduced-motion
tzone85 Jun 2, 2026
8e1ef5a
chore: prettier formatting cleanup
tzone85 Jun 2, 2026
efa36b0
test(editor): martypy-completions coverage
tzone85 Jun 2, 2026
701834a
test(content): tutorial + challenge data shape coverage
tzone85 Jun 2, 2026
2c81a52
test(scene): animation definitions coverage
tzone85 Jun 2, 2026
a784f5d
test(pyodide): usePyodide + usePythonExecution coverage
tzone85 Jun 2, 2026
c7e6f7e
test(blocks): marty-blocks + toolbox-config coverage
tzone85 Jun 2, 2026
19c49b5
test(e2e): axe a11y scans on all routes
tzone85 Jun 2, 2026
31ea4d8
test(e2e): keyboard-only navigation
tzone85 Jun 2, 2026
daf579d
test(e2e): dark mode persistence
tzone85 Jun 2, 2026
48aa250
test(e2e): python editor smoke
tzone85 Jun 2, 2026
75ca8d1
test(e2e): block editor smoke
tzone85 Jun 2, 2026
42f64e9
test(e2e): tutorials + challenges smoke
tzone85 Jun 2, 2026
c06f4cc
test: lift coverage to >=80% with documented vendor exclusions
tzone85 Jun 2, 2026
3874c66
perf: code-split Pyodide/Monaco/Blockly/Three via next/dynamic
tzone85 Jun 2, 2026
6894649
feat(obs): Web Vitals reporting to analytics
tzone85 Jun 2, 2026
bd3435d
ci: add Lighthouse job (non-blocking initially)
tzone85 Jun 2, 2026
de7c332
docs: architecture, runbook, contributing
tzone85 Jun 2, 2026
b417905
docs: six SVG diagrams (no mermaid)
tzone85 Jun 2, 2026
e12e3ce
docs(obsidian): vault pages + diagrams cross-link
tzone85 Jun 2, 2026
0fb7fbd
docs: refresh README + TRAINING with new docs links
tzone85 Jun 2, 2026
ae7687c
fix: post-Phase3 cleanup
tzone85 Jun 2, 2026
6d4d5f2
fix: route all localStorage I/O through Zod safe-storage
tzone85 Jun 2, 2026
eb7cda7
fix: drop unused CSP nonce in proxy middleware
tzone85 Jun 2, 2026
103f5e7
fix: emit web vitals under a dedicated analytics event
tzone85 Jun 2, 2026
113d1d7
fix: report Pyodide load failures via the observability port
tzone85 Jun 2, 2026
04fc1e1
refactor: collapse PyodideRegistry into PyodideLoader
tzone85 Jun 2, 2026
ca4f5f7
refactor: inject Clock into VirtualMarty, move id counter to instance
tzone85 Jun 2, 2026
7597883
refactor: derive wrapper line offset and per-call entry ids
tzone85 Jun 2, 2026
20e7030
fix: make NoopAnalytics an actual no-op; introduce MemoryAnalytics
tzone85 Jun 2, 2026
a9b878e
fix: harden error pages and add explicit type to every button
tzone85 Jun 2, 2026
5bdab63
docs: refresh architecture and runbook references after refactors
tzone85 Jun 2, 2026
b1cba1b
chore: drop unused pwaEnabled flag
tzone85 Jun 2, 2026
26583af
fix: replace 1x1 placeholder PWA icons with a real SVG
tzone85 Jun 2, 2026
d3537bd
style: prettier reflow on migration call sites
tzone85 Jun 2, 2026
14e23fc
fix: restore CSP nonce so Next.js inline scripts can hydrate
tzone85 Jun 2, 2026
da0b102
Merge main into PR #21
tzone85 Jun 3, 2026
e8d6b55
fix(merge): noop observability fallback + restore RTL imports
tzone85 Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
* text=auto eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.woff binary
*.woff2 binary
*.svg text eol=lf
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,22 @@ jobs:
name: playwright-report
path: playwright-report/
retention-days: 14

lighthouse:
name: Lighthouse
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- run: npm run start &
env:
PORT: "3000"
- run: npx wait-on http://localhost:3000
- run: npx @lhci/cli@0.14 autorun --collect.url=http://localhost:3000 --upload.target=temporary-public-storage
continue-on-error: true
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ next-env.d.ts
# IDE
.vscode/
.idea/
<<<<<<< HEAD
=======

# hive agent working copies
.hive/
repos/
firebase-debug.log
>>>>>>> origin/feature/REQ-I1QAV-1Z
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "repos/mini-marty"]
path = repos/mini-marty
url = https://github.com/tzone85/mini-marty.git
branch = main
72 changes: 53 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,67 @@
# Mini Marty

Visual programming environment for the Marty robot.
[![CI](https://github.com/tzone85/mini-marty/actions/workflows/ci.yml/badge.svg)](https://github.com/tzone85/mini-marty/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](#license)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Ftzone85%2Fmini-marty)

## Getting Started
A virtual programming environment for learning to code with Marty the Robot. No physical robot needed.

Write Python or drag Blockly blocks; a `VirtualMarty` instance enqueues commands; a React Three Fiber scene animates them at 60 fps.

## Quick start

```bash
npm install
npm run dev
```

Open [http://localhost:3000](http://localhost:3000).
Open http://localhost:3000.

## Scripts

| Command | Description |
|---------|-------------|
| `npm run dev` | Start development server |
|---|---|
| `npm run dev` | Start the dev server |
| `npm run build` | Production build |
| `npm run lint` | Run ESLint |
| `npm run format` | Format code with Prettier |
| `npm run typecheck` | TypeScript type checking |
| `npm test` | Run unit tests |
| `npm run test:e2e` | Run E2E tests |

## Tech Stack

- **Framework:** Next.js 16 (App Router)
- **Language:** TypeScript
- **Styling:** Tailwind CSS v4
- **Unit Testing:** Vitest + React Testing Library
- **E2E Testing:** Playwright
- **Linting:** ESLint + Prettier
| `npm run lint` | ESLint |
| `npm run format:check` | Prettier check |
| `npm run typecheck` | TypeScript |
| `npm test` | Vitest unit + integration |
| `npm run test:coverage` | Coverage with 80% gate |
| `npm run test:e2e` | Playwright |

## Tech stack

Next.js 16 (App Router) + TypeScript + Tailwind. Three.js / React Three Fiber for 3D. Pyodide for Python in the browser. Blockly for blocks. Monaco for code editing. Vitest + Playwright for tests. Vercel for hosting.

## Marty commands

| Command | Effect |
|---|---|
| `walk(steps)` | Walk forward |
| `dance()` | Dance routine |
| `kick("left"\|"right")` | Kick with leg |
| `slide("left"\|"right")` | Slide sideways |
| `lean("left"\|"right")` | Lean |
| `wiggle()` | Wiggle body |
| `circle_dance()` | Circular dance |
| `celebrate()` | Celebrate |
| `get_ready()` / `stand_straight()` | Pose |
| `eyes(expression)` | Set eye expression |
| `arms(left, right)` | Set arm angles |
| `foot_on_ground(side)` | Read foot sensor |
| `get_distance_sensor()` | Distance in cm |
| `get_accelerometer()` | `{x, y, z}` tilt |
| `stop()` | Stop all motion |

## Documentation

- [`docs/`](./docs) — architecture, runbook, contributing
- [`docs/architecture/overview.md`](./docs/architecture/overview.md) — layered diagram
- [`docs/runbook.md`](./docs/runbook.md) — incident response
- [`docs/contributing.md`](./docs/contributing.md) — branch model, code review
- [`TRAINING.md`](./TRAINING.md) — parent-child learning guide

## License

MIT
238 changes: 238 additions & 0 deletions TRAINING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Mini Marty — Training Guide

A guide for parents and kids to learn programming together using Mini Marty.

## Getting Started

### Setup (5 minutes)

1. Open your terminal and navigate to the project folder
2. Run `npm install` to install dependencies
3. Run `npm run dev` to start the development server
4. Open http://localhost:3000 in your browser

### First Look (5 minutes)

When you open Mini Marty, you will see:

- A **header** with navigation links at the top
- A **sidebar** on the left with context-specific options
- The **main area** showing the current page
- A **3D Marty robot** on the home page that you can rotate by clicking and dragging

Explore the navigation: Home, Block Editor, Python Editor, Tutorials, and Challenges.

---

## Learning Path

We recommend following these sessions in order. Each session is designed for 30-45 minutes.

### Session 1: Block Coding (Beginner)

**Goal:** Understand the concept of giving instructions to a robot.

1. Go to the **Block Editor**
2. Open the **Motion** category in the toolbox
3. Drag a "when program starts" block (from Events) to the workspace
4. Connect "get ready" and "walk 2 steps" blocks below it
5. Click **Save** to save your work

**Discussion points:**
- What is a program? (A list of instructions for a computer/robot)
- Why does order matter? (Marty needs to get ready before walking)
- What happens if you change the number of steps?

### Session 2: First Python Program (Beginner)

**Goal:** Write your first Python code and see the virtual robot respond.

1. Go to the **Python Editor**
2. Wait for "Python ready" (green dot) in the top right
3. The starter code is already there — click **Run**
4. Watch Marty move in the 3D viewport on the right!
5. Try the "Hello Marty!" tutorial (Tutorials page)

**Key concepts to explain:**
- `from martypy import Marty` — importing a tool/library
- `Marty("virtual")` — creating an instance of the robot
- `await` — waiting for an action to complete before the next one
- `print()` — showing text output in the console

### Session 3: Movement Commands (Beginner)

**Goal:** Learn all of Marty's movement commands.

1. Go to the **Tutorials** page
2. Work through "Hello Marty!" and "Dance Moves" tutorials
3. Try the "First Walk" and "Dance Party" challenges

**Experiment together:**
- Change `walk(2)` to `walk(5)` — what happens?
- Change `kick("left")` to `kick("right")`
- Chain multiple commands: walk → kick → dance → celebrate

### Session 4: Sensors & Decisions (Intermediate)

**Goal:** Learn about sensors and if-else statements.

1. Work through the "Sensing the World" tutorial
2. Try the "Sensor Check" challenge

**Key concepts:**
- Sensors give robots information about the world
- `if`/`elif`/`else` lets the program make decisions
- The distance sensor measures how far away things are
- The accelerometer detects tilt and gravity

### Session 5: Loops & Functions (Intermediate)

**Goal:** Learn to repeat actions and organise code into functions.

1. Work through "Loops & Control Flow" tutorial
2. Try "Loop Walker" and "Smart Marty" challenges

**Key concepts:**
- `for i in range(5):` — repeat something 5 times
- Functions let you name a group of commands
- `async def my_function():` — defining a reusable function
- Why functions are useful (avoid copy-pasting code)

### Session 6: Advanced Python (Advanced)

**Goal:** Use lists, variables, and advanced patterns.

1. Work through the "Python Power" tutorial
2. Try the advanced challenges: "Robot Choreographer", "Function Factory", "The Marty Show"

**Key concepts:**
- Lists store multiple items: `moves = ["walk", "dance", "kick"]`
- `getattr()` calls methods by name — powerful for dynamic code
- Building a full program with structure (intro, main, finale)

---

## Tips for Parents

### Making it fun
- Let your child experiment freely — there is no way to break anything
- Celebrate small wins: "Look, you made Marty walk!"
- Take turns: you code one command, they code the next
- Challenge each other: "Can you make Marty do 3 different things?"

### When they get stuck
- Use the **hints** in challenges — they reveal one at a time
- Check the **API Reference** on the Tutorials page
- Read error messages together — they often say exactly what went wrong
- Look at tutorial code for similar examples

### Building confidence
- Start with Block Editor if Python feels intimidating
- The "Hello Marty" tutorial is designed to be impossible to fail
- Emphasise that professional programmers Google things and make mistakes constantly
- Save working programs with the Save button so they can show others

### Extending the learning
- Ask "what if" questions: "What if Marty kicked 10 times?"
- Suggest modifications: "Can you add more moves to your dance?"
- Introduce debugging: intentionally break code and fix it together
- Connect to real-world concepts: "Traffic lights use if-else too!"

---

## Troubleshooting

### Python says "loading" for a long time
The Python runtime (Pyodide) is about 10MB and loads from a CDN. On a slow connection, this can take 30-60 seconds. After the first load, it is cached by your browser.

### 3D scene is blank
Make sure your browser supports WebGL. Most modern browsers do. Try Chrome or Firefox if you're having issues.

### Code runs but Marty does not move
Make sure you're using `await` before movement commands:
- Correct: `await my_marty.walk(2)`
- Incorrect: `my_marty.walk(2)` (this sends the command but does not wait for it)

### Block Editor is empty
Blockly requires JavaScript to be enabled. If blocks disappear after saving, try clearing your browser's localStorage for localhost:3000.

---

## Progression Checklist

Use this to track your child's progress:

**Beginner**
- [ ] Explored the 3D Marty on the home page
- [ ] Created a block program in the Block Editor
- [ ] Ran their first Python program
- [ ] Completed "Hello Marty!" tutorial
- [ ] Completed "Dance Moves" tutorial
- [ ] Solved all 3 beginner challenges

**Intermediate**
- [ ] Completed "Sensing the World" tutorial
- [ ] Used an if-else statement
- [ ] Completed "Loops & Control Flow" tutorial
- [ ] Used a for loop
- [ ] Wrote their first function
- [ ] Solved all 3 intermediate challenges

**Advanced**
- [ ] Completed "Python Power" tutorial
- [ ] Used lists and variables
- [ ] Created a multi-function program
- [ ] Solved all 3 advanced challenges
- [ ] Created their own original program from scratch!

---

## Troubleshooting on Windows

Mini Marty runs on Windows 10 / 11 (PowerShell or Windows Terminal). Below are the most common rough edges.

### Line endings

Git on Windows usually converts `\n` to `\r\n`. The repo has not pinned a `.gitattributes` yet, so if your editor or a script trips on the difference, run:

```powershell
git config --global core.autocrlf input
```

This keeps the working tree as `\n` for source files while still letting Windows tools open them.

### Installing on PowerShell

Use PowerShell (not Command Prompt). If `npm install` fails with a script execution policy error:

```powershell
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
```

Then close and reopen the terminal and retry.

### WebGL check

The 3D scene needs WebGL. Open `chrome://gpu` in Chrome or `about:support` in Firefox; look for "WebGL: Hardware accelerated". If it says "Software only" or "Disabled":

1. Update your graphics driver.
2. In Chrome, enable `chrome://flags/#ignore-gpu-blocklist` if your GPU is on the blocklist but otherwise healthy.
3. As a last resort, the Block Editor and Python Editor still work without WebGL; only the home-page scene degrades.

### Long paths

If `npm install` errors with `ENAMETOOLONG`, enable long paths:

```powershell
git config --global core.longpaths true
```

And in an admin PowerShell:

```powershell
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
```

### Antivirus slowing `node_modules`

Windows Defender scans every file in `node_modules`, which can multiply install time. Add the project folder to Defender's exclusion list under Settings -> Virus & threat protection -> Exclusions if you control the machine.
Loading
Loading