Skip to content

fix(web): match upstream's live activity motion and restore the spin utility - #452

Merged
rynfar merged 1 commit into
pylonfrom
fix/live-activity-upstream-parity
Sep 10, 2026
Merged

fix(web): match upstream's live activity motion and restore the spin utility#452
rynfar merged 1 commit into
pylonfrom
fix/live-activity-upstream-parity

Conversation

@rynfar

@rynfar rynfar commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Pylon had drifted from upstream in the chat timeline, and the drift took a working animation with it.

cdde7f3150 (in #444) gave every active row the sweeping live-activity-focus overlay and deleted live-tool-shine as dead. Upstream splits the two treatments by row type: the overlay is for rows with no tool — Thinking, worktree setup, context compaction — selected by active && shimmer, which only ThinkingTimelineRow passes; tool labels keep the gradient shine via active={animated && !shimmer}.

The spinner regression

That same commit also deleted @utility visible-animate-spin, while spinner.tsx and refresh-icon.tsx still apply motion-safe:visible-animate-spin. index.css is the only stylesheet in the web app and nothing else defines it, so Tailwind emitted no rule: every <Spinner> and running <RefreshIcon> in Pylon web has been static since that commit landed. Restored from upstream verbatim.

What changed

  • Upstream's shimmer prop, animated = active && !failed guard, and showShimmer selection.
  • The observer ref moves from the overlay back onto the animated row, as upstream has it, so the shine's ancestor sets --visible-animation-state too.
  • Overlays restored on the worktree-setup and compaction labels.
  • One LiveActivityRow serves both active and settled work rows, replacing the branch that rendered a bare LiveActivityContent.
  • @utility live-tool-shine, its keyframes, and @utility visible-animate-spin restored byte-identical to upstream, in upstream's order.
  • Consequences of the old arrangement dropped: the overlay copy no longer receives failed, so the icon tint precedence and the trailing failure mark match upstream again.

The live activity components now differ from t3code-upstream/main by exactly one line — a retained comment on the highlighted prop.

Preserved deliberately

  • workingStepLabel beside the working timer. Pylon feature, absent upstream.
  • document.hasFocus() in visibleAnimation.ts. AGENTS.md requires animations to stop for "reduce-motion, backgrounding, and unfocused screens"; upstream gates only the first two. This is a behavior gate, not theming, so it stays under the Pylon-first default for behavior.

Guard tests

Two invariants over index.css, both verified against real mutations:

Guard Negative control Result
Every animation utility a component names must be defined delete @utility visible-animate-spin fails, naming both spinner.tsx and refresh-icon.tsx
Gated utilities are applied only by files that register the observer remove the row's observeVisibleAnimation ref does not fail — see below

Being precise about the second: it is file-level. It catches #426's shape — a file applying a gated utility while registering nothing, and a gated utility with no consumer at all — but it cannot see one ref go missing in a file that still registers the observer elsewhere, which MessagesTimeline.tsx now does. The test comment says so rather than overselling it.

Surfaces

Web and desktop share the bundle. Mobile's shimmer is Reanimated in thread-work-log.tsx and untouched. docs/user/tool-activity.md now describes both treatments and the spinner gate; it had said "only the active tool label shines", which stopped being true in either direction.

Verification

  • vp test run on visibleAnimation.test.ts, MessagesTimeline.test.tsx, MessagesTimeline.logic.test.ts — 161 passed, with updated assertions for the shine/overlay split and new coverage for the Thinking and worktree-setup overlays
  • tsgo --noEmit in apps/web — clean
  • vp lint --report-unused-disable-directives and vp fmt --check on the six changed files — clean
  • Both CSS utilities and the three timeline regions diffed against t3code-upstream/main

Supersedes #445, which took a different route to the same area before #444 landed and is now obsolete.

Motion change, so it wants a short video; that needs your go-ahead to drive a browser.

Model: Opus 5 (1M context), harness: Claude Code.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…utility

Pylon had drifted from upstream in the chat timeline. `cdde7f3150` gave every
active row the sweeping `live-activity-focus` overlay and deleted
`live-tool-shine` as dead, but upstream reserves the overlay for rows with no
tool — Thinking, worktree setup, context compaction — and keeps the gradient
shine for tool labels, selected by `active && !shimmer`.

Restores upstream's arrangement exactly: the `shimmer` prop, the
`animated = active && !failed` guard, the observer ref on the animated row
rather than on the overlay, overlays on the worktree-setup and compaction
labels, one `LiveActivityRow` for both active and settled work rows, and both
CSS utilities byte-identical to upstream's. The live activity components now
differ from upstream only by one retained prop comment.

That same commit also deleted `@utility visible-animate-spin` while
`spinner.tsx` and `refresh-icon.tsx` still applied it. Tailwind emits nothing
for an undefined utility, so every spinner and refresh glyph in the web app has
been static since. Restored from upstream verbatim.

Preserved deliberately: Pylon's `workingStepLabel` beside the working timer, and
the `document.hasFocus()` gate in visibleAnimation, which AGENTS.md requires
alongside reduce-motion and backgrounding and upstream does not have.

Adds two guard tests over `index.css`: gated utilities must be applied only by
files that register the observer, and every animation utility a component names
must actually be defined. The second fails on the `visible-animate-spin`
deletion; the first is file-level and catches #426's shape, not a single missing
ref.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 14.0 KiB 13.9 KiB −157 B (−1.1%) 15.1 KiB
Codex Thread snapshot wire 7.2 KiB 7.2 KiB −3 B (−0.0%) 7.3 KiB
Codex Live turn WebSocket wire 6.8 KiB 6.7 KiB −154 B (−2.2%) 7.8 KiB
Codex Live turn WebSocket decoded 58.8 KiB 58.0 KiB −866 B (−1.4%) 66.4 KiB
Codex Live turn messages 9 8 −1 (−11.1%) 21
Claude Total thread wire 14.1 KiB 13.9 KiB −150 B (−1.0%) 15.1 KiB
Claude Thread snapshot wire 7.2 KiB 7.2 KiB −6 B (−0.1%) 7.3 KiB
Claude Live turn WebSocket wire 6.9 KiB 6.7 KiB −144 B (−2.1%) 7.8 KiB
Claude Live turn WebSocket decoded 59.7 KiB 58.8 KiB −884 B (−1.4%) 66.4 KiB
Claude Live turn messages 9 8 −1 (−11.1%) 21

Baseline: 390ba78 · PR result: aad0257 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 115.6 KiB
  • Claude decoded thread snapshot: 116.3 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@rynfar
rynfar merged commit 6c1632c into pylon Sep 10, 2026
17 checks passed
@rynfar
rynfar deleted the fix/live-activity-upstream-parity branch September 10, 2026 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant