Skip to content

feat(website): interactive browser playground - #5

Merged
pkurcx merged 17 commits into
mainfrom
feat/playground
Sep 4, 2026
Merged

feat(website): interactive browser playground#5
pkurcx merged 17 commits into
mainfrom
feat/playground

Conversation

@pkurcx

@pkurcx pkurcx commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds an interactive playground to the docs site at /playground/: paste or open an OpenAPI spec, edit the generator config as JSONC, and see the generated files in the browser. It runs the @avsystem/openapi-ng-wasm32-wasi build shipped in #4, so the site needs no backend. Also tracks the StackBlitz demo app in stackblitz/ and links both from the README.

Changes

  • Playground page: three CodeMirror panes (spec, config, output) with a file tree, an "Open file…" button and drag-and-drop that replaces the document. Diagnostics land in a console footer coloured by severity, with a contrast test guarding the light theme.
  • Config editor: JSONC with a shipped template listing mappedTypes, responseTypeMapping and naming commented out at their defaults. "/…/flags" strings are lowered into the { source, flags } RegExp shape. An e2e test checks that enabling every default leaves the output unchanged.
  • Theme: panes are painted from Starlight's bundled Night Owl palette on its code-block background tokens, so the theme toggle stays a pure CSS switch. highlight.js is gone.
  • Header: a Docs / Playground switch replaces the sidebar entry, so the playground no longer strands readers on a page without a sidebar.
  • Engine bundle: scripts/bundle-engine.mjs pre-bundles the napi-rs WASI browser loader into public/playground-engine/ (gitignored) before dev and build.
  • Workspace: website joins the root pnpm workspace; its own lockfile is removed. The docs workflow now type-checks and runs the unit and Playwright suites, and triggers on lockfile and workspace changes.
  • Root tests: ava is scoped to __test__/**/*.spec.ts, since the website's vitest and Playwright files matched its default glob.
  • StackBlitz: the demo Angular app is tracked in stackblitz/ and linked from the README next to the playground.

Testing

  • pnpm test (root, ava): 333 passed.
  • website: pnpm build OK, pnpm check 0 errors, pnpm test:unit 16 passed, pnpm test:e2e 15 passed against wrangler dev.
  • pnpm lint clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR

pkurcx and others added 17 commits September 3, 2026 19:15
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
Add a JSON config editor beside the spec: it takes an openapi-ng config
body (emit, mappedTypes, responseTypeMapping, naming), drops input and
output with a note, and lowers "/…/flags" strings into the {source,
flags} shape the wrapper needs, since JSON cannot carry a RegExp.

Diagnostics move into a scrolling console card under the panes, and the
page hands the viewport below the title to the panes so only they
scroll. Starlight's auto inline margins made the content container a
content-sized flex item, so its width tracked the widest generated
file; pin it to 100%. CodeMirror and the highlight.js theme each own
their scrolling, so drop the wrapper scrollers that fought them.

Remove the share link: it earned little next to the cost of carrying
lz-string and a hash codec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
Warnings were rendered in the body colour, so a dropped cookie
parameter read like ordinary chrome next to a red fatal. Colour each
line from the diagnostic's own `severity` instead of its position in
the list, and give notes their own hue.

Use the `-high` tints throughout: the mid-tone red and orange fall to
3.35:1 and 1.62:1 against the console background in the light theme,
which a new contrast test now guards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
The output pane rendered each file through highlight.js into a bare
<pre>, so it had no line numbers, no search, and re-highlighted the
whole file on every regeneration. Swap it for a read-only CodeMirror
view in TypeScript mode, which also renders only the viewport.

The theme and token mapping move to theme.ts and are shared by all
three panes; the highlight.js runtime is gone, its GitHub stylesheets
stay as the colour source. Non-editable content is not focusable by
default, so the view gets a tabindex for the search keymap to reach it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
Pasting was the only way in. Add an "Open file…" button backed by a
hidden file input, and replace the document when a file is dropped on
an editor: CodeMirror's default handler would splice the file in at
the cursor and merge two specs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
The playground sat in the sidebar, so following that link dropped the
reader onto a splash page where the sidebar disappears. Override the
Starlight header with a Docs / Playground switch next to the site
title, marking the current section, and take the entry out of the
sidebar. Docs points at Getting started, the first page with a sidebar.

The playground page drops its own heading and the docs search box, both
redundant next to the switch. Below Starlight's mobile breakpoint the
header fits only the title and one link, so it shows the way out.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
The config pane accepted plain JSON and a single live option, so every
other setting meant a trip to the docs. Parse the config as JSONC and
ship a template with mappedTypes, responseTypeMapping and naming
commented out at their default values, each with a one-line example of
the shape. Enabling all of them must not change the output, which an
e2e test checks on petstore and on a spec without tags.

The JSON grammar has no comments, so the config editor uses the
JavaScript mode. The root element now records the last settled
generation so tests can wait for a specific edit instead of the debounce.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
The editors wore highlight.js's GitHub theme while every snippet in the
docs is Night Owl on Starlight's grey. Colour the CodeMirror panes from
a --pg-* palette copied out of Starlight's bundled Night Owl themes,
on the same background tokens Starlight paints its code blocks with,
and drop highlight.js altogether.

Tokens get plain tok-* classes styled in CSS, so the theme toggle stays
a pure CSS switch. Property names share the JSON key colour across
YAML, JSON and TypeScript; Night Owl varies it per language, but
CodeMirror has a single tag for all three.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
The website's vitest and Playwright files now match ava's default glob,
so the root suite tried to run them and failed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
ready() returned as soon as the status landed, ahead of the first run.
On a fast machine the run counter was still unset, so the first check
passed trivially; on CI the run had settled and the test replaced the
config with its identical default, which CodeMirror treats as no change,
so nothing regenerated. Wait for the first run, drop the no-op edit and
settle after each edit so the debounce cannot merge them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011t8uBVY6h4kfN5cXHXuCpR
@pkurcx
pkurcx merged commit e404e42 into main Sep 4, 2026
37 checks passed
@pkurcx
pkurcx deleted the feat/playground branch September 4, 2026 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant