Skip to content

Dwelling in startup opens the novice menu - #201

Merged
QuentinRoy merged 5 commits into
mainfrom
claude/implement-179-9rinsj
Jul 31, 2026
Merged

Dwelling in startup opens the novice menu#201
QuentinRoy merged 5 commits into
mainfrom
claude/implement-179-9rinsj

Conversation

@QuentinRoy

Copy link
Copy Markdown
Owner

Closes #179

Summary

Adds the novice phase to the navigation state machine: dwelling without significant movement during startup now opens novice mode at the root menu, dispatching open. This is the other branch of the decision the tracer bullet (#177) already made — movement past movementsThreshold goes to expert, dwelling goes to novice, and the two stay mutually exclusive, with the first qualifying input winning.

This also introduces the runtime's first real timer: a named registry with monotonically increasing tokens, armed and cancelled strictly during commit (never during dispatch), so a superseded timer firing later is a no-op at the machine level.

Key changes

  • machine.ts: new novice phase, timer.elapsed input, and timer.schedule/timer.cancel commands. transitionIdle arms a mode-dwell timer on pointer.down; transitionStartup cancels it on significant movement (→ expert) or opens novice mode when it elapses. The shared finish() helper now also cancels a phase's owned timer and carries the currently-open menu, so novice's pointer.up/pointer.cancel (always cancel for now — hit-testing isn't implemented yet) reuse the same terminal-transition logic as startup/expert.
  • runtime.ts: a named timer registry (Map<TimerKind, handle>) interprets timer.schedule/timer.cancel during commit, before layout is projected or anything is dispatched.
  • layout-view.ts: LayoutView gains the menu region and a cursor: 'none' state, and the stroke splits into upperStroke (fresh, starting at the menu center) and lowerStroke (the accumulated startup stroke).
  • renderer.ts: renders the menu DOM (recreated only when the menu's model reference changes) and a second stroke canvas for lowerStroke. The renderer performs the one DOM-dependent client-to-parent coordinate conversion the projector deliberately leaves to it.
  • controller.ts: new noviceDwellingTime config option (default 1000 / 3, matching the legacy engine).

Scope

Novice hit-testing, submenu descent, and the submenu-dwell timer are out of scope — no ticket drives them into existence yet. Every release in novice mode cancels for now, since nothing can be active without hit-testing.

Test plan

  • yarn test — 279 tests passing
  • yarn typecheck
  • yarn lint
  • yarn format:check
  • yarn test:coverage — all files touched by this change are at 100%; overall coverage is above the repo's thresholds (95%/90%/95%/95%)
  • No changeset (per the ticket's acceptance criteria — this doesn't touch the public API yet)

https://claude.ai/code/session_014FTh6HiCg6Sr9pa9eV5UAe


Generated by Claude Code

claude added 2 commits July 29, 2026 23:16
Add the novice phase to the navigation state machine: dwelling without
significant movement during startup arms and fires a mode-dwell timer,
opening novice mode at the root menu and dispatching `open`. Movement
past `movementsThreshold` still wins the race first, cancelling the
timer; the two outcomes stay mutually exclusive.

Introduces the runtime's named timer registry (monotonically increasing
tokens, armed/cancelled during commit) and generalizes the shared
`finish` helper to carry a phase's owned timer and the currently open
menu. `LayoutView` and the renderer gain the menu region and the
lower-stroke canvas, splitting the accumulated stroke from the current
one; the renderer performs the client-to-parent coordinate conversion
so the projector stays DOM-free.

Novice hit-testing and submenu descent are out of scope: every release
in novice mode cancels for now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FTh6HiCg6Sr9pa9eV5UAe
- Collapse finish()'s select/cancel branches into a single return path,
  and spread TimerRef into timer.cancel commands instead of restating
  its fields.
- Extract a shared stroke-layer helper in the renderer instead of
  duplicating the upper/lower stroke canvas logic.
- Cache the menu's active key so the renderer only re-scans the menu
  DOM when it actually changes, matching the existing stroke
  reference-equality guards.
- Merge the runtime's per-command passes that both run during commit
  (timer scheduling/cancellation and feedback) into one.
- Add a small toLocalPoint utility for the client-to-parent coordinate
  conversion the renderer already needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FTh6HiCg6Sr9pa9eV5UAe
@changeset-bot

changeset-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 761ea07

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@QuentinRoy
QuentinRoy enabled auto-merge (squash) July 31, 2026 17:11
@QuentinRoy
QuentinRoy merged commit 7fb63a5 into main Jul 31, 2026
5 checks passed
@QuentinRoy
QuentinRoy deleted the claude/implement-179-9rinsj branch July 31, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dwelling in startup opens the novice menu

2 participants