Skip to content

fix(ui-web): a picture in a delegation card fits the card instead of widening it - #539

Merged
0xKT merged 1 commit into
EverMind-AI:mainfrom
Handsome-wzw:fix/a_picture_fits_its_card
Sep 22, 2026
Merged

0xKT merged 1 commit into
EverMind-AI:mainfrom
Handsome-wzw:fix/a_picture_fits_its_card

Conversation

@Handsome-wzw

@Handsome-wzw Handsome-wzw commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Two ways a picture widened a delegation or tool card past its column, both measured in the served page against the real stylesheet.

A generated image (.gshots, what an image_generate or ppt_generate_image call opens) was capped by height only. A 1600x400 banner scaled to 260px tall comes out 1040px wide in a 562px column and hands the tool card a horizontal scrollbar. The column now caps the width too, with min-width: 0 on the flex item so the cap can apply.

An image or inline svg inside prose (.prose img, .prose svg) had no cap at all and was laid out at its natural width. In a spawn card that box is the run's own scroll pane, which then scrolled sideways under the report. Both now fit the column, height following so an attribute-sized image keeps its aspect ratio. The artifact chip's icon keeps its own 15px rule, which is the more specific of the two.

Nothing else in prose does this: a table scrolls inside .tw, a code block inside pre, long words break under overflow-wrap. One file, two rules, no markup change.

Rebased onto main and retargeted there. This was opened against refactor/ui_web_architecture, which is retired and read-only now that the frontend rebuild landed on main; the single commit was replayed onto main, where it applied cleanly. Both problems were re-confirmed on main before the move: the rules this adds are absent from main's stylesheet, and features/transcript/TranscriptPage.tsx still builds .gshots for the two media tools while lib/prose.ts still renders .prose.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

  • npm run lint --prefix ui-web: 0 errors (the 4 warnings are present on main unchanged)

  • npm run type-check --prefix ui-web: passes

  • npx vitest run in ui-web: 188 files, 2464 tests passed, including the CSS gates (css-one-owner, css-balance, check-css) and check-class-namespace

  • npm run build --prefix ui-web, ui-web/build.py and scripts/check-page.mjs: build passes and both boot snapshots match their goldens unchanged, since no markup moves

  • Served this head's build from a real gateway in a 900px window and measured the markup features/transcript/TranscriptPage.tsx builds for a media tool call. Before: the card body had scrollWidth 1611 in a 562px box, holding a 1040x260 picture, and a 1600px-wide prose image in a 540px column. After: 562 in 562, the picture at 540x135 and the prose image at 540. No horizontal scrollbar either way, and toggling the two rules off in the live stylesheet brings the overflow back.

  • Relevant tests pass locally

  • Relevant lint / type checks pass locally

  • User-facing docs or screenshots are updated when needed

Risk

  • Security impact considered
  • Backward compatibility considered
  • Rollback path is clear for risky changes

CSS only. A picture that already fit its column is unchanged; only one wider than the column is scaled down. Reverting the commit restores the previous rules.

Related Issues

N/A

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

Reviewed the full target-to-head diff, the generated-shot and delegation/prose callers, selector specificity, relevant stylesheet history, backward compatibility, and the Web UI architecture/CSS constraints. The flex item can now shrink to the card width, the image retains its aspect ratio, and the existing more-specific artifact-icon sizing remains intact. I also checked the repository rules and confirmed that this change does not weaken tests.

Verification:

  • npm test: 181 files passed, 2518 tests passed (the run emitted the existing happy-dom aborted-fetch noise)
  • npm run type-check: passed
  • npm run lint: passed with 0 errors and 5 warnings in untouched files
  • npm run build: passed
  • python3 build.py: passed; both boot snapshots matched
  • node scripts/check-page.mjs: passed
  • git diff --check: passed

The initial npm test attempt could not start because dependencies were absent; after npm ci, the unmodified suite passed as reported above. The environment uses unsupported Node 23, so npm warned that the locked Vitest/ESLint tooling expects Node 20, 22, or 24+, but the checks completed successfully.

@gloryfromca

Copy link
Copy Markdown
Member

