From b94c1a6557b0be1d8b89d6bd2e04f227f7fcb0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sun, 23 Aug 2026 23:07:56 +0000 Subject: [PATCH] workspace layout: two empty panels left, the game right MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arrangement the rest of the app uses for controls beside a surface, and the same shape as Atelier's. 25/75 rather than Atelier's 30/70, because the board is drawn at a whole-number scale and a quarter of the width is enough to drop it a step. The left panels are `appType: null`, which is the framework's empty-panel state rather than a missing app. That distinction is load-bearing here: PluginScreen passes `appTypes.allowed` built from the plugin's own panel keys, and this plugin has exactly one. Anything not on that list is rewritten to the fallback — so if `null` were not explicitly allowed by `normalizeLayout`, this layout would render three Minesweepers. It is allowed, and there is a test for it. The screen is `locked`, so an empty panel renders as a plain bordered box with no app picker. That is the intended look until something goes in them. The game panel keeps the id `minesweeper` it had as a single panel, so any per-panel state keyed on it survives. Checked before writing it: no persisted layout exists for this screen in `user_state`, so the new default actually applies rather than being shadowed by a saved one. Co-Authored-By: Claude Opus 5 Officer-Session: de3ed4e6-f88a-429c-94e6-a760336ba83b --- web/layout.ts | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/web/layout.ts b/web/layout.ts index 2fe3dc4..f8e3bb6 100644 --- a/web/layout.ts +++ b/web/layout.ts @@ -1,9 +1,35 @@ import type { LayoutNode } from 'officerdev'; -// One panel. The game is one fixed-size stage and there is nothing to put beside it — a split here -// would be a second panel of empty space. +// Two panels stacked on the left, the game on the right — the same arrangement Atelier uses, and the one +// the rest of the app reads as "controls beside a surface". +// +// The left column is EMPTY on purpose. `appType: null` is a state of the framework rather than a missing +// app, and `normalizeLayout` allows it explicitly, so these survive the plugin's own allow-list instead of +// being rewritten to the fallback — which on a one-panel plugin would have rendered three Minesweepers. +// The screen is `locked`, so an empty panel here is a plain bordered box with no app picker. That is the +// intended look until something goes in them. +// +// The game panel keeps the id `minesweeper` it had when this was a single panel, so any per-panel state +// already keyed on it survives the change. export const defaultLayout: LayoutNode = { - type: 'panel', - id: 'minesweeper', - appType: 'minesweeper', + type: 'group', + id: 'minesweeper-root', + direction: 'horizontal', + children: [ + { + node: { + type: 'group', + id: 'minesweeper-side', + direction: 'vertical', + children: [ + { node: { type: 'panel', id: 'minesweeper-side-top', appType: null }, size: 50 }, + { node: { type: 'panel', id: 'minesweeper-side-bottom', appType: null }, size: 50 }, + ], + }, + // 25 rather than Atelier's 30: the board is drawn at a whole-number scale, so the panel losing a + // quarter of its width can drop it a step. 75% of a typical panel still clears 2x. + size: 25, + }, + { node: { type: 'panel', id: 'minesweeper', appType: 'minesweeper' }, size: 75 }, + ], };