Skip to content

fix(web): size the chat image slot from server-reported dimensions - #10200

Merged
juliusmarminge merged 3 commits into
media-dims/mobilefrom
media-dims/web
Sep 5, 2026
Merged

juliusmarminge merged 3 commits into
media-dims/mobilefrom
media-dims/web

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Sep 5, 2026 •

Copy link
Copy Markdown
Member

Stack 3/3 (#10201). Depends on #10198.

Problem

After #9938 a workspace image on web still moved the rows below it once: a portrait screenshot grew past its 16:9 slot when it decoded.

Fix

ChatMarkdownAssetImage applies the server's imageDimensions (#10198) through the existing authoredImageSizeStyle, so the slot is the image's final box from the moment the URL resolves. An authored width/height still wins; a caller's cap (the viewed-image row's maxHeight: 16rem) layers on top rather than shadowing the dimensions.

Evidence

Cold thread mount with image responses delayed 4 s, layout-shift observer armed before mount (same harness as #9938):

#9938 this PR
Media layout-shift entries 1 (0.013 — portrait growing past 16:9) 0

The only remaining entry on the page is the composer resizing, unrelated to media.

Verification

  • ChatMarkdown.workspace-images.test.tsx: slot is sized from server dimensions (width: 720px; aspect-ratio: 720 / 1400); authored size overrides server dimensions. 32 passed.
  • apps/web typecheck clean.
  • In-browser: portrait image renders with aspect-ratio: 720 / 1400 on the slot and the settled <img>; a 16rem maxHeight cap yields a 247×256 box.

Model: Claude Fable 5 · Harness: Claude Code in T3 Code

🤖 Generated with Claude Code

Note

Size chat asset images from server-reported dimensions

  • ChatMarkdownAssetImage now reads reported width and height from successful asset URL loads and derives inline width, aspect ratio, and a height-derived max-width
  • authoredImageSizeStyle gains an optional height-cap argument (default 30rem); when both dimensions are present the cap is folded into the max-width calculation
  • PlainWorkEntryRow in MessagesTimeline.tsx switches from an inline max-height style to the new maxHeightRem prop, keeping the 16rem value
  • Explicit incoming style props still take precedence; callers without dimensions can still apply a custom rem height cap as a fallback
  • Risk: asset images without server-reported dimensions no longer get a computed aspect ratio — they fall back to the height-cap path only, which may shift layout for previously auto-sized images in ChatMarkdown.tsx

Macroscope summarized d18793c.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Sep 5, 2026
@juliusmarminge juliusmarminge changed the title media dims/web fix(web): size the chat image slot from server-reported dimensions Sep 5, 2026
Comment thread apps/web/src/components/ChatMarkdown.tsx Outdated
@github-actions

github-actions Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 13.6 KiB — 15.1 KiB ✅
Codex Thread snapshot wire — 7.0 KiB — 7.3 KiB ✅
Codex Live turn WebSocket wire — 6.6 KiB — 7.8 KiB ✅
Codex Live turn WebSocket decoded — 57.1 KiB — 66.4 KiB ✅
Codex Live turn messages — 10 — 21 ✅
Claude Total thread wire — 13.5 KiB — 15.1 KiB ✅
Claude Thread snapshot wire — 7.0 KiB — 7.3 KiB ✅
Claude Live turn WebSocket wire — 6.5 KiB — 7.8 KiB ✅
Claude Live turn WebSocket decoded — 57.8 KiB — 66.4 KiB ✅
Claude Live turn messages — 8 — 21 ✅

Baseline: unavailable · PR result: d18793c · 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: 113.8 KiB
  • Claude decoded thread snapshot: 114.5 KiB

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

@macroscopeapp

macroscopeapp Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a localized image-layout fix that uses existing server dimensions, preserves authored sizes, and retains explicit height caps. It also changes the default rendering of un-authored workspace images from the prior 16:9-style slot to their reported aspect ratio, making it a product-default behavior change.

No code changes detected at d18793c. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

juliusmarminge and others added 3 commits September 5, 2026 14:01
A workspace image still moved the rows below it once, when a portrait
screenshot grew past its 16:9 slot. The signed URL now carries the
image's pixel size, so the slot is the final box from the start. An
authored width and height still win.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The viewed-image row passes a max height, which used to shadow the
server dimensions entirely. Merge them so the slot is the image's box
capped by the caller.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A max-height alone does not feed back through aspect-ratio once width is
definite, so a capped portrait image kept a box wider than the picture.
The viewed-image row now passes its cap in rem and the width bound
shrinks to match.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit d0f855b into main Sep 5, 2026
33 of 44 checks passed
@juliusmarminge
juliusmarminge deleted the media-dims/web branch September 5, 2026 22:51
leoisadev1 added a commit to opencoredev/akeru-bot that referenced this pull request Sep 11, 2026
Chat images reserved a 16:9 slot, then jumped to their natural size after
decode. The server now reads PNG, JPEG, GIF, and WebP headers when it
issues a signed URL, and live bot/group markdown uses that size for the
slot.

Adapted from pingdotgg#10198 and pingdotgg#10200. Host-file media-file
resources and native mobile slot sizing remain follow-up work.

Implemented with Grok 4.6 High in Grok Build via Orca.
incognitojam added a commit to incognitojam/styal that referenced this pull request Sep 24, 2026
When the PR selected for `early` does not apply at the reconciled
upstream boundary, the command stopped with a single line that gave
nothing to act on:

```console
$ node scripts/upstream-queue.ts early 11784 --through upstream/main
Selected pingdotgg#11784 cannot be applied at the upstream boundary.
```

That happens when the PR builds on upstream work the fork has not taken
in yet, and the planner only looks for dependencies in the changes it
replays after a successful apply.

The command now reports which of the PR's files conflict at the boundary
and which earlier pending upstream PRs and direct commits change those
files, in upstream order. Text output lists the first 20 and says how
many remain; `--json` includes every source with its SHA, PR, and title.
A plan that succeeds prints as before. The intake runbook's `early`
paragraph describes the report.

## Validation

- A new test in `scripts/upstream-early.test.ts` checks which sources
the report lists: pending sources after the boundary and before the
blocked PR whose files overlap the conflicting paths, including direct
commits and directory prefixes, and not recorded or later sources.
- `scripts/upstream-early.test.ts` and `scripts/upstream-queue.test.ts`
pass (21 tests); lint, formatting, and the scripts typecheck are clean.
- Against fetched fork `main` (`495b7cfd5`) and upstream `main`
(`b2b43bef7`), the same command now prints:

  ```console
  $ node scripts/upstream-queue.ts early 11784 --through upstream/main
  Fork 495b7cf; reconciled through d28077e
pingdotgg#11784 does not apply at the reconciled boundary. 16 of its files
conflict there:
    apps/mobile/src/features/threads/ThreadFeed.tsx
    apps/mobile/src/features/threads/thread-work-log.tsx
    apps/mobile/src/lib/threadActivity.ts
    apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts
    apps/server/src/orchestration/decider.ts
    apps/server/src/provider/Layers/ClaudeAdapter.test.ts
    apps/server/src/provider/Layers/ClaudeAdapter.ts
    apps/server/src/textGeneration/ThreadTitleContext.test.ts
    apps/server/src/textGeneration/ThreadTitleContext.ts
    apps/server/src/ws.ts
    apps/web/src/components/chat/MessagesTimeline.logic.ts
    apps/web/src/components/chat/MessagesTimeline.tsx
    apps/web/src/components/settings/SettingsPanels.tsx
    packages/client-runtime/src/state/environmentHttpAuth.test.ts
    packages/client-runtime/src/state/threadSnapshotHttp.ts
    packages/contracts/src/orchestration.ts
Earlier pending upstream changes to those files: 110 PRs. Take them in
first, or add their PRs to this plan.
pingdotgg#9594 fix(connect): refresh HTTP credentials without reconnecting
(pingdotgg#9594)
    pingdotgg#9894 fix(chat): show hours for long runs (pingdotgg#9894)
pingdotgg#9933 fix(settings): share restart continuation across environments
(pingdotgg#9933)
    pingdotgg#10092 fix(web): scale agent spawn rows with interface font (pingdotgg#10092)
pingdotgg#9965 fix(web): keep the composer expanded until the thread can scroll
(pingdotgg#9965)
pingdotgg#9935 fix(web): align tool disclosure chevrons with expanded state
(pingdotgg#9935)
pingdotgg#10199 fix(mobile): size a chat image's frame before its bytes arrive
(pingdotgg#10199)
pingdotgg#10200 fix(web): size the chat image slot from server-reported
dimensions (pingdotgg#10200)
pingdotgg#10167 refactor(client-runtime): remove unused runtime exports and
helpers (pingdotgg#10167)
pingdotgg#10173 fix(mobile): restore live tool shimmer and add a Thinking row
(pingdotgg#10173)
pingdotgg#10210 fix(mobile): only make work rows expandable when the body adds
something (pingdotgg#10210)
pingdotgg#10211 fix(mobile): fold subagent lifecycle rows into one batch per
spawn (pingdotgg#10211)
    pingdotgg#10212 fix(mobile): stop clipping expanded tool groups (pingdotgg#10212)
    pingdotgg#9875 feat: show provider usage limits with /usage-limits (pingdotgg#9875)
pingdotgg#9561 fix(ui): unify loading and refresh feedback across clients (pingdotgg#9561)
pingdotgg#10226 refactor(web): keep feature component helpers private (pingdotgg#10226)
pingdotgg#9754 feat(settings): add shared project defaults and scoped overrides
(pingdotgg#9754)
pingdotgg#9895 feat(connections): balance new threads across connected machines
(pingdotgg#9895)
pingdotgg#10273 fix(mobile): stop the work log flickering during subagent runs
and failing calls (pingdotgg#10273)
    pingdotgg#10271 fix(mobile): save linked media from chat (pingdotgg#10271)
    ... 90 more; --json lists every source.
  ```

`--json` returns the same 16 paths and all 110 sources. `early 5521
--through upstream/main`, which plans successfully, prints the same
summary as before.

---
Written by an agent (Claude Code, claude-opus-5-5).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10-29 changed lines (additions + deletions). 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