Skip to content

feat(stepper): render inline thumbnails for opaque values (DrRacket style) - #90

Merged
martin-henz merged 2 commits into
mainfrom
stepper-opaque-thumbnails
Aug 6, 2026
Merged

feat(stepper): render inline thumbnails for opaque values (DrRacket style)#90
martin-henz merged 2 commits into
mainfrom
stepper-opaque-thumbnails

Conversation

@martin-henz

Copy link
Copy Markdown
Member

Summary

  • common-stepper: adds an image SyntaxTemplatePart (renders a node's data-URL property as an inline <img>, falling back to nothing when absent, like child/list) and an unless part (the inverse of when, for pairing an image with a textual fallback).
  • web-stepper: renders both generically in SubstVisualizer.tsx, plus CSS (.stepper-opaque-thumbnail) sizing the image to text height, DrRacket-style, rather than expanding the line.
  • Changeset included (patch-bumps both packages).

A language opts in by using these in its SyntaxProfile templates — companion change: source-academy/py-slang#402, which wires this up for opaque module values (e.g. a rune Rune) that attach a stepper-thumbnail render hook (see source-academy/modules's opaque-thumbnails doc).

Verified live end-to-end against a local build of this branch + the py-slang PR + the already-shipped rune thumbnail hook: from rune import red, heart; red(heart) renders the plain heart thumbnail inline as the argument, and the red-colored result's own thumbnail after stepping through the call.

Test plan

  • yarn workspace @sourceacademy/common-stepper build — clean (pre-existing vitest/jest config warning unrelated to this change)
  • yarn workspace @sourceacademy/web-stepper build — clean (pre-existing common-tabs/renderFunctionArguments warnings unrelated to this change)
  • yarn eslint --concurrency=auto src/common/stepper/src/index.ts src/web/stepper/src/SubstVisualizer.tsx src/web/stepper/src/styles.ts — clean
  • Manual: standalone harness rendering both the thumbnail and text-fallback cases
  • Manual: sourceacademy.org playground, Python §2 Stepper, from rune import red, heart\nred(heart) — thumbnail renders inline at each relevant step

🤖 Generated with Claude Code

https://claude.ai/code/session_0193EaXSjQ3fGYJGCU1uDvmD

…tyle)

Adds an `image` SyntaxTemplatePart (renders a node's data-URL property as an
inline <img>, falling back to nothing when absent, like `child`/`list`) and
an `unless` part (the inverse of `when`, for pairing an image with a textual
fallback) to common-stepper. web-stepper renders both generically.

A language opts in by using them in its SyntaxProfile templates — see the
companion py-slang PR, which wires this up for opaque module values (e.g. a
`rune` Rune) that attach a stepper-thumbnail render hook.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193EaXSjQ3fGYJGCU1uDvmD
@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cab24aa

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

💥 An error occurred when fetching the changed packages and changesets in this PR
Some errors occurred when validating the changesets config:
The package or glob expression "@sourceacademy/web-stepper" is specified in the `ignore` option but it is not found in the project. You may have misspelled the package name or provided an invalid glob expression. Note that glob expressions must be defined according to https://www.npmjs.com/package/micromatch
The package or glob expression "@sourceacademy/web-data-visualizer" is specified in the `ignore` option but it is not found in the project. You may have misspelled the package name or provided an invalid glob expression. Note that glob expressions must be defined according to https://www.npmjs.com/package/micromatch

@martin-henz

Copy link
Copy Markdown
Member Author

@codearabbitai review

@martin-henz

Copy link
Copy Markdown
Member Author

Review

Small, well-scoped PR that extends the stepper's declarative template language with image and unless parts, mirroring the existing when/child/list patterns. ESLint is clean and web-stepper builds successfully (pre-existing warnings match what the PR description claims).

Three things worth addressing:

  1. Doc typosrc/common/stepper/src/index.ts:121. The unless JSDoc says it renders when node[when] is absent/falsy; should say node[unless]. The code itself is correct, only the comment is wrong.

  2. Unvalidated image srcSubstVisualizer.tsx:850. The type/doc say the bound property is "expected to be a data URL," but renderPart sets <img src={src}> from any string, with no data: prefix check. If a future module (or a bug) ever puts a non-data-URL — especially anything program-influenced — into that property, the browser will silently fetch it, leaking IP/referrer/timing to a third party on every render. Suggest guarding with src.startsWith("data:") and rendering nothing otherwise, so the contract is enforced rather than just documented.

  3. Changeset bumps an ignored package.changeset/stepper-opaque-thumbnail.md patch-bumps @sourceacademy/web-stepper, but that package is in .changeset/config.json's ignore list (bundled host plugins aren't independently published, per the convention set when web-data-visualizer was added there). Harmless no-op, but inconsistent with prior stepper PRs (e.g. feat(stepper): render a cumulative program-output panel #49) that only bumped common-stepper.

Also confirmed the changeset-bot's "package not found" error on this PR is a pre-existing CI quirk (it fires identically on already-merged PR #49) — not introduced by this PR.

- Fix `unless` doc comment, which referenced `node[when]` instead of
  `node[unless]`.
- Enforce that the `image` part's src is actually a `data:` URL before
  rendering it, instead of trusting any string — a live network URL there
  would let a render silently leak the viewer's IP/referrer to a
  third-party host.
- Drop the no-op `@sourceacademy/web-stepper` patch bump from the
  changeset: that package is in .changeset/config.json's `ignore` list
  (bundled host plugin, not independently published), matching how prior
  stepper-only changes (e.g. #49) only bumped common-stepper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017rkUEePRmhJmjB3whkhU6s
@martin-henz
martin-henz merged commit 065da98 into main Aug 6, 2026
2 checks passed
@martin-henz
martin-henz deleted the stepper-opaque-thumbnails branch August 6, 2026 14:42
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