fix(web): size the chat image slot from server-reported dimensions - #10200
Conversation
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 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.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: 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 You can add or adjust custom eligibility rules. Learn more. |
f3bcde2 to
b1c6742
Compare
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>
b1c6742 to
d18793c
Compare
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.
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).
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
ChatMarkdownAssetImageapplies the server'simageDimensions(#10198) through the existingauthoredImageSizeStyle, so the slot is the image's final box from the moment the URL resolves. An authoredwidth/heightstill wins; a caller's cap (the viewed-image row'smaxHeight: 16rem) layers on top rather than shadowing the dimensions.Evidence
Cold thread mount with image responses delayed 4 s,
layout-shiftobserver armed before mount (same harness as #9938):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/webtypecheck clean.aspect-ratio: 720 / 1400on the slot and the settled<img>; a 16remmaxHeightcap 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
ChatMarkdownAssetImagenow reads reported width and height from successful asset URL loads and derives inline width, aspect ratio, and a height-derived max-widthauthoredImageSizeStylegains an optional height-cap argument (default 30rem); when both dimensions are present the cap is folded into the max-width calculationPlainWorkEntryRowin MessagesTimeline.tsx switches from an inlinemax-heightstyle to the newmaxHeightRemprop, keeping the 16rem valuestyleprops still take precedence; callers without dimensions can still apply a custom rem height cap as a fallbackMacroscope summarized d18793c.