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.
This commit is contained in:
Colby McHenry
2026-08-31 17:10:13 -05:00
parent 882ea143e8
commit 3298db1292
14 changed files with 1006 additions and 104 deletions
+15
View File
@@ -580,6 +580,21 @@ describe('expo-router: end-to-end', () => {
expect(toRoot.when).not.toMatch(/!/);
expect(toWelcome.when).toMatch(/!\s*\(?\s*await seen\(\)/);
// …and each site names the DECISION its condition belongs to, so the two
// arms can be drawn as one choice rather than as two lines that happen to
// read as each other's negation. Same branch, opposite arms, one `on`.
const rootArm = toRoot.sites[0]!.decision!;
const welcomeArm = toWelcome.sites[0]!.decision!;
expect(rootArm.branch).toBe(welcomeArm.branch);
expect(rootArm.branch).not.toBe('');
expect(rootArm.form).toBe('ternary');
expect(rootArm.on).toBe(welcomeArm.on);
expect(rootArm.on).toMatch(/await seen\(\)/);
expect(rootArm.on).not.toMatch(/^!/);
expect(rootArm.not).toBeUndefined();
expect(welcomeArm.not).toBe(true);
expect(rootArm.arm).not.toBe(welcomeArm.arm);
cg.close();
});
});