8 Commits
Author SHA1 Message Date
3ed73bc127 feat(ui): the Steps tab lays a screen out in clusters and says a far link in words; a <Card/> is the Card its file imports (#1817)
* fix(ui): a screen's steps read as clusters, and a link too far to follow is said in words

The mobile app's /capture came back as a web: 100 boxes in a 1,227x5,588
ribbon, 113 lines drawn at rest crossing each other 652 times, each one
running over about five other boxes' names. Measured, not guessed — three
separate causes, very unequal.

The region grouping had nothing to divide there (98 of 100 boxes take their
region from one memoized component), so the picture fell back to a single
719px column. But the region dimension was not the lever. The lever was that
`packRegions` packed every step of one distance onto shared rows and wrapped
those rows at a fixed 720px, so a box and the thing it fires landed seven
lines apart: 70 of the 113 lines joined boxes ONE step apart. That is what
the crossings were made of.

So a region is now packed as CLUSTERS — a step, then the steps it sets in
motion on the line under it, stepped in — while the starting points that fire
nothing still share a line, because a screen's handlers are siblings and
giving each its own line turned a flat region into a column. A region's line
width is earned rather than fixed (sqrt(total * pitch), clamped 720..2600),
so a big screen comes out about as wide as it is tall.

Clustering makes most links local but not all: a step reached from two places
is drawn under whichever reached it first, so the other way in still crosses
the picture. Those are now said in WORDS at both ends — `-> resumeInference`
under the box that leads there, `<- CaptureView` under the box it arrives at,
capped at three with `+N more` — rather than drawn. This is not a hiding: the
link is stated, which says more than a line vanishing off the edge of the
screen does, and selecting the box draws every one of its real lines exactly
as before. It is the one at-rest cut that does not produce the "box that leads
somewhere and draws nothing" every earlier cut produced.

Also fixed while here, and predicted by the earlier region work: the in-region
row relaxation had no cycle guard, so a region holding one loop pushed 65 of
its boxes to rows 294-301 while the rest sat at 0-2. `forwardLinks` sets
cycle-closing links aside first, as the order reading's `withoutBackEdges`
already did.

/capture: 1,227x5,588 -> 2,279x4,356, at-rest crossings 652 -> 1,
lines-over-boxes 553 -> 26, with half the links still drawn as real lines and
every quiet box still one the screen itself fires directly. The order reading
is untouched (it keeps every line).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTUFNN5bH3aPw2gi2LqbYD

* fix(ui): a stub names its box without the mark the box wears for its kind

`← ⇠ onCaptureProgress +2` reads as two arrows arguing: the stub already
leads with a direction, and the box's own kind mark was competing with it.
Verified in the live canvas. The box keeps its mark, where nothing competes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTUFNN5bH3aPw2gi2LqbYD

* fix(ui): the screen's line into each of its parts stops sweeping the picture

Audited all 51 screens of the mobile app on this branch. 96 lines — the
screen's own stand-in line into each region — were 17% of everything drawn
and caused 79% of every crossing left. A screen with ten regions tiles them
into bands, so the line into a region two bands down travelled the height of
the whole picture.

Two causes, both fixed. The entry the line lands on was the walk's first
member of the region; clustering moves a step that fires something BELOW the
ones that fire nothing, so that box could sit lines down inside the region
and the line had to reach past everything above it. It now lands on the box
nearest the region's top-left that the screen actually leads to. And the
stand-in line is no longer exempt from the stub rule — when the region is
still too far to follow, the link is said in words like any other. The rest
of the anchor's fan stays quiet as before: it is already stood in for.

Across the 51 screens: crossings 47 -> 10, no screen above 10 (worst was 19,
now 2); lines-over-boxes 236 -> 182; boxes with neither a line nor a word
150 -> 135.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTUFNN5bH3aPw2gi2LqbYD

* fix(ui): a screen's parts fill the canvas instead of squaring off into rows

Audited the app's 28 regioned screens: the median canvas was 55% region and
45% nothing, and /home was 44% — 4,860px tall to hold about 2,160px of
picture. The cause is that regions were tiled a row at a time with each row
as tall as its tallest member, so one short region beside a tall one left the
rest of that row blank, and a reader scrolls through the blank.

Each region now goes as high as it can and then as far left as it can, over a
skyline of what is already placed. Reading order is untouched: regions are
still walked in the screen's own source order, so an earlier one is never
pushed below a later one — a short one just tucks under another short one
rather than waiting for the tall one beside it. Layering now comes from the
finished geometry rather than a band counter, since once regions drop
independently what a reader sees as one row IS one row.

/home 4,860px -> 3,584px, aspect 0.59 -> 0.94. Tallest screen in the app
4,860 -> 4,356. Crossings 10 -> 13 across all 51 screens, still none above 10.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTUFNN5bH3aPw2gi2LqbYD

* fix(ui): the width a picture wraps at is tried, not estimated

A region's line width came from sqrt(total * pitch) — the width at which
total/width lines come out square. That estimate is wrong for how these
pictures are drawn: a cluster spends lines on its own structure (a hub gets a
line to itself, and what it fires starts another), so it undercounts a
region's lines badly and wrapped /capture's 98 boxes into a 4,356px column.

Laying a picture out is cheap and exact, so the widths are tried instead:
layoutAt runs the whole pack at each of eight widths and the best finished
canvas wins (~2ms for the model, all eight included). It has to be scored on
the CANVAS, not per region — squaring each region off individually leaves
fewer of them side by side, which took /home from 3,584px to 5,624px while
every region looked better on its own.

Also measured and rejected while here: dropping a region's CLUSTERS side by
side the way the regions drop onto the canvas. Total height 42,084 -> 39,756px
(-6%), but lines-over-boxes 120 -> 134 and crossings 5 -> 8, because two
clusters side by side put each one's lines through the other. Height is cheap
to scroll; a crossed line is what made this picture unreadable. The reasoning
is recorded in the code so it is not re-tried blindly. Regions differ — they
sit far enough apart that few lines run between them.

Across the app's 51 screens: tallest picture 4,356 -> 3,796px, total height
45,744 -> 42,084px, lines-over-boxes 184 -> 120, crossings 13 -> 5, and no
screen is a tall ribbon any more.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTUFNN5bH3aPw2gi2LqbYD

* stuff

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 00:54:15 -05:00
Colby McHenry 3298db1292 feat(steps): render fork decisions as points with per-arm edges and captions
Adds full support for decisions at forks in both the code graph and the UI. Key changes introduce a decision model for forks (innermost guard decisions), propagate decision data through the server and wire layer, and render decisions in the UI as distinct points with labeled arms. New components (ForkPoint and DecisionCaption) visualize the decision and its arms, while utilities (armWords, forkLabel) generate arm captions. The order reading (canvas) now shows decisions as points, and arms are drawn as separate edges (yes/no/case), with labels and captions displayed under the deciding box. Tests, typings, and docs updated to reflect the new decision visualization and behavior, including selection reach and resting-label semantics. This lays the groundwork for clearer visualization of conditional navigation and guarded branches on the order canvas.
2026-08-31 17:10:13 -05:00
Colby McHenry 882ea143e8 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.
2026-08-31 15:38:13 -05:00
Colby McHenryandClaude Fable 5 783f3954ec feat(steps): rows read in the code's order; a hop written inside another call says so
- branch-guards callSiteInTree: a call's span and the call it is written inside the arguments of (`within`), stopping at a function or block boundary
- steps.ts: each step records the hop that first reached it (position, span, enclosing call — the fold's first hop out of the root, inherited down the fold); a row is ordered by that position, a hop inside another site's arguments before that site, and `WireStep.order` carries it; links carry `within`
- map-model: an `order` option — the row's initial order, sweeps over parents only, tie-broken by it; the Map and Screens tabs pass none and are unchanged
- viewer: rows laid out by `order`; `inside res.json(…)` in the panel rows and the tooltip
- tests: servers fixture (a token signed inside the reply's arguments: `within`, and the row `create · queue · mail · jwt.sign · 201`), model row order; spec §3.13, CHANGELOG

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REFyW9hmNrxhwN5wxRoAkC
2026-08-29 12:25:27 -05:00
Colby McHenryandClaude Fable 5 950686def4 feat(ui): Steps for servers — route roots, server effects, request/decorator triggers, guards for Python/Java/Kotlin/C#/Go/C
- api/route-roots.ts: the symbol a route runs (references-edge handler, exported page component, or the route itself for an inline handler), shared by steps and screens; the bare Steps tab lists an API's endpoints by router file
- api/effects.ts: database / response / queue / email / payments / cache / auth / process / network / storage / device / telemetry, matched on the call as written per language family, with model + read/write and the literal status on a response site
- graph/branch-guards.ts: callSitesForFile (the whole member chain), memberTypesInTree, decoratorsForFile, request/decorator triggers with the middleware/guard chain; guard + argument rules for Python, Java, Kotlin, C#, Go and C
- steps.ts: classify on the chain before trusting a name match, retarget this.x.y() by declared type, skip test doubles after the effect pre-check, project kind on the wire
- viewer: kindWord/kindWords per project kind, endpoint chooser, response boxes labelled by status codes
- python.ts: FastAPI detected from a monorepo sub-directory; is-test-file: samples/examples package paths are not tests
- tests: ui-steps-api-servers, ui-effects, branch-guards-languages; spec §3.13 Servers paragraph, CHANGELOG, plan doc

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01REFyW9hmNrxhwN5wxRoAkC
2026-08-28 13:45:38 -05:00
Colby McHenry 5e06204deb feat(expo-router): add Expo Router support for Screens and navigations and introduce Steps API
- Introduces trigger metadata for steps and edges to capture what fires a site (JS prop, on* option, or callback) to improve cross-boundary flow analysis.
- Extends parsing/analysis to detect triggers in JSX attributes, on* bindings, and late-bound callbacks; adds utilities (calleeText, lastSegment) to extract trigger sources.
- Ships new trigger structures (WireStepTrigger, trigger on WireStepSite/WireStep) and propagates trigger through built steps; updates step labeling to reflect trigger information.
- Adds triggerWords helper and uses it to render human-readable trigger descriptions in Steps UI, including edge labels and per-site visuals.
- Updates UI (ScreensView, StepsView) to display FIRES FROM information, with styling tweaks to highlight triggers and related elements; enhances tooltips and inline text wrapping for readability.
- Extends tests to cover trigger detection and rendering across various binding patterns (prop, option, callback) and inline RN listeners.
- Updates design/docs and changelog to reflect Expo Router integration, per-site trigger metadata, and the new Steps surface.
2026-08-28 10:40:38 -05:00
Colby McHenry e288d7645b feat(expo-router): add Expo Router support for Screens and navigations and introduce Steps API
- Adds Expo Router integration with a new Screens view and a Steps API to surface screens and their transitions.
- Extends codegraph extraction/resolution to handle namespace objects, React hook bindings for handlers, and Swift RN bridge evidence; introduces per-site guard arguments and trigger metadata, enabling richer flow analysis across JS ↔ native boundaries.
- Introduces UI and data-model changes to represent conditions as words (WHEN/AND/OR/NOT), display per-site call arguments, and show what fires a site (triggers). Adds new utilities (ui/conditions.ts) and updates ScreensView and StepsView to render scenarios with multiple sites and “ways” counts.
- Implements site readers for WHEN/ARGS/TRIGGER, and wiring to expose steps via API endpoints (including /api/steps); enhances tests to cover namespace resolution, useCallback-driven handlers, and inline RN event listeners.
- Updates styling and templates to reflect the new wording, scenario rows, and per-site details, including NOT instead of leading negation strings and multi-way links.
- Documents and reflects changes in changelog and design docs to describe Expo Router integration and the Steps surface.
2026-08-28 10:21:46 -05:00
Colby McHenry 873f133c96 feat(expo-router): add Expo Router support for Screens and navigations and introduce Steps API
Introduce Expo Router integration with a new Screens view and API to surface screens and transitions, plus a new Steps API and UI to depict typed steps from anchors or symbols. Extend codegraph’s extraction and resolution to handle namespace objects (export default NAME, two-statement forms, and default bindings) and React hook bindings for handlers, improving accuracy of flows across JS ↔ native boundaries. Add Swift/React Native bridge receiver evidence (RCT_EXTERN_MODULE, RCT_EXTERN_METHOD) and related resolution logic, with tests covering namespace-object resolution, useCallback-driven handlers, and inline RN event listeners. Update UI to include a Steps tab and associated components (StepsView, StepNode, ScreenEdge) and wire navigation to expose steps-based exploration via /api/steps and UI routes. Documentation and changelog reflect the new Expo Router integration and steps surface capabilities.
2026-08-28 09:46:50 -05:00