The frontend rebuild landed on main in #612, as 83 individual commits rather than a squash, so refactor/ui_web_architecture has served its purpose and is now read-only: nothing can be pushed to it or merged into it.

This PR is not lost and was deliberately left open rather than closed -- deleting the branch would have closed it with no way to reopen it. To land the change:

  1. Rebase the branch onto main. Everything this PR was written against is there, and main has moved on past it as well (the retired branch is 157 commits behind).
  2. Change this PR base from refactor/ui_web_architecture to main (Edit, next to the title).

Retargeting before the rebase will show the whole gap between the two branches rather than your change, so do them in that order. Shout if the rebase turns out to be more than it looks and we will sort it out.

@gloryfromca

Copy link
Copy Markdown
Member

Blocking: the branch must be rebased onto main and the PR retargeted to main.

Confirmed the PR is still at 2705a60272e0 against refactor/ui_web_architecture, so the maintainer note changes my standing stance even though the reviewed code itself remains clean. Current GitHub main is a40e911dd; a three-way dry run of this PR's single commit using its original base applies cleanly to ui-web/src/styles/page.css, with no conflicts.

I did not rewrite or push the author's branch: repository rules require explicit authorization for the history rewrite and push. After the author rebases, force-pushes with lease, and retargets the PR, the new head will need its own review verdict.

Verification on the unchanged head: npm test passed 181 files and 2518 tests; the run emitted the existing happy-dom aborted-fetch/network noise.

…widening it

Two ways a picture widened a card past its column, both measured in the
served page against the real stylesheet:

A generated image (`.gshots`, what an image_generate or ppt_generate_image
call opens) was capped by height only: a 1600x400 banner scaled to 260px
tall comes out 1040px wide in a 562px column and hands the tool card a
horizontal scrollbar. The column now caps the width too, with `min-width: 0`
on the flex item so the cap can apply.

An image or inline svg inside prose (`.prose img`, `.prose svg`) had no cap
at all and was laid out at its natural width; in a spawn card that box is
the run's own scroll pane, which then scrolled sideways under the report.
Both now fit the column, height following so an attribute-sized image keeps
its aspect ratio. The artifact chip's icon keeps its own 15px rule, which is
the more specific of the two.

Nothing else in prose does this: a table scrolls inside `.tw`, a code block
inside `pre`, long words break under `overflow-wrap`.

Verified against this head in a real gateway's page, 900px window, on the
markup features/transcript/TranscriptPage.tsx builds for a media tool call:
the card body went from scrollWidth 1611 in a 562px box (a 1040x260
picture) to 562 in 562 (540x135), and a 1600px-wide prose image from 1600
to 540. No horizontal scrollbar either way.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
@Handsome-wzw
Handsome-wzw force-pushed the fix/a_picture_fits_its_card branch from 2705a60 to e022cef Compare September 22, 2026 03:23
@Handsome-wzw
Handsome-wzw changed the base branch from refactor/ui_web_architecture to main September 22, 2026 03:24
@Handsome-wzw Handsome-wzw reopened this Sep 22, 2026

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

The rebase and retarget are complete. I reviewed the full github/main...HEAD diff and used git range-diff to confirm that the substantive CSS patch was preserved. I also checked the current generated-media and delegated-report callers, including the newer DAG terminal-output path, selector specificity, stylesheet history, backward compatibility, test integrity, and the applicable AGENTS.md, CONTEXT-MAP.md, Web UI context, and CSS architecture rules. The image wrapper can shrink within its flex row, image aspect ratios are retained, and existing prose SVG consumers keep their intended sizing. No tests were removed or weakened; the revision changes only ui-web/src/styles/page.css.

Verification on the rebased head:

  • npm ci: passed
  • npm test: 188 files passed, 2464 tests passed
  • npm run type-check: passed
  • npm run lint: passed with 0 errors and 4 warnings in untouched files
  • npm run build: passed
  • python3 build.py: passed; both boot snapshots matched
  • node scripts/check-page.mjs: passed
  • git diff --check github/main...HEAD: passed
  • source-language gate via the Makefile target's exact uv command: passed (make is unavailable in this environment)

GitHub CI was still pending when this review was submitted.

@0xKT

0xKT commented Sep 22, 2026

Copy link
Copy Markdown
Member

