Skip to content

hoai2026: levelbuilder update - #75109

Open
breville wants to merge 5 commits into
stagingfrom
hoai2026-levelbuilder-updates
Open

hoai2026: levelbuilder update#75109
breville wants to merge 5 commits into
stagingfrom
hoai2026-levelbuilder-updates

Conversation

@breville

@breville breville commented Sep 4, 2026

Copy link
Copy Markdown
Member

[written by Claude]

Newer Sprite Lab in Lab2 flags ship before their editing UI: show_world_tab, guide_steps, visible_tabs, images_advanced and friends appear nowhere on the level edit page — today they can only be set by editing .level files on a dev checkout. This adds an escape hatch: a collapsed "Additional properties (JSON)" section at the bottom of the level edit page, for saved Sprite Lab levels with Lab2 enabled.

Entirely client-side

The PR is one JS module (plus its test and an import line) — not a single server change. Everything the feature needs already exists:

  • Values come from the level's level_properties endpoint, the same one the lab itself reads.
  • Saves go through the levelbuilder-authorized update_properties endpoint, which App Lab's starter-code editor has used for years, and which logs who changed what.
  • The code rides the levelbuilder bundle the edit page already loads (which already contains the CodeMirror setup it uses), injected after the level form. On any other page, or a level that isn't Sprite Lab in Lab2 (checked against the form's exact level type — Dancelab and Poetry subclass GamelabJr and are deliberately excluded — and the saved Lab2 checkbox), it does nothing.

An explicit allowlist, not a general property editor

The editable set is a constant — ten flags today. The dump enumerates exactly those keys, set or not (null = unset), so it reads like a form of the available knobs, and the box refuses any other key with "…isn't in this level's editable list." Every other property keeps its own editor — form fields, the blocks editors, the encrypted values — by construction, and the list doubles as the working list of flags still waiting for real UI: remove an entry when its editor lands, and the section retires with the last one.

  • The values box is the levelbuilder CodeMirror with a JSON lint gutter. Keys merge by whole top-level key — a value replaces its property outright, so a nested tweak is copy the value from the dump, edit, save it back. A key set to null — or to any empty or false value — is removed: properties store no blanks, so flags are true or absent. The help text says all of this.
  • A successful save refetches the dump from the server, so it always shows what was actually stored (a removed value shows as null) — it is the documented copy-source for nested edits, so it must never show submitted-but-unstored values. The "Saved." status annotates itself when the box holds edits newer than what was saved, and when the level is unpublished — an unpublished level's save reaches the database but not its .level file, so it warns to publish before the value can survive a seed. The section sits outside the level form on purpose: it saves through its own endpoint, and sharing the form's submit or dirty-check machinery would discard unsaved edits elsewhere on the page.

Screenshot

Screenshot 2026-09-04 at 11 00 29 PM

Testing

  • A unit test pins the seam that must not drift: the snake-to-camel name mapping for every editable key (values are read by the camelized names level_properties serves and saved by the stored snake_case names). Because a save refetches rather than mirroring, the client never needs its own copy of the server's blank-stripping rule.
  • Driven end to end in the browser the way a person uses it: expand the section by clicking its heading, type {"show_world_tab": true} into CodeMirror, click Save — and the level itself gains the World tab on next load; typing null and saving removes it again. The dump shows all ten keys (null for unset, real values for set ones), a non-allowlisted key (start_html) is refused with a plain message, and on this unpublished scratch level the save status carried the publish-to-persist warning. On a classic (non-Lab2) Sprite Lab level the section does not render. Lint and yarn run typecheck pass.

History note

This branch was rewritten before review: earlier exploratory commits built the same feature server-side (a haml partial, a model allowlist, and update_properties endpoint hardening). The endpoint hardening — refusing encrypted properties and their plaintext twins, 400s with reasons for malformed bodies and values the save hooks reject — is parked as a candidate separate PR, since it protects the endpoint's other callers and is orthogonal to this UI.

@breville breville added the AI generated This PR has been substantially generated using AI. label Sep 4, 2026
@breville breville changed the title Levelbuilder: raw additional-properties editor on the level edit page hoai2026: levelbuilder updates Sep 4, 2026
@breville
breville requested review from dju90 and katiejofr September 4, 2026 04:54
@breville
breville force-pushed the hoai2026-levelbuilder-updates branch 2 times, most recently from 7bec7fc to 649506b Compare September 5, 2026 05:17
Newer lab flags ship before their editing UI: show_world_tab,
guide_steps, visible_tabs and friends can only be set by editing .level
files on a dev checkout. This injects a collapsed "Additional
properties (JSON)" section onto the level edit page for saved Sprite
Lab levels with Lab2 enabled.

Entirely client-side — everything it needs from the server already
exists: values come from the level_properties endpoint the lab itself
reads, saves go through the levelbuilder-authorized update_properties
endpoint App Lab's starter-code editor already uses, and the module
rides the levelbuilder bundle the edit page already loads. The editable
set is an explicit allowlist (RAW_EDITABLE_PROPERTIES): the dump
enumerates exactly those keys (null = unset) and the box refuses
everything else, so every other property keeps its own editor and the
list doubles as the burndown of flags still lacking real UI.

Replaces earlier exploratory commits on this branch (history rewritten
pre-review), including a dashboard-side design whose endpoint hardening
is parked for a possible separate PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@breville
breville force-pushed the hoai2026-levelbuilder-updates branch from 649506b to 9fbfc68 Compare September 5, 2026 05:21
breville and others added 2 commits September 5, 2026 05:35
The dump always shows what the server stored: a successful save
refetches level_properties (epoch-guarded against a stale in-flight
load) instead of mirroring the submitted values client-side, which also
retires the client's copy of the blank-stripping rule. "Saved."
annotates itself when the box gained newer edits during the request and
when the level is unpublished — an unpublished level's save reaches the
database but not its .level file, so the next seed would drop it without
a publish. A CodeMirror initialization failure puts the plain textarea
back instead of leaving a hidden input. Both requests send
Accept: application/json.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CodeMirror's lint callback is debounced, so a lint pass for pre-save
typing can land during the save window; the edited-during-save suffix
now compares the box's text against what was sent instead of trusting
lint timing. Caught by driving the editor the way a person does — click
the heading, type into CodeMirror, click Save.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@breville breville changed the title hoai2026: levelbuilder updates hoai2026: levelbuilder update Sep 5, 2026
breville and others added 2 commits September 5, 2026 07:11
"Editor chrome" in plain words.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@breville
breville requested a review from a team September 5, 2026 07:14
@breville
breville marked this pull request as draft September 6, 2026 23:35
@breville
breville marked this pull request as ready for review September 7, 2026 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI generated This PR has been substantially generated using AI.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant