Skip to content

Add a code playground component - #1170

Merged
TomNaessens merged 1 commit into
feature/code-playgroundfrom
feature/shared-output-rendering
Oct 26, 2026
Merged

TomNaessens merged 1 commit into
feature/code-playgroundfrom
feature/shared-output-rendering

Conversation

@TomNaessens

@TomNaessens TomNaessens commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review map: dodona-edu/dodona#9402 (step 3 of 7: what each pull request does, the review order and live status)

This pull request adds <p-code-playground>: a small runnable editor for code blocks in Dodona's reading activities, moved from Dodona's <dw-code-playground> into Papyros. It is step 3 of 5; see #1168 for the overview.

Stacked on #1168. Dodona side: dodona-edu/dodona#9304 replaces its component with a thin wrapper around this one.

How to review

Most of CodePlayground.ts is a port of Dodona's component, reviewed in dodona-edu/dodona#8648 (including a WCAG pass). The useful view is the diff against that original:

git -C ../dodona show bfa2d95c4:app/assets/javascript/components/code_playground/code_playground.ts > /tmp/dodona-playground.ts
git diff --no-index /tmp/dodona-playground.ts src/frontend/components/CodePlayground.ts

About 200 of its added lines are the styles, moved from Dodona's code_playground.css.scss into the component and switched from --d-* to the --md-sys-color-* tokens Dodona already maps.

