Skip to content

fix(ui): keep the session model chip inside its own border - #8

Closed
richardfogaca wants to merge 1 commit into
mainfrom
fix/model-chip-overflow
Closed

fix(ui): keep the session model chip inside its own border#8
richardfogaca wants to merge 1 commit into
mainfrom
fix/model-chip-overflow

Conversation

@richardfogaca

Copy link
Copy Markdown
Owner

Summary

  • Add an opt-in truncate prop to the shared Tag wrapper that owns chip-label truncation correctly in one place
  • Use it for the session footer model chip, replacing a hand-rolled inner span and a hard maxWidth: 180 cap
  • The chip now grows with the available info-bar width and ellipsizes only when it has to, with the full provider/model id on hover

Why / context

On sessions whose model id is long — OpenCode reports provider/model, e.g. kimi-for-coding/kimi-for-coding-highspeed — the label rendered straight through the chip's border and background instead of being clipped.

Root cause: the chip capped itself at maxWidth: 180 and put overflow/text-overflow on a custom inner <span>. Neither rule could take effect.

  • When a tag has an icon, antd moves the children into its own content span, so the custom span sat inside that element — and text-overflow does not apply to an inline box anyway.
  • That content span is a flex item whose automatic minimum size is its min-content width. For nowrap text that is the full label width, so it never shrank and the text escaped the 180px border box.

Implementation notes

truncate keeps the tricky part in the owning component rather than at each call site, per the styling hierarchy in context/guidelines/frontend.md (AntD semantic styles API above inline style, both above CSS, and no .ant-* overrides):

  • The label box clips and ellipsizes itself — antd's content span via styles.content when there is an icon, or a span this wrapper supplies when there is not (antd renders bare children in that case, and ellipsis on a flex root is inert).
  • The root gets minWidth: 0, not overflow: hidden. Zeroing the min-content floor is what lets the chip shrink; clipping the root would also clip the click ripple antd renders inside the tag, so a clickable chip would lose its press feedback.
  • Caller-supplied styles are merged per key, so styling one part (say the icon) cannot silently drop truncation. style merges the same way, with the caller winning on conflicts.
  • The prop's doc comment states the one precondition: a percentage max-width needs an ancestor with a definite width, so a shrink-to-fit parent (table cell, inline-block wrapper) should also pass a numeric style.maxWidth.

Call sites stay declarative — the model chip is now <Tag icon={...} truncate title={modelName}>.

Validation / test plan

  • pnpm vitest run in apps/agor-ui — 187 files, 1277 tests pass
  • 6 cases in the new Tag.test.tsx pin the contract: label clips while the root stays unclipped, the icon-less path gets its own label box, truncation survives a caller styling another part, and truncate off leaves layout untouched
  • 1 case in SessionFooter.test.tsx covers the reported kimi-for-coding/kimi-for-coding-highspeed chip
  • Biome clean on every touched file; pre-commit hooks ran normally
  • pnpm typecheck — no new errors. The 7 pre-existing errors under these paths (stale packages/client build artifacts: reasoningEffortLevels, isAgenticToolName) are identical before and after this branch, and Tag.tsx reports none
  • Rendered before/after not captured here — the dev UI requires a signed-in session. Easiest check: open a session whose model id is longer than the chip and confirm the label ellipsizes inside the border and reads in full on hover

Risks / rollout / rollback

Low. truncate defaults to false, so all 28 existing Tag consumers render byte-identically; only the model chip opts in. No API, data, or config changes. Rollback is reverting the commit.

Known trade-off: without the 180px cap, a very long id can claim the whole info-bar row and wrap the neighbouring chips (tokens, context, IDs) onto an extra line in a narrow panel. That is the intended direction — the hard cap is what produced the reported bug — but it is worth a look if the footer feels tall on small panels.

Out of scope / follow-ups

  • Pill.tsx still hand-rolls the same clipping for IssuePill/PullRequestPill (pillTextStyle, maxWidth: 220). Migrating those to truncate is the natural next step and matches the "converge repeated pill treatments" follow-up in the frontend guidelines.
  • The {cursor, height: 22, display: 'inline-flex', alignItems: 'center'} chip style is still duplicated across the footer info-bar chips and SessionMcpFooterControl; extracting a small footer-chip component would remove it.

Long provider/model ids (OpenCode, e.g.
kimi-for-coding/kimi-for-coding-highspeed) painted straight through the
footer model chip's 180px box. The ellipsis never applied: antd moves a
tag's children into its own content span when an icon is set, so the
custom span sat inside that flex item, and the flex item's automatic
minimum size kept it at full text width.

Add a `truncate` prop to the shared Tag wrapper that owns the fix once:
`minWidth: 0` on the root so it can shrink without clipping (clipping the
root would eat antd's click ripple), the label box clipping and
ellipsizing itself, and a per-key merge so a caller styling one part
cannot silently drop truncation. Icon-less tags get their own label box
because antd renders bare children there.

The model chip now grows with the available info-bar width and ellipsizes
only when it must, with the full id on hover.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 20:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the shared UI Tag wrapper to support an opt-in truncate mode so long chip labels (notably the session model provider/model id) ellipsize within the tag border instead of overflowing, and adds targeted unit tests to lock in the behavior.

Changes:

  • Add truncate?: boolean to the shared Tag wrapper and implement truncation via semantic styles.content (icon path) or a wrapper <span> (no-icon path).
  • Update the SessionFooter model chip to use truncate + title instead of a hand-rolled inner span and fixed maxWidth.
  • Add unit tests for the Tag truncation contract and a regression test for the long model id case in SessionFooter.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
apps/agor-ui/src/components/Tag/Tag.tsx Adds truncate behavior and style-merging logic to keep ellipsis/clipping owned by the shared Tag wrapper.
apps/agor-ui/src/components/Tag/Tag.test.tsx Adds tests validating truncation behavior for icon and no-icon variants and style merging.
apps/agor-ui/src/components/SessionPanel/SessionFooter.tsx Switches the model chip to truncate and removes the fixed max-width + inner span workaround.
apps/agor-ui/src/components/SessionPanel/SessionFooter.test.tsx Adds a regression test for truncating a long provider/model id and ensuring full value is available via title.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +24 to +28
const TRUNCATE_LABEL_STYLE: CSSProperties = {
minWidth: 0,
overflow: 'hidden',
textOverflow: 'ellipsis',
};
@richardfogaca

Copy link
Copy Markdown
Owner Author

Superseded — opened against preset-io/agor instead.

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.

2 participants