Skip to content

feat(tui): the intro splash — mark, ring, and a tagline that types itself - #223

Open
plombeer31 wants to merge 2 commits into
valeryb/onboarding-shellfrom
valeryb/onboarding-intro
Open

feat(tui): the intro splash — mark, ring, and a tagline that types itself#223
plombeer31 wants to merge 2 commits into
valeryb/onboarding-shellfrom
valeryb/onboarding-intro

Conversation

@plombeer31

Copy link
Copy Markdown
Collaborator

Stacked on #222 (which is stacked on #220). Retarget as each merges.

Why

A first run opened on three unstyled option rows. The product's name appeared nowhere on the screen a new user meets first — the mark, the wordmark and the tagline all shipped in logo.tsx and none of them were used until the operator was already inside the app.

The screen

The mark inside a ring of smaller crosses, ATOMIC (the first 23 columns of the shipped ATOMIC AGENT wordmark), and TAGLINE — the string the codebase already carries — typing itself in at ~45 ms/char, then [ press any key to continue ].

                                                  ✛
                                         ✛                ✛
                                             ███████░
                                  ✛         ████████░░
                                           █████████░░           ✛
                              ✛   █████████████████████████████░    ✛
                                  █████████████████████████████░░
                              ✛              ██████████░░░░░        ✛
                                 ✛           ███████░░░
                                        ✛                ✛
                                      ▄▀█ ▀█▀ █▀█ █▀▄▀█ █ █▀▀
                                      █▀█  █  █▄█ █ ▀ █ █ █▄▄
                                        Local AI-First Agent
                                   [ press any key to continue ]
  ctrl+c quit

Three things worth reviewing

use-typewriter.ts runs one interval for the whole reveal. My first draft listed the revealed count in the effect's dependency array, so every character tore the timer down and armed a fresh one — with Ink's frame commit in between, a 45 ms/char reveal actually crawled at ~230 ms/char. The count advances inside the tick now and the timer stops itself on the last character. (Under ink-testing-library the rate is bounded by its ~4 fps commits regardless, which is why the test asserts progress-then-settle on a short string rather than a duration.)

The ring is an ellipse. A cell is ~2.2× taller than wide, so a circle drawn in cells reads as a wide oval. orbit-field.ts derives the vertical radius from CELL_ASPECT, clips to the viewport and to reserved bands, and dedupes cells that round onto each other.

Nothing is allowed to outgrow its rows. Ink 7 overlaps rather than clips, so an art block one row too tall does not get cropped — it paints over the lines above. intro-art.ts takes a row budget and sizes the ring by whichever runs out first, rows or columns; the mark steps md → sm → none by the rows actually left over rather than by the size tier; and the ring is dropped entirely if it cannot clear the mark's clear space (one arm width) instead of resting a cross against an arm.

Interaction

Any key finishes the reveal, a second key moves on — an animation nobody can interrupt is a wait, and one that vanishes on the key meant to hurry it is a screen nobody reads. The splash claims Esc too: it must not skip setup from a screen that has not yet said setup is what comes next. introSeenAt is stamped as the splash is dismissed, not at the end of the flow.

Verified in a PTY

  • 100×30 — full ring, everything on screen, hints on the last row.
  • 86×26 — ring dropped for clear space, mark + wordmark + tagline + advisory footer.
  • 72×20 — sm mark with a small ring; the whole screen still fits.

Tests

orbit-field.test.ts (ellipse ratio, viewport clipping, dedupe, reserved bands), intro-art.test.ts (mark centred, height never exceeds budget, ring present / dropped by clearance, crosses never overwrite the mark), use-typewriter.test.tsx (progresses, settles, skip lands on the full string, empty string is done), and four new onboarding-screen.test.tsx cases including "two keys off the splash" and "Esc does not skip from the splash".

Full suite: 5169 passed; the two failures on this machine (fs-glob-real, send-message-concurrency) fail identically on main.

…self

A first run opened on a list of three options with no product name on it
anywhere. This is the screen before that: the mark inside a ring of
smaller crosses, the `ATOMIC` wordmark, and the shipped tagline typing
itself in at ~45ms/char.

- `use-typewriter.ts` — one interval for the whole reveal. The first
  draft listed the revealed count in the effect deps, so every character
  tore the timer down and armed a new one; with Ink's frame commit in
  between, a 45ms/char reveal actually crawled at ~230ms/char.
- `orbit-field.ts` — ring placement, pure. An ellipse, not a circle: a
  cell is ~2.2× taller than wide, so a circle drawn in cells reads as a
  wide oval.
- `intro-art.ts` — composes mark + ring into one grid. The ring is sized
  by whichever runs out first, rows or columns, and is dropped entirely
  rather than resting a cross inside the mark's clear space. Ink 7
  overlaps instead of clipping, so the grid never exceeds its row budget.
- The mark steps down md → sm → none by the rows actually left over, not
  by the size tier alone, for the same reason.

Any key finishes the reveal; a second key moves on. The splash claims Esc
too — it must not skip setup from a screen that has not yet said setup is
what comes next. `introSeenAt` is stamped as it is dismissed.

Verified in a PTY at 100×30 (full ring), 86×26 (ring dropped for clear
space, advisory in the footer) and 72×20 (sm mark, small ring, everything
still on screen).
A single radius threshold cannot express clearance around this mark. The
ring is an ellipse and the mark is wider than it is tall, so one number
is either too strict vertically or too loose horizontally — mine was the
former, and silently dropped the ring at 100×30, the size the flow
actually opens at, where it fits with four columns and two rows to
spare.

Checked per axis now, against the mark's own box. Pinned at 96×21 (the
intro's usable area inside a 100×30 terminal) and at a wide-but-short
window, where the rows are what runs out.
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.

1 participant