Not a blocker -- the fix works, and I measured it. Two of the three lines do not do what their comments say, which is worth knowing before the next person reads them as precedent.

Everything below is e022cefd merged onto the live tip 15c93d39, measured in Chromium (playwright) against the page's real stylesheet, with the diff's own lines retracted one at a time as the control.

It does fix the reported bug

A 1600x400 picture in a delegation card, card 520px wide:

before this PR (all three lines retracted)   shot 1040x260
as shipped                                   shot  496x124

1040 is exactly the number your comment predicts (1600x400 capped at 260 tall). And both claims your comments make about what must survive hold:

                       before -> after
code-block copy icon   13x13  -> 13x13     (svg.ic, attribute-sized, from lib/prose.ts)
artifact chip icon     15x15  -> 15x15     (.prose .artf svg, the more specific rule, as you say)

I checked the cascade rather than trusting it: .prose svg { height: auto } does outrank a height="13" presentation attribute, but the SVG carries viewBox="0 0 24 24", so width:13px + height:auto resolves back to 13. No collateral.

1. .gshots .pic.shot { min-width: 0; max-width: 100% } changes nothing

Retracted on its own, in every configuration I could build where a flex item's min-width is supposed to bite:

case                                   with the line              without it                 same?
one 1600x400 shot, 520px card          496x124                    496x124                    yes
three shots (.gshots.set), 520px card  496x124 x3                 496x124 x3                 yes
three shots, 320px card                296x74 x3                  296x74 x3                  yes
one shot, 200px card                   176x44                     176x44                     yes
two portrait 400x1600 shots            40x160 x2                  40x160 x2                  yes

The comment says min-width: 0 "is what lets the cap apply, since a flex item otherwise refuses to shrink below its content". That is true when the cap is on the flex item -- but the cap that works here is on the img inside it, and an image's max-width does not need its parent's min-width relaxed. Retract only the img line and the bug comes straight back:

retract .gshots .pic.shot img's max-width   shot 1040x260   <- the whole fix is this one declaration

2. .prose img cannot match anything in this tree

.prose svg has real referents (the code-block copy button, the artifact chip) and is harmless. .prose img has none:

  • every .prose in the app is a leaf with dangerouslySetInnerHTML and no React children -- TranscriptPage.tsx:893,1040,1165,1185,1295, WorkspacePage.tsx:577, Skills.tsx:138, NodeRecord.tsx:155;
  • all of them are fed by md() (store.mdHtml = (src) => md(src), transcript/store.ts:107);
  • md() never emits an image. Run against the real renderer:
markdown image   ![banner](url)              imgs=0  -> <p><a href="url" target="_blank" ...>banner</a></p>
raw html img     <img src="url" width=1400>  imgs=0  -> escaped to &lt;img ...
raw html svg     <svg width=1400>            imgs=0  -> escaped

and grep -c '<img' src/lib/prose.ts is 0 (positive control: <svg> is 3, so the search works).

So the delegation-card picture your first comment describes is never inside a .prose -- .gshots is pushed into a .dtl block that is a sibling of the prose, not a child (TranscriptPage.tsx:398 into the body array rendered at :426). That is why retracting the .prose rule alone changes nothing:

retract .prose img/.prose svg   shot 496x124   (unchanged)

I built this wrong the first time, by the way: my first probe nested .gshots inside .prose, which let the .prose rule cover the shot and made the .gshots lines look inert. The numbers above are from the real nesting.

None of this is harmful -- a defensive cap on a future markdown image is a reasonable thing to want. It is the comments I would change: as written they tell the next reader that .prose img is what fixed the card and that min-width: 0 is load-bearing, and neither is true here.


Gates and suites on the merged tree: GATES OK head=e022cefd tip=15c93d39 base=a40e911d ruff/lint-imports/commit/large/lang rc=0; npm test 190 files / 2495 tests pass; npx tsc --noEmit exit 0; npm run gen:check in sync (194 methods); npm run build + ui-web/build.py both booted-DOM goldens OK at 243 nodes.

@0xKT
0xKT merged commit 54b1650 into EverMind-AI:main Sep 22, 2026
26 of 29 checks passed
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.

3 participants