Part of the layout-DSL adoption epic #108.
Port the app-shell demos in @jsvision/examples from the imperative new Group() → .layout → .add() idiom to col/row/grow/fixed. Extra weight here: examples are the first thing people read to learn the framework, so these should model the recommended composition idiom, not the raw one. Behavior-preserving — the demos must render identically.
Portable composition + sizing sites concentrate in these files (line refs are exact):
Clean wins (do these)
editor-demo/main.ts (S, zero risk) — the cleanest: root = col(grow(ed), fixed(ind, 1)) (sites 67/68/69).
event-demo/main.ts (M) — app-shell header/body/dialog/status. row({ fixed:1, gap:2 }, btnOk, btnOpen) (107/109), col({ fixed:2, background:'dialog' }, dialogLabel, btnClose) (114/116/119), col({ padding:1, background:'desktop' }, header, body, dialog, status) (103/124/128). Layout is incidental scaffolding for the event-loop demo → low-risk.
controls-demo/main.ts (S–M) — keep the data-driven loop: const form = col({ padding:1, gap:0, background:'window' }); for (const [v, rows] of …) form.add(fixed(v, rows)); (sites 95/107).
router-demo/main.ts (M) — the list route build() closure → col({ padding:1, background:'window' }, fixed(title,1), grow(listView)) (101/104/111). The DetailScreen extends Group stays self-config (site 59), but its children become this.add(fixed(title,1)) / fixed(hint,1) / fixed(back,2) (63/65/67).
kitchen-sink/stories/drill-down.story.ts (S–M) — mirror of router-demo: col({ background:'window' }, grow(list)) (69/77) + the same DetailScreen child sizings (32/34/36). Story at() infra around it stays absolute.
chrome-bars-demo/main.ts (S, trivial) — one-line consistency fix win.add(grow(body)) (site 99); the file already imports spacer/fixed.
Judgment calls (flag on the PR, decide with the reviewer)
view-demo/main.ts (M) — a textbook col-over-row app-shell (sites 50/53/57/59/61/67/77) that ports beautifully, but its stated job is to teach the raw View/Group spine. Porting arguably teaches the recommended idiom better — but confirm that's the intent before changing the lesson.
layout.story.ts (S) — site 31 is a clean row(...), but (a) the local variable is literally named row (collides with the builder — needs a rename) and (b) the story exists to teach the raw cell-native flex engine. Port only if the intent shifts to "here's the DSL over the engine."
Out of scope (surveyed, not portable — leave as-is)
Every deliberate absolute-coordinate demo using the shared at()/place() idiom (dropdowns, containers, date, color, tabs, tree, surface, feedback, table, files, playground, themes, controls-live, wizard-demo, amiga-clock, tvedit-demo, kitchen-sink shell.ts/story.ts), and the already-ported exemplars (recipes/form-dialog.ts, layout-dsl.story.ts, forms-showcase.story.ts, layout-dsl-playground/*, status-bar.story.ts). The *.layout.rect = window-placement mutations are also out of scope.
Note: wizard-demo and themes-demo define local place()/row() helpers that look like the DSL but aren't — see the cleanup issue.
Acceptance
- Each ported demo renders identically (headless smoke + a manual run).
yarn verify green; yarn lint:fix run before the PR.
Part of the layout-DSL adoption epic #108.
Port the app-shell demos in
@jsvision/examplesfrom the imperativenew Group()→.layout→.add()idiom tocol/row/grow/fixed. Extra weight here: examples are the first thing people read to learn the framework, so these should model the recommended composition idiom, not the raw one. Behavior-preserving — the demos must render identically.Portable composition + sizing sites concentrate in these files (line refs are exact):
Clean wins (do these)
editor-demo/main.ts(S, zero risk) — the cleanest:root = col(grow(ed), fixed(ind, 1))(sites 67/68/69).event-demo/main.ts(M) — app-shell header/body/dialog/status.row({ fixed:1, gap:2 }, btnOk, btnOpen)(107/109),col({ fixed:2, background:'dialog' }, dialogLabel, btnClose)(114/116/119),col({ padding:1, background:'desktop' }, header, body, dialog, status)(103/124/128). Layout is incidental scaffolding for the event-loop demo → low-risk.controls-demo/main.ts(S–M) — keep the data-driven loop:const form = col({ padding:1, gap:0, background:'window' }); for (const [v, rows] of …) form.add(fixed(v, rows));(sites 95/107).router-demo/main.ts(M) — thelistroutebuild()closure →col({ padding:1, background:'window' }, fixed(title,1), grow(listView))(101/104/111). TheDetailScreen extends Groupstays self-config (site 59), but its children becomethis.add(fixed(title,1))/fixed(hint,1)/fixed(back,2)(63/65/67).kitchen-sink/stories/drill-down.story.ts(S–M) — mirror of router-demo:col({ background:'window' }, grow(list))(69/77) + the sameDetailScreenchild sizings (32/34/36). Storyat()infra around it stays absolute.chrome-bars-demo/main.ts(S, trivial) — one-line consistency fixwin.add(grow(body))(site 99); the file already importsspacer/fixed.Judgment calls (flag on the PR, decide with the reviewer)
view-demo/main.ts(M) — a textbookcol-over-rowapp-shell (sites 50/53/57/59/61/67/77) that ports beautifully, but its stated job is to teach the raw View/Group spine. Porting arguably teaches the recommended idiom better — but confirm that's the intent before changing the lesson.layout.story.ts(S) — site 31 is a cleanrow(...), but (a) the local variable is literally namedrow(collides with the builder — needs a rename) and (b) the story exists to teach the raw cell-native flex engine. Port only if the intent shifts to "here's the DSL over the engine."Out of scope (surveyed, not portable — leave as-is)
Every deliberate absolute-coordinate demo using the shared
at()/place()idiom (dropdowns,containers,date,color,tabs,tree,surface,feedback,table,files,playground,themes,controls-live,wizard-demo,amiga-clock,tvedit-demo, kitchen-sinkshell.ts/story.ts), and the already-ported exemplars (recipes/form-dialog.ts,layout-dsl.story.ts,forms-showcase.story.ts,layout-dsl-playground/*,status-bar.story.ts). The*.layout.rect =window-placement mutations are also out of scope.Note:
wizard-demoandthemes-demodefine localplace()/row()helpers that look like the DSL but aren't — see the cleanup issue.Acceptance
yarn verifygreen;yarn lint:fixrun before the PR.