Transcript entrance animation — the terminal counterpart of the companion's
@starting-style fade-in (transcriptEnterClass, ChatContent.tsx).
Opening or focusing a session TELEPORTS its transcript onto the screen: one frame is the loading spinner (or the previous tab), the next is a full, bottom-anchored wall of text. Nothing moves, so the eye gets no cue about what changed or where to look — the app called it "jumping".
The web app solves this with opacity+translate on mount. A terminal cannot translate sub-cell, and shifting whole rows for 3 frames reads as a lurch, not a rise. What it CAN do is dissolve: every cell of the messages band is painted normally and then blended back toward the terminal background, with the blend weight ramping to 1.0 over a few hundred ms. Rows nearest the input resolve FIRST and the wave runs upward into history, so the newest message — the reason you reopened the session — is what materialises under the cursor while older turns fade in behind it.
Everything here is pure: active?/row-alpha are wall-clock math and
blend is a color mix. screen.clj owns the one back-buffer pass that
applies them (paint-transcript-enter!), and state.clj owns the single
:transcript-enter-at timestamp that arms it.
Transcript entrance animation — the terminal counterpart of the companion's `@starting-style` fade-in (`transcriptEnterClass`, ChatContent.tsx). Opening or focusing a session TELEPORTS its transcript onto the screen: one frame is the loading spinner (or the previous tab), the next is a full, bottom-anchored wall of text. Nothing moves, so the eye gets no cue about what changed or where to look — the app called it "jumping". The web app solves this with opacity+translate on mount. A terminal cannot translate sub-cell, and shifting whole rows for 3 frames reads as a lurch, not a rise. What it CAN do is dissolve: every cell of the messages band is painted normally and then blended back toward the terminal background, with the blend weight ramping to 1.0 over a few hundred ms. Rows nearest the input resolve FIRST and the wave runs upward into history, so the newest message — the reason you reopened the session — is what materialises under the cursor while older turns fade in behind it. Everything here is pure: `active?`/`row-alpha` are wall-clock math and `blend` is a color mix. `screen.clj` owns the one back-buffer pass that applies them (`paint-transcript-enter!`), and `state.clj` owns the single `:transcript-enter-at` timestamp that arms it.
(active? start-ms now-ms)True while the entrance armed at start-ms is still mid-flight. nil
start (nothing armed) and any elapsed past duration-ms are false, so a
settled view stops repainting.
True while the entrance armed at `start-ms` is still mid-flight. `nil` start (nothing armed) and any elapsed past `duration-ms` are false, so a settled view stops repainting.
(blend color bg alpha)Mix color toward bg by alpha (0.0 ⇒ pure bg, 1.0 ⇒ pure color).
Works for any TextColor — ANSI and indexed palettes answer getRed/
getGreen/getBlue too — and always returns a concrete RGB so the fork's
painter emits a truecolor escape per cell instead of snapping to the
nearest palette entry mid-fade.
Mix `color` toward `bg` by `alpha` (0.0 ⇒ pure `bg`, 1.0 ⇒ pure `color`). Works for any `TextColor` — ANSI and indexed palettes answer `getRed`/ `getGreen`/`getBlue` too — and always returns a concrete RGB so the fork's painter emits a truecolor escape per cell instead of snapping to the nearest palette entry mid-fade.
Kill switch (env VIS_NO_TRANSCRIPT_ANIM), mirroring VIS_FORCE_FULL_FRAME:
a production escape hatch for terminals where per-cell recoloring is too
expensive or simply unwanted. Also the reduced-motion answer — set it and
transcripts appear instantly, exactly as before.
Kill switch (env `VIS_NO_TRANSCRIPT_ANIM`), mirroring `VIS_FORCE_FULL_FRAME`: a production escape hatch for terminals where per-cell recoloring is too expensive or simply unwanted. Also the reduced-motion answer — set it and transcripts appear instantly, exactly as before.
Total wall time of the entrance: the last (topmost) row starts at
stagger-ms and needs fade-ms to finish. The render loop uses this to
decide when to stop forcing frames.
Total wall time of the entrance: the last (topmost) row starts at `stagger-ms` and needs `fade-ms` to finish. The render loop uses this to decide when to stop forcing frames.
How long ONE row takes to go from invisible to fully painted. Matches the
companion's duration-200 beat; long enough to read as a dissolve on a
60fps terminal tick, short enough that it never delays reading.
How long ONE row takes to go from invisible to fully painted. Matches the companion's `duration-200` beat; long enough to read as a dissolve on a 60fps terminal tick, short enough that it never delays reading.
(row-alpha start-ms now-ms row top bottom)Paint weight (0.0 invisible … 1.0 final colors) for screen row of the
messages band [top, bottom) at now-ms.
The row's own fade STARTS at a delay proportional to its distance from the bottom of the band, so the wave travels bottom → top. Rows outside the band and a settled/never-armed entrance answer 1.0, which callers treat as "leave this cell alone".
Paint weight (0.0 invisible … 1.0 final colors) for screen `row` of the messages band `[top, bottom)` at `now-ms`. The row's own fade STARTS at a delay proportional to its distance from the bottom of the band, so the wave travels bottom → top. Rows outside the band and a settled/never-armed entrance answer 1.0, which callers treat as "leave this cell alone".
Extra delay budget spread across the band, bottom row (0ms) to top row
(this much). The wave — not the fade — is what encodes direction, so it has
to be comparable to fade-ms; much smaller and the whole band just blinks
on together.
Extra delay budget spread across the band, bottom row (0ms) to top row (this much). The wave — not the fade — is what encodes direction, so it has to be comparable to `fade-ms`; much smaller and the whole band just blinks on together.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |