ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Greta – the English tutor
Translation pending
EducationTypeScript · CSSGitHub @designlovesOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Greta – the English tutor
The app was migrated from a single-file vanilla-JS index.html to a React + TypeScript + Vite app. app/ is the whole frontend now — there's no legacy version left.
cd app
npm install
npm run dev # local dev server
npm run lint # oxlint
npm run format:check # prettier
npm run build # tsc -b && vite build
npm test # playwright, against the built app
React + TypeScript + Vite, with Base UI for unstyled accessible components. All state lives in a single useAppState() hook (app/src/state/useAppState.ts), passed down as props — no Context or Redux. Screens are components in app/src/screens/, switched by a plain if/else chain in App.tsx keyed on the current screen. Pure data and logic with no dependency on runtime state (icons, i18n strings, avatar/list constants, paste-parsing, shuffling, etc.) live in app/src/data/ and app/src/lib/.
Sign-in is real — a magic-link email through Supabase, the same project's public auth endpoints the app always used (app/src/lib/auth.ts) — but nothing re-syncs a signed-in user's lists to the server yet. Every screen still reads/writes local state only, exactly as a guest would; wiring up server sync for lists is separate follow-up work.
The app tracks the visual viewport (app/src/lib/viewportHeight.ts) so the on-screen keyboard shrinks #frame to fit above it instead of covering it, and corrects mobile browsers' own "scroll the focused input into view" behavior where it fights with that shrink — without this, content above a focused input (like the coin pouch during the write-5x remediation) can end up scrolled out of view exactly when it matters. index.html's meta viewport also declares interactive-widget=resizes-content, which gets browsers that honor it (Chrome for Android 108+) the same result natively, but that's Chromium-only — Safari has never implemented it, so this JS is what actually makes it work on iOS. #screen is a CSS size container so the word card can size its own padding/font off cqh (its real, shrunk height) rather than vh (the viewport as a whole) — otherwise the frame shrinks but its content doesn't, and overflows it.
A multi-step plan: coins (earning, persistence, balance display — shipped) → a dressing room shell proving the equip/render loop end to end with one free accessory (shipped) → a real priced shop with several accessories across categories, using the same overlay technique → full-body avatars and clothing, if the earlier steps land well.
The dressing room (app/src/screens/DressingRoom.tsx) lets you equip/unequip accessories, which then render as an overlay (app/src/components/AccessoryOverlay.tsx) on top of the avatar image wherever it appears (Home header, Settings profile card). Accessories are defined in app/src/data/accessories.ts; there's currently one free starter item (a hand-drawn party hat, since no sourced accessory art exists yet) with no purchase flow — that's the next step, once real art is available.
End-to-end tests (Playwright) drive the real UI against the built app (app/tests/, run via npm test from app/).
.github/workflows/deploy.yml publishes app/'s build output to the gh-pages branch:
main build the app (npm ci && npm run build in app/) and deploy it to the site rootapp/** gets its own preview build at pr-preview/pr-<number>/, deployed on open/update and removed on close (via rossjrw/pr-preview-action)The build's asset paths are relative (base: './' in app/vite.config.ts), so the exact same app/dist output works unmodified whether it's deployed at the site root or nested under a PR's own pr-preview/pr-<n>/ subpath — no per-deployment rebuild needed.
.github/workflows/app-ci.yml lints, format-checks, builds, and runs the Playwright tests on every PR that touches app/**, as a quality gate independent of deployment.
One-time repo settings this workflow needs (Settings → …):
gh-pages / (root).contents: write permission is capped by whatever this is set to, so it must allow write for the deploy step to push.supabase/functions/greta/ is a Supabase Edge Function backing account sync and word-list translation — see SETUP.md for deploying it. It's independent of the frontend rewrite above.