Skip to content

Commit f4d899f

Browse files
Merge pull request #35 from gpu-cli/james/playground-wasm
In-browser WASM playground + site-wide UX/design polish
2 parents 7c58c72 + b38fe4a commit f4d899f

36 files changed

Lines changed: 1893 additions & 331 deletions

.beads/issues.jsonl

Lines changed: 7 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Playground WASM
2+
3+
# Builds the website playground's WASM bundle whenever the generator changes,
4+
# publishes it as a public release asset, and bumps website/playground-wasm.lock.
5+
# The lock-bump commit triggers Vercel's git auto-deploy, whose build fetches
6+
# the asset via website/scripts/fetch-playground-wasm.mjs. No Vercel secrets.
7+
#
8+
# Self-retrigger is impossible by construction: the lock file lives under
9+
# website/, outside this workflow's path filter.
10+
11+
on:
12+
push:
13+
branches: [main]
14+
paths:
15+
- "src/**"
16+
- "wasm/**"
17+
- "Cargo.toml"
18+
- "Cargo.lock"
19+
- "scripts/build-playground-wasm.sh"
20+
workflow_dispatch:
21+
22+
permissions:
23+
contents: write
24+
25+
concurrency:
26+
group: playground-wasm
27+
cancel-in-progress: false
28+
29+
env:
30+
CARGO_TERM_COLOR: always
31+
32+
jobs:
33+
build-and-publish:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
- uses: dtolnay/rust-toolchain@stable
38+
with:
39+
targets: wasm32-unknown-unknown
40+
- uses: Swatinem/rust-cache@v2
41+
- uses: taiki-e/install-action@v2
42+
with:
43+
tool: wasm-pack
44+
45+
- name: Build playground bundle
46+
run: ./scripts/build-playground-wasm.sh
47+
48+
- name: Package and publish release asset
49+
env:
50+
GH_TOKEN: ${{ github.token }}
51+
run: |
52+
set -euo pipefail
53+
version=$(cargo metadata --no-deps --format-version 1 \
54+
| jq -r '.packages[] | select(.name == "openapi-to-rust") | .version')
55+
tag="playground-wasm-${version}-${GITHUB_SHA::7}"
56+
tar czf playground-pkg.tar.gz -C website/public/playground pkg
57+
gh release create "$tag" playground-pkg.tar.gz \
58+
--prerelease \
59+
--title "Playground WASM $version (${GITHUB_SHA::7})" \
60+
--notes "WASM bundle for openapi-to-rust.dev/playground, built from ${GITHUB_SHA}."
61+
echo "$tag" > website/playground-wasm.lock
62+
63+
- name: Commit lock bump
64+
# Pushes back to the ref the workflow ran on: main for the normal
65+
# push-triggered flow, the dispatched branch for pre-merge test runs.
66+
run: |
67+
set -euo pipefail
68+
if git diff --quiet -- website/playground-wasm.lock; then
69+
echo "lock unchanged; nothing to deploy"
70+
exit 0
71+
fi
72+
git config user.name "github-actions[bot]"
73+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
74+
git add website/playground-wasm.lock
75+
git commit -m "chore: bump playground wasm to $(cat website/playground-wasm.lock) [skip ci]"
76+
git pull --rebase origin "$GITHUB_REF_NAME"
77+
git push origin "HEAD:$GITHUB_REF_NAME"

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@
3030
/website/dist/
3131
/website/.astro/
3232
/website/.vercel/
33+
34+
# Playground WASM bundle: built by scripts/build-playground-wasm.sh locally,
35+
# published as a GitHub release asset by .github/workflows/playground-wasm.yml
36+
# and fetched by website/scripts/fetch-playground-wasm.mjs on Vercel.
37+
/website/public/playground/pkg/
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
target: website/src (playground + site)
3+
total_score: 25
4+
p0_count: 2
5+
p1_count: 3
6+
timestamp: 2026-07-18T05-06-10Z
7+
slug: website-src
8+
---
9+
Method: dual-agent (A: design review sub-agent · B: detector/browser sub-agent)
10+
11+
# Design Health Score
12+
13+
| # | Heuristic | Score | Key Issue |
14+
|---|-----------|-------|-----------|
15+
| 1 | Visibility of System Status | 2 | Generate/example pick before WASM ready is a silent no-op; stale example dropdown label |
16+
| 2 | Match System / Real World | 3 | YAML parser jargon in errors; otherwise excellent domain copy |
17+
| 3 | User Control and Freedom | 2 | Failed generate destroys previous result; example pick clobbers pasted spec |
18+
| 4 | Consistency and Standards | 2 | Unstyled UA-default tab buttons; nested <main>; tablist roles without keyboard pattern |
19+
| 5 | Error Prevention | 2 | No guard on clobbering user spec; pre-ready clicks eaten |
20+
| 6 | Recognition Rather Than Recall | 3 | REQUIRED_DEPS.toml leads tab row; otherwise good hints |
21+
| 7 | Flexibility and Efficiency | 3 | Cmd+Enter, URL fetch, crate download; no permalink |
22+
| 8 | Aesthetic and Minimalist Design | 3 | 6rem "Playground" h1 pushes Generate below the fold at 900px |
23+
| 9 | Error Recovery | 2 | line/col given but no editor line numbers; no aria-live |
24+
| 10 | Help and Documentation | 3 | Strong footer + docs cross-links |
25+
| **Total** | | **25/40** | **Acceptable** |
26+
27+
# Anti-Patterns Verdict
28+
Not slop overall: varied composition, evidence-driven copy, coherent print-terminal register. Static detector: 0 findings. In-page detector: 13 findings across 5 pages — hero-eyebrow-chip on 4/5 pages, tiny-text 11.52px on 3 pages, cramped .table-wrap padding on 2, oversized 84px compare h1, long line-length on /playground (~160ch) and /compare (~110ch). The one "looks broken/AI" moment: unstyled native tab buttons on the playground result.
29+
30+
# Priority Issues
31+
- [P0] Playground output pane clipped ≤900px: tabs/Copy/Download unreachable; `.playground { grid-template-columns: 1fr }` at ≤900px loses minmax(0,…) + `main{overflow:clip}` makes it unscrollable (playground.astro:575, global.css:65).
32+
- [P0] Docs-layout pages truncate text at 390px: `.doc-shell { grid-template-columns: 1fr }` (global.css:508) needs minmax(0,1fr); verified live.
33+
- [P1] Result tabs unstyled: JS-created buttons never get data-astro-cid-*, scoped .tab-row rules can't match (playground.astro:212, 528-543). Active tab indistinguishable, ~24px targets.
34+
- [P1] Silent no-op before worker ready (generate() early-return, playground.astro:243); flagship example path fills textarea then nothing happens.
35+
- [P1] Nested <main> landmarks (BaseLayout.astro:64 + playground.astro:19).
36+
- [P2] Homepage never sells the playground; hero CTAs go to docs + anchor only.
37+
- [P2] Errors invisible to AT (no aria-live/role=alert) and destroy prior result (playground.astro:74-75, 233-240).
38+
- [P2] Tablist semantics without keyboard behavior; REQUIRED_DEPS.toml first; "1 schemas" pluralization (playground.astro:81, 210-226).
39+
- [P2] Compare h1 84px breaks product name across lines; page-hero h1 clamp oversized (global.css:338).
40+
- [P2] Tiny text 11.52px (0.72rem figcaption/footer labels) on 3 pages; doc-pagination 0.66rem.
41+
- [P3] No JSON-LD on /playground; shared generic og-card. Stale example dropdown label. DocsLayout hardcoded updated-date default. Avenir-only personality on macOS.
42+
43+
# Persona Red Flags
44+
- Alex: Cmd+Enter swallowed during load → "it's broken" in 5s; role=tab promises arrow keys that don't exist; no shareable permalink.
45+
- Jordan: REQUIRED_DEPS.toml + "2 schemas · 4 operations" vocabulary; parser-speak errors with no line numbers; example pick destroys pasted work.
46+
- Casey: mobile playground generates but result is clipped + no scroll-into-view — can't copy/download; docs pages unreadable at 390.
47+
48+
# Minor Observations
49+
Warning palette untokenized; playground-footer inline-code wraps oddly; 404 page is great; a11y baseline (skip-link, focus-visible, reduced-motion) above average; dev-toolbar 504 is a dev-only artifact.
50+
51+
# Questions
52+
- Should the playground be the homepage hero's primary demo ("run it on your spec now, nothing uploaded")?
53+
- Could the error state keep the last good result visible instead of a 480px void?
54+
- Is "Playground" worth 230px of h1, or should the tool start at the top?
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
target: website/src (playground + site)
3+
total_score: 37
4+
p0_count: 0
5+
p1_count: 1
6+
timestamp: 2026-07-18T05-19-10Z
7+
slug: website-src
8+
---
9+
Method: dual-agent (A: design review sub-agent · B: detector/browser sub-agent) — round 2, post-fix verification
10+
11+
# Design Health Score
12+
| # | Heuristic | Score | Key Issue |
13+
|---|-----------|-------|-----------|
14+
| 1 | Visibility of System Status | 4 | Loading notice, stale dim, auto-scroll — exemplary |
15+
| 2 | Match System / Real World | 4 | |
16+
| 3 | User Control and Freedom | 3 | No clear/reset affordance |
17+
| 4 | Consistency and Standards | 3 | Site-wide compressHTML missing-space text bug |
18+
| 5 | Error Prevention | 3 | |
19+
| 6 | Recognition Rather Than Recall | 4 | |
20+
| 7 | Flexibility and Efficiency | 4 | |
21+
| 8 | Aesthetic and Minimalist Design | 4 | |
22+
| 9 | Error Recovery | 4 | Prev output kept dimmed; exact parse errors |
23+
| 10 | Help and Documentation | 4 | |
24+
| **Total** | | **37/40** | **Excellent** |
25+
26+
# Verification
27+
All 11 round-1 fixes VERIFIED live (incl. pre-WASM-ready notice under 6× CPU throttle). Zero horizontal overflow at 390/768. Detector: static scan clean; oversized-h1 fixed; tiny-text down to 1 element (#comparison-scroll-hint 11.84px); line-length on .playground-footer (~160ch) and .verification-note (~110ch); table-wrap + eyebrow findings accepted in ignore.md.
28+
29+
# Priority Issues
30+
- [P1] compressHTML strips newlines before inline <a>/<code> → words jam together ("theschema support notes"); 12+ instances / 6 pages. Fix: compressHTML: false in astro.config.mjs.
31+
- [P3] Compare h1 hyphen-breaks "openapi-to-rust" at 390 → nowrap span.
32+
- [P3] Screen-reader announcement of playground states (role=alert/status present in source — verify implicit live regions suffice).
33+
- [P3] scroll-hint 11.84px; playground-footer + verification-note line length.

.impeccable/critique/ignore.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Accepted / false-positive detector findings
2+
3+
- **hero-eyebrow-chip** (all pages): the mono kicker above page-hero h1s is a deliberate,
4+
named brand system used exactly once per page (category label above the h1 in
5+
DocsLayout, `.hero-kicker` on the homepage). It is not the per-section eyebrow
6+
scaffold the rule targets. Re-flag only if eyebrows start appearing above body
7+
sections.
8+
- **cramped-padding on `.table-wrap`** (/docs/getting-started, /compare/openapi-generator):
9+
a table inside a horizontal-scroll container is conventionally flush with the
10+
container edges; cells carry their own 0.9rem/1rem padding. Judged intentional.

Cargo.lock

Lines changed: 39 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
[workspace]
2+
members = ["wasm"]
3+
# Scratch crates (spec-compile.sh, ad-hoc generator runs) live under tmp/;
4+
# they must never be adopted into this workspace.
5+
exclude = ["tmp"]
6+
17
[package]
28
name = "openapi-to-rust"
39
version = "0.7.0"
@@ -26,12 +32,13 @@ include = [
2632
]
2733

2834
[dependencies]
29-
clap = { version = "4.5", features = ["derive"] }
35+
clap = { version = "4.5", features = ["derive"], optional = true }
3036
once_cell = "1.19"
3137
prettyplease = "0.2"
3238
proc-macro2 = "1.0"
3339
quote = "1.0"
34-
reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls"] }
40+
reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls"], optional = true }
41+
url = "2"
3542
serde = { version = "1.0", features = ["derive"] }
3643
serde_json = "1.0"
3744
serde_yaml = "0.9"
@@ -50,14 +57,17 @@ insta = { version = "1.41", features = ["yaml"] }
5057
tempfile = "3.0"
5158

5259
[features]
53-
default = []
60+
default = ["cli"]
61+
cli = ["dep:clap", "dep:reqwest", "http-error"]
62+
http-error = ["dep:reqwest"]
5463
internal-tools = []
5564
specta = ["dep:specta"]
5665
test-helpers = ["dep:insta", "dep:tempfile"]
5766

5867
[[bin]]
5968
name = "openapi-to-rust"
6069
path = "src/bin/openapi-to-rust.rs"
70+
required-features = ["cli"]
6171

6272
[[bin]]
6373
name = "catalog-gen"

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99

1010
Read the [guides and documentation](https://openapi-to-rust.dev/) for the quickest path from an OpenAPI document to compiling Rust.
1111

12+
## 5-second trial
13+
14+
Paste your spec into the **[browser playground](https://openapi-to-rust.dev/playground)**
15+
the real generator compiled to WebAssembly. See the generated Rust instantly and
16+
download it as a complete, compilable crate. Nothing is uploaded.
17+
1218
## 30-second trial
1319

1420
Install the CLI from crates.io, then generate a tiny client from a stable,

scripts/build-playground-wasm.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
# Build the playground WASM bundle and stage it for the website.
3+
#
4+
# Output: website/public/playground/pkg/ (gitignored build artifact).
5+
# wasm-pack runs wasm-opt -Oz itself (configured in wasm/Cargo.toml), and the
6+
# env vars below give the release profile size-focused settings without
7+
# touching the workspace profile used by the CLI.
8+
set -euo pipefail
9+
cd "$(dirname "$0")/.."
10+
11+
DEST="website/public/playground/pkg"
12+
13+
CARGO_PROFILE_RELEASE_OPT_LEVEL=z \
14+
CARGO_PROFILE_RELEASE_LTO=true \
15+
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 \
16+
wasm-pack build wasm --release --target web --out-dir "../$DEST"
17+
18+
# Mark the bundle as a local build so fetch-playground-wasm.mjs never
19+
# clobbers it with the lock-pinned release asset.
20+
echo "local" > "$DEST/.source-tag"
21+
22+
echo
23+
echo "Playground WASM staged in $DEST:"
24+
ls -lh "$DEST" | awk 'NR>1 {print " " $9 " (" $5 ")"}'

0 commit comments

Comments
 (0)