feat(steps): lay out screen pictures by region and render region captions
Adds region-based layout support for screens: steps now carry region information, and the server packs regions into dedicated bands with per-region captions. UI changes introduce RegionCaption and region-aware step rendering; StepsModel and related views (StepsView) consume region data, while the region-aware layout keeps anchor and region boundaries intact. Tests and docs updated to reflect region-driven organization and visualization of screen regions. This enables visualizing a screen’s picture as region-based columns rather than a single distance-driven row.
This commit is contained in:
@@ -14,6 +14,10 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
### New Features
|
||||
|
||||
- **A busy screen's picture is laid out by the parts of the screen.** A screen is a set of handlers with no order between them, so on a hub screen the old rows-by-distance collapsed into one enormous row — the main screen of one app put 89 boxes side by side on a canvas over 28,000px wide, every line a near-horizontal sweep across all of it. The Steps tab now groups a screen's picture by region — the component that owns each handler, named in a small caption over its boxes — with each region a column where a step sits above what it sets in motion, tiled in the screen's own source order. At rest the picture hides only two things: the screen's own fan-out — one line into each region stands in for it — and lines that point back up; every other line draws where it leads, between two regions included, and selecting a step brings out its whole story in the side panel, link by link. A box nothing points at is the screen's own doing — run on render or mount, or from a binding written inline — the key says so, and selecting it lights its line from the screen with what fires it. The same app's widest screen now lays out under 3,500px with every line local, and the whole picture fits on screen when it opens. Endpoints, handlers and the in-order reading are untouched, and nothing needs a re-index: the regions come from the same walk that draws the steps.
|
||||
|
||||
- **A dialog's buttons fire what they run.** `Alert.prompt('Add Folder', …, [{ onPress: (name) => createBackgroundFolder(name) }])` is two facts: the prompt is a call that leaves the index, and its button fires the handler. The handler's line now arrives from the dialog's own box — with the condition on it — instead of from the screen, so the confirm-then-act chains a mobile app is full of read as chains: the delete alert leads to the delete, which leads to the request it sends. The same holds for anything bound inside the arguments of a call that leaves the index. Nothing needs a re-index: it is read from the source at request time.
|
||||
|
||||
- **The Steps tab draws a handler in the order its code runs.** The picture of what an endpoint sets in motion put the lookup, the token signing, the 200 and the 401 side by side, because each is one step from the anchor — true, and not how the code reads. Now a handler, an endpoint or any function opens as the same picture laid out by *when* things happen: a line means **and then**, so the 200 sits below the token signing it is built from and the 401 branches off the check that chose it. Where the code forks — an `if`, a `switch`, a `try`, an early exit — the line says what has to hold, and an arm that answers the request, returns or throws simply has nothing leaving it. A call written inside another call's arguments happens first, so the token is signed before the reply that carries it. A helper is drawn where it is called (`via generateToken`), a body that repeats says so (`for each item of items`), and work registered to run later (`later · then`) or started at once (`together · Promise.all`) says that rather than pretending to be a sequence. A screen still opens as before — its handlers fire on events and have no order between them — and either reading is one click, or one `&view=order` / `&view=tree` in the link, away. Nothing to re-index: it is read from the source at request time, and where the conditions cannot be read the picture is a plain sequence rather than an invented structure.
|
||||
|
||||
- **A Next.js app lands on the Screens tab like a mobile app.** App Router pages (`app/(group)/blog/[slug]/page.tsx` → `/blog/:slug`) and Pages Router pages are screens bound to the component they export; `<Link href>`, an internal `<a href>`, `router.push` / `router.replace` (`next/navigation` and `next/router`), `redirect()` / `permanentRedirect()` in a server action or a page, and the middleware's `NextResponse.redirect(new URL('/login', req.url))` are the transitions between them — each attributed back to the page it starts on with the plumbing folded and the condition on the arrow, a link written in markup drawn dashed as an inferred hop. `app/api/**/route.ts` exports (`GET`, `POST`, …) are endpoints bound to their functions, `pages/api/*` handlers are `ANY /api/…`, and a page's Steps picture fires from its load (`FIRES FROM page load · /users`), draws the data it reads, the handlers it wires, the server actions it crosses to and the pages it leads to as boundaries. A response's status written as `{ status: 201 }` is read too. Re-index after upgrading.
|
||||
@@ -40,6 +44,14 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
### Fixes
|
||||
|
||||
- **Where the app goes after login is a fork, not two always-es.** A navigation whose destination comes back from a helper — `router.replace(await resolvePostLoginRoute())` over `return (await hasSeenWelcome(…)) ? '/home/' : '/welcome/'` — drew both screens with no condition, reading as if the welcome screen always shows. The two arms share a line, and only a column can tell them apart; each synthesized edge now carries its literal's own position, so the guard reader says which arm it is: `WHEN await hasSeenWelcome(…)` → home, and its negation → welcome. And the scan starts at the helper's body, so a literal-union return type — `Promise<'/welcome/' | '/home/'>`, whose routes are string literals too, written first — no longer stands in for the navigation itself. Re-index after upgrading to pick the positions up.
|
||||
|
||||
- **A handler called from under a binding says what it passes.** A press that runs `tryCatchSync(onClosePress)` drew a box for the wrapper and stopped — leaving the one thing a reader asks ("what is being wrapped?") unsaid, even though every other call-shaped site already prints its arguments. The panel and tooltip now say `tryCatchSync(onClosePress)` — the argument is the answer.
|
||||
|
||||
- **A step the walk stopped at keeps its whole name.** A boundary — another screen, or a cap the walk hit — ends its name with an ellipsis by design, but the box was not sized for it, so a longer name lost its last letters instead (`/scan-to-verif…` for `/scan-to-verify …`). The anchor's start mark clipped a long path the same way (`/sheets/forgot-passw…`). The box now makes room for both.
|
||||
|
||||
- **A screen that talks to native code keeps its own navigations.** In a React Native or Expo app, a `router.push` written inside a listener for a native event was credited to whichever screen had *started* that round trip, not to the screen the push is written on. In one app that moved seven transitions off the capture screen and onto the review screen it opens — leaving the review screen looking as though nothing in the app could reach it, stranded in the "no transition reaches this" band at the bottom of the Screens tab, and printing Swift conditions like `Thread.isMainThread` on a JavaScript navigation. A navigation now belongs to the screen whose file it is written in; an event arriving from native code, from an HTTP call or off a queue is no longer read backwards as if it were a caller.
|
||||
|
||||
- **A link written under a condition says so on the Screens tab.** A checkout stepper whose tabs are each enabled by their own prop, and a navbar whose admin links only render for an admin, both read as **always** — every transition written in markup was drawn with no condition at all, while the ones written as calls carried theirs. They are read the same way now: a store's checkout tabs say `step1` … `step4`, its navbar says `userInfo && userInfo.isAdmin` for the admin links and `!userInfo` for sign-in, and 59 of that store's 74 transitions carry the condition they actually run under, up from 20. A template language with no condition rules of its own still says nothing rather than guessing.
|
||||
|
||||
- **A link in markup no longer reads as a helper's return value.** `<Link to='/shipping'>` was labelled `return /shipping`, which in this picture means the destination came back from somewhere else and was inferred. It is written right there, so it now reads `link /shipping` — and an internal `<a href>` reads `a`. Only a destination that genuinely arrives from elsewhere still says `return`.
|
||||
|
||||
Reference in New Issue
Block a user