Skip to content

Extract playground shell + IIFE runner (prep for ESM)#324

Merged
brianmhunt merged 2 commits into
mainfrom
playground/refactor-shell
Apr 17, 2026
Merged

Extract playground shell + IIFE runner (prep for ESM)#324
brianmhunt merged 2 commits into
mainfrom
playground/refactor-shell

Conversation

@brianmhunt
Copy link
Copy Markdown
Member

Summary

Splits the 565-line playground.astro into compartmentalized modules, setting up a clean boundary for adding an ESM playground in a follow-up PR.

The wedge between IIFE and ESM is just one methodbuildSrcdoc(files) → string. Everything else (editors, tabs, console capture, URL hash, status bar, run button, keybinding) is genuinely shared. This PR extracts the shared bits now so the ESM PR is purely additive.

Files

  • src/playground/styles.css — the CSS block, verbatim
  • src/playground/runner.tsRunner interface: { title, tabs, init, buildSrcdoc }
  • src/playground/shell.ts — runner-agnostic UI; builds DOM from a runner spec
  • src/playground/runner-iife.ts — current esbuild.transform + IIFE srcdoc logic
  • src/playground/entry-iife.ts — page entry; URL imports for esbuild-wasm / CodeMirror
  • src/pages/playground.astro — ~15 lines: <head> + mount point + one <script>

Design notes

Dependency injection for URL imports. The page entry does the esm.sh imports and passes them into mount(...). This keeps shell.ts/runner-iife.ts as pure TS that Vite bundles cleanly — no URL imports leaking into module code, no accidental coupling.

URL hash format unchanged. Still flat { html, js }, so existing shared playground URLs keep working.

No behavior change. This is a pure refactor; visual output and UX are identical.

Test plan

  • /playground loads
  • Both tabs (index.html, app.tsx) render editors
  • esbuild initializes ("esbuild ready" status)
  • Default JSX code compiles and renders in iframe ("Count: 0" + Increment button)
  • Run button re-compiles on demand
  • Console capture works (iframe → parent postMessage)
  • Clear button empties console
  • URL hash updates on edit

🤖 Generated with Claude Code

Splits the 565-line playground.astro into compartmentalized pieces:

- styles.css: the CSS block, unchanged
- runner.ts: Runner interface (title, tabs, init, buildSrcdoc)
- shell.ts: runner-agnostic UI (editors, tabs, console, status, URL hash)
- runner-iife.ts: current esbuild.transform + IIFE srcdoc behavior
- entry-iife.ts: page entry; wires CodeMirror/esbuild URL imports + runner
- playground.astro: ~15 lines, just <head> + mount point + script

URL hash format preserved (flat {html, js}) so existing shared URLs keep
working. Dependency injection pattern keeps URL imports (esbuild-wasm,
CodeMirror, esm.sh) at the page entry; shell/runner modules stay pure TS
that Vite bundles cleanly.

No behavior change — prepares the ground for a separate /playground/esm
page that reuses shell.ts with a different runner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 16, 2026 19:11
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@brianmhunt has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 35 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 4 minutes and 35 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d2e2fa20-ca68-44de-a362-cd31118568a6

📥 Commits

Reviewing files that changed from the base of the PR and between e4441a3 and 8d63516.

📒 Files selected for processing (6)
  • tko.io/src/pages/playground.astro
  • tko.io/src/playground/entry-iife.ts
  • tko.io/src/playground/runner-iife.ts
  • tko.io/src/playground/runner.ts
  • tko.io/src/playground/shell.ts
  • tko.io/src/playground/styles.css
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch playground/refactor-shell

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d33bf63a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tko.io/src/playground/shell.ts Outdated

<div class="editor-panel">
<div class="tabs" role="tablist" data-role="tabs"></div>
<div data-role="editor-mounts"></div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve full-height editor layout

Wrapping the editor mounts in an unstyled <div data-role="editor-mounts"> breaks the previous flex sizing: .editor-mount { flex: 1 } only works when the parent is a flex container, and this new wrapper is a normal block element. As a result, CodeMirror no longer gets the full panel height and the editor area collapses to content height, which makes the playground editor effectively unusable on load. Please make the wrapper a flex/height container (or remove it) so the mounts remain full-height.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the tko.io playground page into modular TypeScript + CSS pieces to create a clean runner boundary (via buildSrcdoc(files)) ahead of an upcoming ESM playground addition.

Changes:

  • Extracted playground styling into src/playground/styles.css and simplified playground.astro to a mount point + script.
  • Introduced a runner abstraction (Runner, tabs, init, buildSrcdoc) and implemented the current IIFE/esbuild runner as runner-iife.ts.
  • Added a runner-agnostic UI shell (shell.ts) and a dedicated page entry (entry-iife.ts) that performs URL imports and mounts the app.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tko.io/src/playground/styles.css Moves inline playground CSS into a standalone stylesheet.
tko.io/src/playground/runner.ts Defines shared runner/tab/status types to support multiple playground runners.
tko.io/src/playground/shell.ts New runner-agnostic playground UI (editors/tabs/iframe/console/status) + hashing.
tko.io/src/playground/runner-iife.ts IIFE runner implementation using esbuild.transform and srcdoc generation.
tko.io/src/playground/entry-iife.ts Entry module that performs URL imports and wires deps into mount(...).
tko.io/src/pages/playground.astro Replaces the large inline implementation with CSS import + mount + entry import.

Comment on lines +184 to +186
const files = collectFiles()
saveToHash(files)
try {
await initEsbuild()
run()
<div id="app"></div>
<script>
Comment on lines +14 to +15
const parsed = JSON.parse(json)
return typeof parsed === 'object' && parsed !== null ? parsed : null
Comment on lines +153 to +160
window.addEventListener('message', (e) => {
const data = e.data
if (data?.type === 'console') {
addConsoleMsg(data.args.join(' '), data.method)
} else if (data?.type === 'error') {
addConsoleMsg(data.message, 'error')
}
})
Codex flagged: wrapping the editor mounts in a data-role="editor-mounts"
div broke .editor-mount { flex: 1 } because the wrapper is a plain block,
not a flex container. CodeMirror collapsed to content height (~27px)
instead of filling the panel.

Fix: drop the wrapper, append mounts directly to .editor-panel (which
is already display: flex, flex-direction: column). Matches the original
DOM layout exactly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@brianmhunt brianmhunt merged commit 8d63516 into main Apr 17, 2026
8 checks passed
@brianmhunt brianmhunt deleted the playground/refactor-shell branch April 17, 2026 12:19
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.

2 participants