Waleed 5dbe95e0c1 fix(files): reserve image layout space so images stop reflowing on load (#6299)
* fix(files): reserve image layout space so images stop reflowing on load

A markdown image with no stored dimensions reserved zero vertical space until
it downloaded, then snapped to its natural height and pushed content below it
down (cumulative layout shift). Reserve the box up front from the image's
intrinsic aspect ratio instead.

Store intrinsic width/height as workspace_file metadata (not in the markdown —
it stays clean `![](src)`), read it synchronously from the already-loaded file
list to reserve a responsive aspect-ratio box on first render, and lazily
backfill it once per image on first view via a write-gated, idempotent PATCH.
The node view falls back to on-load measurement for the first-ever view and for
external images. Images stay fluid (max-width:100%, height:auto).

* fix(files): address review — reserve on stale memo, clear dims on content swap

- onLoad guards on the memoized storedDimensions the render uses (not a fresh
  cache read), so a sibling's non-reactive backfill can't leave a view unreserved.
- updateWorkspaceFileContent clears width/height when it swaps bytes, so stale
  dimensions can't be reserved for new content (and the null re-enables backfill).
- Keep optimistically-cached dimensions when the PATCH fails (correct measurement;
  a 403/transient error shouldn't wipe sibling reservations).
- Test imports the sibling via the absolute @/ path.

* fix(files): re-derive image dimensions on content swap instead of clearing

Clearing width/height to NULL on a content swap reopened the width IS NULL
backfill path, so a late fire-and-forget PATCH for the previous image could
write its stale size onto the new content. Instead, measure the new bytes'
intrinsic dimensions server-side (image-size, headers only) and store those
(or null for a non-image), so the row always matches the current content and a
stale backfill can't apply.

* fix(files): self-heal image dimensions from the browser instead of server-measuring

Round-3 review: server-side image-size returns raw (non-EXIF) dimensions, and
clearing dims on content swap reopened the stale-PATCH race for non-image or
unmeasurable content. Move authority to the browser's own naturalWidth/Height
(EXIF-correct): the node view reserves from it and reports on any mismatch, and
updateWorkspaceFileDimensions overwrites (no width IS NULL gate) so stale values
self-correct on the next view. Reverts the server-side measurement and the
content-swap dimension touch entirely.

* fix(files): clear image dimensions on content swap (completes self-heal)

The self-heal rework left the old image's dimensions in the row after a content
replacement, so the next view of the new bytes reserved a wrong-sized box before
correcting. Clear width/height on the content-swap write so the row never
describes stale content: the next view falls back to the baseline first-load
reflow and the browser's measurement backfills the correct size. No server-side
decode (EXIF-safe), and the client's overwrite-on-mismatch handles a late PATCH.

* fix(files): guard dimension writes by content key so a stale PATCH can't persist

Ties the dimensions write to the storage key the client measured. The key is
regenerated on every content replacement, so an in-flight PATCH measured against
superseded bytes is rejected at the DB (WHERE key = measured key) instead of
persisting the old aspect ratio for new content. Closes the last stale-ordering
window Greptile flagged — the write is now content-version-conditioned, not just
corrected on the next render.

* chore(files): fix stale route TSDoc and hoist a regex literal (cleanup pass)

Post-review /cleanup: the dimensions route TSDoc still described backfill-once
behavior (now overwrite-on-mismatch via the content-key CAS); the bare-pixel
width regex is hoisted to module scope. No behavior change.

* fix(files): reflect the content-version guard outcome in the dimensions response

The route returned success:true even when updateWorkspaceFileDimensions matched
0 rows (the CAS rejected a write whose measured key no longer matches the row).
Return success:<whether a row was written> and widen the contract response to
{ success: boolean }. Not an error path — the client's next measurement persists
once its file list has the new key; this just stops the API claiming a persist
that did not happen.

* fix(files): reconcile the cache when a dimension write is content-version-rejected

Previously the client discarded a success:false (CAS-rejected) response, leaving
its optimistic patch — which is for superseded bytes — lingering in the file-list
cache. On rejection, invalidate the list so the cache reconciles with the new
content (whose real size persists on its next load). Deliberately NOT a retry:
re-sending the old measurement under the new key would write the wrong size. A
transport error / read-only 403 still keeps the optimistic value (it's the real
displayed size).

* docs(files): align stale dimension docs with the overwrite/self-heal behavior

Cleanup audit: the ImageDimensionsSource/reportImageDimensions interface docs and
one route log string still said backfill-once/no-op; the mechanism overwrites on
mismatch to self-correct. Wording only, no behavior change.
2026-08-05 17:53:41 -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 install
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, and knowledge 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

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.

Reconfigure an optional capability without rerunning the full wizard:

bun run setup status
bun run setup email
bun run setup storage
bun run setup sandbox
bun run setup jobs
bun run setup cache
bun run setup knowledge
bun run setup llm
bun run setup integration slack

bun run setup status detects the effective local-dev, Docker Compose, or current-context Helm configuration and reports configured, missing, or invalid capabilities and OAuth integrations without printing credential values. This is separate from bun run sim status, which reports whether installed services are running and healthy.

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%