fix(web): clamp preset card descriptions instead of sizing the roster

A preset publishes its own description, of any length, and `.cards` sizes
rows with `grid-auto-rows: 1fr` — which makes every implicit row the same
height, not just the row holding the tall card. One long description
therefore set the height of the whole roster.

The description now clamps to four lines and offers the rest through the
shared Tooltip, attached only while the element actually overflows. Card
height stays derived: with the description bounded, `grid-auto-rows: 1fr`
already equalizes, and a card carrying the broken-preset reason or a
revealed path still sizes itself.

Tooltip gains an optional `maxWidth`; its default half-viewport cap
renders a description wider than the settings dialog it belongs to.

The failed-shape-check badge reads "Failed to load" rather than "Broken":
discovery reports a composition that is missing, unreadable, or malformed,
which overstates as damage.

Fixes #2238
This commit is contained in:
Yichen Jiang
2026-08-11 14:09:04 +08:00
parent a40155ad23
commit 9f5eb5da8a
14 changed files with 251 additions and 26 deletions

View File

@@ -95,6 +95,18 @@ describe('Tooltip', () => {
const rect = (left: number, right: number): DOMRect =>
({ left, right, top: 0, bottom: 20, width: right - left, height: 20, x: left, y: 0, toJSON: () => ({}) })
it('caps the bubble width where the label would otherwise slab across the surface', () => {
render(
<Tooltip label="A description long enough to need a cap" side="bottom" maxWidth={360}>
<button type="button">anchor</button>
</Tooltip>,
)
fireEvent.mouseEnter(screen.getByText('anchor'))
// The stylesheet's half-viewport cap stays the default; this one overrides it.
expect(screen.getByRole('tooltip').style.maxWidth).toBe('360px')
})
it('clamps a bubble overflowing the right viewport edge back inside', () => {
const spy = vi.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue(rect(900, 1100))
try {