Skip to content

perf(native): keep hover state local and drop offscreen passes - #450

Merged
EraPartner merged 1 commit into
mainfrom
claude/project-thread-1p1whz
Nov 14, 2026
Merged

EraPartner merged 1 commit into
mainfrom
claude/project-thread-1p1whz

Conversation

@EraPartner

Copy link
Copy Markdown
Owner

Requested by EraPartner · project thread

Summary

First round of a static performance audit of the Swift frontend (apps/native/Sources/VisionNativeUI). Each change removes work SwiftUI or Core Animation did per pointer move or per composited frame. Nothing was measured: there is no Swift/SwiftUI toolchain for macOS in the cloud container, so every benefit below is reasoning from the code (inference), not a number.

Change File What it removes
Table hover band observes its own state (VisionTableHover, VisionTableHoverBand) Components/VisionTable.swift hoverY was @State on the table: every pointer move re-ran the table body, content() for every cell, updateCache (which drops the plan) and the full table layout
Row hover fill observes its own flag (RowHoverBand) Components/LocalDataTable.swift pointer entering or leaving a row rebuilt that row's cells (AnyView closures)
ChartSelectionScope owns chart hover state Components/ChartSupport.swift, HomeView.swift hover over the summary sparkline or monthly trends chart re-ran the whole HomeView body; three unused @State fields removed
Tooltip anchor view only while hovered Components/VisionTooltip.swift one AppKit view per tooltip'd control (74 call sites, one in each ledger row)
Rectangular clipShape instead of .mask on the detail column and the canvas ShellRootView.swift a mask view over the whole page (offscreen pass through the mask alpha)
No shadow cut-out mask under an opaque card fill Components/VisionCardLayers.swift one masked offscreen pass per card on the reduced tier and with Reduce Transparency
shadow-sm cast by the fill, only on variants that have one Components/VisionButtonStyles.swift a shadow effect (alpha 0) on every ghost, outline and secondary button
Topbar veil over opaque --card on the reduced tier and with Reduce Transparency Components/NativeTopbar.swift a backdrop blur of the page under the bar while scrolled; matches :root.fx-reduced .app-topbar (backdrop-filter: none; background: hsl(var(--card)))

Type of change

  • fix — bug fix
  • feat — new feature
  • refactor — no behavior change
  • perf — performance
  • docs — documentation only
  • chore / ci / test — tooling, build, or tests

Checklist

  • Tests pass (bun run test) and lint is clean (bun run lint): not run locally; the CI swift job builds and tests the package
  • Types check (bun run typecheck): no TypeScript changed
  • Conventional Commit title (type(scope): subject)
  • No leftover console.log / debug code
  • Affected docs/ pages updated (docs/architecture/native-app.md, new section "Swift redraw and layer optimisations")
  • i18n: no strings changed

Architecture & data

  • No architectural change
  • No API change
  • No schema change

Verification

  • swiftc -frontend -parse (swift:6.2-noble container) on the nine changed Swift files: exit 0. This checks syntax only; SwiftUI and AppKit do not exist on Linux, so type checking is left to the CI swift job.
  • bun scripts/native-lint.ts: native-lint ok.
  • Not run: the app itself. No visual check was possible from the cloud.

Residual risk & follow-ups

  • Visible change, reduced tier or Reduce Transparency only: once the page has scrolled, the topbar is an opaque card colour under its gradient instead of a blur of the page. At scroll top nothing changes.
  • Table hover band: after a table re-lays out under a parked pointer, the band keeps its old row until the pointer next moves.
  • Tooltip: the anchor view is created on hover. With no open delay (another tooltip closed under 300 ms ago) the presenter waits up to about 100 ms for it to join the window.
  • Card shadow without the cut-out (opaque fill only): the shadow now lies under the card's antialiased edge pixels; inference: not visible.
  • Button shadow: cast by the fill shape instead of the composited label, fill and border; same silhouette (inference).

Generated by Claude Code

@EraPartner EraPartner self-assigned this Nov 14, 2026
- VisionTable and LocalDataTable: the hover band observes its own state, so a
  pointer move no longer re-runs the table body, cells and layout.
- Home: chart hover state lives in ChartSelectionScope, not the page.
- Tooltips: the AppKit anchor view exists only while its control is hovered.
- Shell: clip the detail column with a rectangle instead of a mask view.
- Cards: no shadow cut-out mask under an opaque fill.
- Buttons: shadow-sm is cast by the fill and only where a variant has one.
- Topbar: no backdrop blur on the reduced tier or with Reduce Transparency.
@EraPartner
EraPartner force-pushed the claude/project-thread-1p1whz branch from 3867cf2 to da97ff5 Compare November 14, 2026 22:04
@EraPartner
EraPartner marked this pull request as ready for review November 14, 2026 22:20
@EraPartner
EraPartner merged commit 85e9272 into main Nov 14, 2026
11 checks passed
@EraPartner
EraPartner deleted the claude/project-thread-1p1whz branch November 14, 2026 22:20
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.

2 participants