Waleed 806ea0c5bb fix(tooltip): stop text blurring for ~150ms every time a tooltip appears (#6211)
* fix(tooltip): remove velocity skew/scale that blurred text on every appear

The tooltip animated a fractional scale() + skew() over 150ms on the element
containing its text. Chrome promotes the bubble to a compositor layer for the
transition, rasterizes the text once at the pre-transition scale, then
GPU-resamples that bitmap for the duration — so text rendered blurry until the
transition settled and the layer re-rasterized at 1:1.

It fired on every appear: a pointer entering a trigger is by definition moving,
so the first pointermove after pointerenter always set a non-zero skew and a
fractional scale.

- drop the velocity-reactive skew/scale flourish and the pointer-velocity
  bookkeeping that existed only to feed it
- round tooltip position to whole pixels; clientX/clientY are fractional on
  HiDPI/zoomed displays, leaving the bubble on a subpixel boundary
- drop the dead `filter` from the transition list — nothing ever set a filter
- skip the state update when the rounded position is unchanged, so pointer
  jitter no longer re-renders every Tooltip.Trigger/Content consumer

The 150ms ease-out translate is kept, so the bubble still trails the cursor.

* improvement(tooltip): keep the velocity flourish, drive it without a CSS transition

Restores the velocity-reactive skew/scale removed in the previous commit. The
flourish was never the problem on its own — handing it to a CSS transition was.
An interpolated fractional scale makes the compositor rasterize the tooltip's
text once and resample that bitmap for the duration, which is what read as blur.

Applied as a static value per pointer event instead, so every frame is
rasterized at its own scale:

- split the transform across the individual `translate`, `scale`, and
  `transform: skew()` properties, and transition only `translate` — position
  still eases toward the cursor, the flourish no longer interpolates
- smooth the pointer velocity in JS (low-pass filter) to replace the smoothing
  the CSS transition used to provide, so the squish still ramps rather than
  snapping between raw per-event velocities
- quantize the flourish to 3 decimals so jitter below the visible threshold
  settles instead of re-rendering every consumer

Whole-pixel position rounding and the redundant-update bail-out are unchanged.

* fix(tooltip): don't seed pointer velocity from the trigger box on focus

The previous commit routed `onFocus` through a shared reveal helper that seeds
`lastPointerRef` from the coordinates it is given. For focus those are the
trigger's box center, not the pointer — so if the pointer already happened to be
over the trigger, the next `pointermove` measured the box-to-cursor delta as
velocity and spiked the skew/scale flourish.

Split the helper in two: reveal-from-pointer seeds velocity tracking,
reveal-from-element leaves it cleared. Restores the pre-PR behavior, where focus
explicitly nulled the pointer snapshot.

Caught by Cursor Bugbot.

* fix(tooltip): make the flourish smoothing frame-rate independent

The velocity low-pass filter applied a fixed coefficient per pointer event, so
how fast the squish settled depended on how fast the device emitted events —
233ms at 30Hz down to 29ms at 240Hz, an 8x spread for the same gesture. It was
also far snappier than the 150ms CSS ease-out it replaced, so the flourish read
as twitchier than before.

Derive the coefficient from the real elapsed time instead
(1 - exp(-dt / tau), tau = 50ms). Settling is now flat at ~150ms from 60Hz
upward, matching the duration of the transition this stands in for.

Also separates the smoothing delta from the velocity-normalization delta: the
latter is still floored at one frame to keep a 1ms event from reporting an
enormous velocity, but flooring the former was itself a source of frame-rate
dependence below 16ms.

Verified against Chrome's documented re-raster behavior: a layer is re-rastered
at its new scale when the scale changes via script, but not when a declarative
animation interpolates it, which is why the flourish must stay out of the
transition list.
https://developer.chrome.com/blog/re-rastering-composite
2026-08-03 11:37:23 -07:00

Sim.ai Documentation Slack X

Ask DeepWiki Set Up with Cursor

Sim — Integrate, Context, Build, and Monitor AI agents

A workspace to build, deploy and manage AI agents and workflows.

Quickstart

Cloud-hosted: sim.ai

Open sim.ai

Self-hosted

git clone https://github.com/simstudioai/sim.git && cd sim
bun run setup

Open http://localhost:3000

The Sim platform — chat on the left, the visual workflow builder on the right

Capabilities

  • Connect 1,000+ integrations and every major LLM
  • Add Slack, Notion, HubSpot, Salesforce, databases, and more
  • Build agents visually, conversationally, or with code
  • Ingest files, knowledge bases, and structured table data
  • Monitor runs, logs, schedules, and workflow activity

One workspace, every surface

Chat and workflows are just the start — tables, files, knowledge, and scheduled tasks all live in the same workspace.

Tables in Sim — structured data your agents can query

Tables — a database, built in

Files in Sim — documents for your team and every agent

Files — one store for your team and every agent

Knowledge bases in Sim — synced docs your agents can search

Knowledge — your agents' memory

Scheduled tasks in Sim — recurring agent runs on a calendar

Scheduled tasks — runs on your schedule

Self-hosting

Requirements: Bun and Docker.

bun run setup is an interactive wizard: it provisions the database, generates secrets, writes your .env files, connects a Chat API key, and starts Sim the way you choose:

  • Local dev — run from source to contribute or hack on Sim
  • Docker Compose — a self-contained instance for testing self-hosting
  • Kubernetes (Helm) — deploy to a local cluster

When it finishes, open http://localhost:3000.

Manage your install with bun run sim:

bun run sim start | stop | restart   # bring your install up / down / cycle
bun run sim status                    # what's installed and healthy
bun run sim logs                      # follow logs
bun run sim doctor                    # diagnose configuration problems
bun run sim down                      # remove containers (data kept)
bun run sim reset                     # archive .env and wipe managed data

sim detects how you're running (Docker Compose, local dev, or Kubernetes) and acts accordingly.

Prefer a bare sim? Run bun link once — but note sim lands in ~/.bun/bin, which Homebrew's bun doesn't add to your PATH, so you may need export PATH="$HOME/.bun/bin:$PATH" in your shell profile.

Sim also supports local models via Ollama and vLLM. See the self-hosting docs for details.

Chat API Keys

Chat is a Sim-managed service. bun run setup connects a Chat API key for you — sign in when it opens your browser and the key is stored automatically. To view, create, or revoke keys later, go to sim.ai/selfhost/settings/chat-keys.

Environment Variables

See the environment variables reference for the full list, or apps/sim/.env.example for defaults.

Tech Stack

Next.js · Bun · PostgreSQL · Drizzle · Better Auth · Tailwind — and the rest of the stack

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Built by the Sim team in San Francisco

Languages
TypeScript 77%
MDX 20.8%
JavaScript 1.9%
CSS 0.1%