What changed in the port:

  • Each playground is its own Papyros instance on a shared runtime (Let several Papyros instances share one runtime #1168), so Dodona's scheduler is gone: output comes from the instance's own io, and "another playground is running" is read from the runtime.
  • The editor is the sandbox's p-code, bound to the instance's runner.code; errors and images render through the shared helpers from Share rendering and helpers between the sandbox components #1169; the status line shows runner.stateMessage.
  • Shadow DOM instead of Dodona's light DOM.
  • Typed input is not echoed into the output, the same as in the sandbox. Dodona's version did echo it; that was a design choice at the time, not a requirement.
  • Launching when the first playground comes into view is ported from dodona-edu/dodona#9214 (playground/preload.ts).

Kept as in Dodona: Run/Stop with aria-disabled (never native disabled, which drops focus), the transcript as role="log", focus moves, the reserved output height so the page doesn't jump, and scroll-follow only when already at the bottom (through p-output's FollowEnd controller from #1169, so an image that gets its height late no longer stops it).

Manual testing instructions

  1. yarn vitest run --exclude '**/.claude/**'
  2. Through dodona-edu/dodona#9304 with a local build of this branch: a page with several playgrounds runs each block, gates the others while one runs, and Stop, Reset, input() and tracebacks work.
Screenshot (Dodona test page)

Playground

Checklist

  • Tests: CodePlayground.test.ts (ported from Dodona, plus the preload); full suite green locally (466)
  • Accessibility: behaviour and its tests ported from the audited Dodona component
  • Docs: README "Code playgrounds"
  • AI: Claude Code wrote the change

🤖 Generated with Claude Code

@TomNaessens TomNaessens added the enhancement New feature or request label Sep 29, 2026
@TomNaessens TomNaessens self-assigned this Sep 29, 2026
@TomNaessens
TomNaessens added this pull request to stack #1184 October 7, 2026 11:05
@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from dd061b5 to 890c5e0 Compare October 7, 2026 11:17
@TomNaessens TomNaessens changed the title Share output rendering between the sandbox and the playground Add a code playground component Oct 7, 2026
@TomNaessens TomNaessens removed the enhancement New feature or request label Oct 7, 2026
@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 890c5e0 to 069c8fc Compare October 7, 2026 12:28
@TomNaessens
TomNaessens removed this pull request from stack #1184 October 7, 2026 12:38
@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 069c8fc to fd519ec Compare October 7, 2026 12:39
@TomNaessens
TomNaessens added this pull request to stack #1186 October 7, 2026 12:39
Comment thread src/frontend/components/CodePlayground.ts Outdated
Comment thread src/frontend/components/CodePlayground.ts
@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch 2 times, most recently from 270aa63 to 125e25b Compare October 7, 2026 13:55
@TomNaessens
TomNaessens marked this pull request as ready for review October 7, 2026 14:31
Comment thread src/frontend/components/CodePlayground.ts Outdated

@bmesuere bmesuere left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖 Reviewed this PR against its stack base. One reproducible issue is described inline. The additional regression check reproduces it with a real Python run. Validation of the complete stack at 530ba26: 334 existing tests passed; TypeScript checking, ESLint, and the app build passed.

@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 125e25b to 6f235da Compare October 7, 2026 16:02
@TomNaessens
TomNaessens removed this pull request from stack #1186 October 7, 2026 20:49
@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 6f235da to 0e267e0 Compare October 7, 2026 20:49
@TomNaessens

Copy link
Copy Markdown
Contributor Author

🤖 Rebase notes for #1194 (reviewed at 910fb15). The conflicts at this level come from #1168 and #1169; the notes there have the details.

@TomNaessens

Copy link
Copy Markdown
Contributor Author

🤖 Rebase notes for #1195 (reviewed at a5eedf7). The conflicts at this level come from #1169; the note there has the details. This one is about a design overlap, not a conflict.

@TomNaessens

Copy link
Copy Markdown
Contributor Author

🤖 todo (non-blocking): once #1196 is merged, the README has a host contract for Escape: Papyros calls preventDefault() on the Escape keydowns it acts on (Escape followed by Tab out of the code editor, and the rename and add-file inputs), so a host can close its own UI only on an Escape whose defaultPrevented is false.

<p-code-playground> embeds p-code-editor, so the contract applies to it too, and Dodona's playgrounds in descriptions (#9304, #9305) will rely on it. When rebasing:

  1. If Cancel the Escape keydowns that Papyros acts on #1196 still documents the contract under <p-code-runner> (I suggested moving it to a page-level section there), mention it in the "Code playgrounds" section too.
  2. Check that a playground's editor cancels its Escape, like the sandbox editor (the listener lives on p-code-editor, so this should hold as is).
  3. The playground's own input (onInputKeydown) doesn't act on Escape, so it should stay uncancelled. Nothing to change there.

Comment thread src/frontend/components/CodePlayground.ts Outdated
@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from fe686e5 to 89c1195 Compare October 13, 2026 14:09
@TomNaessens

Copy link
Copy Markdown
Contributor Author

🤖 Rebased onto main as 89c1195:

@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 89c1195 to 2aa73de Compare October 13, 2026 14:41
@TomNaessens

Copy link
Copy Markdown
Contributor Author

🤖 Simplify pass after the rebase (now 2aa73de):

  • The playground follows the end through FollowEnd (Share rendering and helpers between the sandbox components #1169), so an image that gets its height after the render no longer stops it (new test; it failed on the old code).
  • The reserved height floor lives in CSS (min-height: max(22px, var(--reserved-height, 0px))).
  • The transcript renders its chunks as an array, so a new chunk adds a text node instead of rebuilding the whole text.
  • .error-title moved into outputStyles with the markup it styles; dead :disabled selectors and the test-only outputs getter removed.

@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 2aa73de to 8c08e2e Compare October 16, 2026 11:12
@TomNaessens

Copy link
Copy Markdown
Contributor Author

🤖 note: Papyros.unsupported_browser now reads "Python could not start in this browser. Close other tabs, update your browser or use another one." (nl: "Python kon niet starten in deze browser. Sluit andere tabbladen, werk je browser bij of gebruik een andere browser.").

Dodona reworded its playground message to this on main (a boot failure can also be a device out of memory), and with this PR the playground shows Papyros's own string instead of Dodona's. The sandbox uses the same string, so it changes there too.

@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 8c08e2e to 7c6741c Compare October 16, 2026 11:44

@TomNaessens TomNaessens left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤖 Automated review on Tom's behalf; summary below.


Generated by Claude Code

Comment thread src/frontend/components/playground/status.ts Outdated
Comment thread src/frontend/components/playground/instance.ts

Copy link
Copy Markdown
Contributor Author

🤖 Minor findings. Plain review (no code-review skill) with a checkout of the PR head. Covers the full PR against its base #1168 (step 3 of the stack in dodona-edu/dodona#9402), compared with Dodona's original component at bfa2d95c4.

Two inline comments: the status spinner ignores reduced motion (a regression from the original), and a default-instance playground drops its errors. Checked and fine:

  • listener, observer and FollowEnd cleanup;
  • the deferred dispose on removal, and moves within the page;
  • the Reset focus hand-off;
  • aria-describedby/labelledby ids and role="alert" placement.

Of the original tests, only "keeps transient status out of the toolbar" wasn't ported. The vitest suite wasn't run here: the installed Chromium build doesn't match the one this Playwright expects.


Generated by Claude Code

@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 7c6741c to 4b838eb Compare October 26, 2026 13:14
@TomNaessens

Copy link
Copy Markdown
Contributor Author

🤖 note: Changed since the approval (4b838eb): fixes for three review-routine findings.

  • The status line shows no spinner under prefers-reduced-motion: reduce.
  • The playground's input row ignores Enter with keyCode 229 (Safari IME).
  • The README says what a playground without .papyros runs with.

<p-code-playground> is a small runnable Python editor for inline code blocks,
moved from Dodona's <dw-code-playground>: Run and Reset, inline input, and the
output and errors rendered with the same helpers as p-output, and an output
log that follows its end the way p-output does. Under reduced motion the
status line leaves its spinner out, as p-run-state does. Every
playground gets its own Papyros instance, on a runtime shared by every
playground the host gave none, with Papyros's defaults (no error handler,
default asset URLs, English); playgrounds that share a runtime boot Python
once, when the first of them scrolls into view, and take turns running. Every
run starts from an empty workspace.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@TomNaessens
TomNaessens force-pushed the feature/shared-output-rendering branch from 4b838eb to bcad724 Compare October 26, 2026 13:29
@TomNaessens
TomNaessens merged commit f985994 into main Oct 26, 2026
6 checks passed
@TomNaessens
TomNaessens deleted the feature/shared-output-rendering branch October 26, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants