Skip to content

App Layout Variants - #82

Draft
aaronsmulktis wants to merge 2 commits into
facebookresearch:aaronsmulktis/themes-and-layoutsfrom
aaronsmulktis:aaronsmulktis/app-layouts
Draft

aaronsmulktis wants to merge 2 commits into
facebookresearch:aaronsmulktis/themes-and-layoutsfrom
aaronsmulktis:aaronsmulktis/app-layouts

Conversation

@aaronsmulktis

@aaronsmulktis aaronsmulktis commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

In an effort to introduce additional layouts for each app to increase variety in eval heuristics, this PR presents the following additional layouts:

App Layout What changes
calendar agenda_first Lands on the agenda rather than the month grid; the view toggle leads with it
sidebar_nav Month nav and view toggle leave the horizontal bar and become a left rail
messenger split_inbox Opening a chat keeps the conversation list on screen beside the thread
compact_list Dense avatar-less rows; thread messages flatten from bubbles to left-aligned rows
maps sidebar_left Search, Saved Locations and route planning move to the left of the map
bottom_sheet Sidebar becomes a full-width panel under the map; saved places scroll horizontally
code_editor sidebar_right File tree to the right of the editor
top_tree No side column — the tree becomes a horizontal strip above the editor

That closes the gap on every app except one:

App Layouts introduced back in #72
todo default, kanban_board
start_page default, broken_logos, clickable_logos

onlineshop is excluded deliberately. It is off by default and gated on OpenJDK 21 plus a
downloaded dataset, so its layouts cannot be screenshot-tested in the default CI path, and a
full Python rewrite is in flight on aaronsmulktis/webshop-rewrite. Layouts written against
the current implementation would be written against code on its way out.

How it works

Each app now follows the todo convention introduced in #72: declaring a - layout: default in their defaults and a current_layout() helper reads the config and the page handlers branch on it.
Layout files use defaults: - default so a variant states only what it changes.

Introducing reusable markup functions:

  • code_editor — the three page handlers all built the same
    Div(cls="flex space-x-2")(side_bar, main_screen). That is now one editor_page(), so a
    new variant is added in one place instead of three.
  • messenger — the chat list is extracted to conversation_list(selected=) so
    split_inbox can render it beside a thread. The layout is applied as a class on <body>,
    because the max-w-lg cap that makes this app phone-shaped lives there and a descendant
    cannot widen its own ancestor.
  • maps — the layout reaches templates/map.html as a <body> class. Both variants are
    CSS-only, so none of the template's JS needs to know which is active.

Reward safety

Layouts change structure only. Routes, element ids and the /<app>_all endpoints are
identical across variants, and rewards are scored from state rather than the DOM, so no
existing task changes behaviour under any of these. The selectors tasks and screenshots key
off — #todo-list, .calendar-table, .agenda-list, #map, #editor, #wrapper — are
preserved in every variant.

No MCP change was needed: registry.py discovers variants by globbing
config/apps/<app>/layout/*.yaml, so the new files surface in list_variants automatically.

Also included

A second commit fixes an unrelated, pre-existing bug the screenshots surfaced: the code
editor's file tree, "No file selected" heading and Language/Theme labels are all marked
text-white, left over from when the app painted its panels a hard-coded blue. The theme
migration repointed .main-content at var(--color-bg) — white in the default theme — so
that chrome was rendering white-on-white in every layout, including default. It is not
covered by the Code Editor Tasks PR, whose code-editor changes are the save method and an
editor_binding() refactor.

Worth noting how it was found: every markup assertion passed the whole time. The text was
present and merely the same colour as what was behind it. Only reading the generated images
caught it.

Test plan

  • uv run -m pytest tests/ → 768 passed, 7 skipped (the skips are the Java-21-gated shop)
  • ruff → no new findings; diffed against a stashed baseline, the pre-existing count is
    unchanged
  • Each layout rendered through a TestClient and asserted on markup with <style>/<script>
    stripped — the inline stylesheets contain every variant's rules, so naive negative
    assertions produce false passes
  • Confirmed sidebar_right performs a real DOM reorder rather than a visual row-reverse,
    so the accessibility tree matches what is rendered
  • Screenshots captured for all eight variations and reviewed by eye

Screenshot coverage adds the eight variations plus a new messages_thread route: both
messenger layouts change the thread view, which the existing /messages list route never
renders, so split_inbox would otherwise have been untestable. Alice is present in every
content variation.

messages_thread

…itor

The theme/layout split landed with the layout axis nearly empty: calendar,
messenger, maps and code_editor all declared `- layout: default` in their
defaults but nothing read `config.<app>.layout`, so their layout groups were
inert placeholders and structure was held almost constant across sweeps.

  calendar     agenda_first   lands on the agenda, not the month grid
               sidebar_nav    month nav + view toggle become a left rail
  messenger    split_inbox    chat list stays beside the open thread
               compact_list   dense avatar-less rows; flat messages
  maps         sidebar_left   search + Saved Locations left of the map
               bottom_sheet   sidebar becomes a panel under the map
  code_editor  sidebar_right  file tree right of the editor
               top_tree       file tree as a strip above the editor

Follows the `todo` convention: a `current_layout()` helper reads the config
and the page handlers branch on it. Three supporting refactors, each of which
removes a reason to duplicate:

- code_editor: the three page handlers all built the same
  `Div(cls="flex space-x-2")(side_bar, main_screen)`; that is now a single
  `editor_page()` so a new variant is added in one place instead of three.
- messenger: the chat list is extracted to `conversation_list(selected=)` so
  `split_inbox` can render it beside a thread. The layout is applied as a
  class on <body>, because the `max-w-lg` cap that makes this app
  phone-shaped lives there and a descendant cannot widen its own ancestor.
- maps: the layout reaches templates/map.html as a <body> class; both
  variants are CSS-only, so none of the template's JS knows about them.

Layouts change structure only. Routes, element ids and the `/<app>_all`
endpoints are identical across variants, so rewards -- which read state, not
the DOM -- are unaffected. Screenshot coverage adds the eight variations plus
a `messages_thread` route: both messenger layouts change the thread view,
which the existing list route never renders. Alice exists in every content
variation.
The file tree, the "No file selected" heading and the Language/Theme labels
are all marked `text-white`, from when this app painted its panels a
hard-coded blue. The theme migration repointed `.main-content` at
`var(--color-bg)` -- white in the default theme -- so that chrome became
invisible in every layout, including `default`.

Point `.text-white` inside `.main-content` at `var(--color-fg)`, and restore
white for the elements that carry their own dark fill: the mode/theme
selects and tab strip (`bg-gray-*`) and the highlighted folder row
(`bg-blue-*`).

Found by reading the generated screenshots rather than the DOM -- the markup
assertions passed throughout, since the text is present and merely the same
color as what is behind it.

Not covered by the Code Editor Tasks PR (aaronsmulktis/editor-tasks), whose
code-editor changes are the save method and an `editor_binding()` refactor;
it touches neither `text-white` nor `.main-content`.
@aaronsmulktis aaronsmulktis self-assigned this Sep 29, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Sep 29, 2026
@aaronsmulktis
aaronsmulktis changed the base branch from main to aaronsmulktis/themes-and-layouts September 29, 2026 21:02

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant