1- import { ActionIcon , Box , Button , Code , CopyButton , Group , Loader , Space , Text , Tooltip } from '@mantine/core'
1+ import {
2+ ActionIcon ,
3+ Anchor ,
4+ Box ,
5+ Button ,
6+ Code ,
7+ CopyButton ,
8+ Group ,
9+ Loader ,
10+ Space ,
11+ Stack ,
12+ Text ,
13+ Tooltip ,
14+ } from '@mantine/core'
215import { IconArrowBackUp , IconCopy } from '@tabler/icons-react'
3- import React , { type ReactNode } from 'react'
16+ import React , { useLayoutEffect , useRef , useState , type ReactNode } from 'react'
417import type { CatalogFlag } from './flagsRequests'
518import { useFlagsContext } from './flagsContext'
619import { validateOverrideValue } from './flagTypes'
@@ -29,7 +42,7 @@ export function FlagCatalogBody() {
2942 < Space h = "xs" />
3043 < FlagList
3144 flags = { bottomFlags }
32- borderColor = "var(--mantine-color-gray-2 )"
45+ borderColor = "var(--mantine-color-default-border )"
3346 // `bottomFlags` is the page minus overridden flags (those are pinned above). Only call it "no
3447 // match" when the server total is 0; otherwise this page's flags are all overridden.
3548 emptyMessage = {
@@ -57,7 +70,7 @@ export function OverridesSection() {
5770 Local overrides ({ overriddenFlags . length } )
5871 </ Text >
5972 < Space h = "xs" />
60- < FlagList flags = { overriddenFlags } borderColor = "var(--mantine-color-violet-2 )" />
73+ < FlagList flags = { overriddenFlags } borderColor = "var(--mantine-color-violet-outline )" />
6174 </ >
6275 )
6376}
@@ -113,20 +126,25 @@ function FlagRow({
113126 < Group
114127 justify = "space-between"
115128 wrap = "nowrap"
116- align = "center"
129+ // Top-align so the variant buttons stay up beside the name/key instead of drifting to the
130+ // vertical middle of a long description.
131+ align = "flex-start"
117132 px = "sm"
118- py = "xs "
133+ py = "sm "
119134 style = { {
120- borderBottom : '1px solid var(--mantine-color-gray-1)' ,
121- backgroundColor : overridden ? 'var(--mantine-color-violet-0)' : undefined ,
135+ borderBottom : '1px solid var(--mantine-color-default-border)' ,
136+ // Mantine's scheme-aware subtle tint (same one variant="light" uses): light violet in light
137+ // mode, a muted translucent violet in dark mode — not a full saturated fill.
138+ backgroundColor : overridden ? 'var(--mantine-color-violet-light)' : undefined ,
122139 } }
123140 >
124- < Box style = { { minWidth : 0 , flex : 1 } } >
141+ < Stack gap = { 6 } style = { { minWidth : 0 , flex : 1 } } >
125142 < Text size = "sm" fw = { 600 } truncate >
126143 { flag . name }
127144 </ Text >
128145 < FlagKey value = { flag . key } />
129- </ Box >
146+ { flag . description && < FlagDescription description = { flag . description } /> }
147+ </ Stack >
130148 < Group gap = "xs" wrap = "wrap" justify = "flex-end" style = { { flexShrink : 0 , maxWidth : '55%' } } >
131149 { overridden && (
132150 < Tooltip label = "Revert override" >
@@ -169,6 +187,56 @@ function FlagRow({
169187 )
170188}
171189
190+ // Free-text descriptions can run long. Show a single line by default with a "Show more" toggle that
191+ // expands the rest inline. The toggle only appears when the one-line clamp actually hides something —
192+ // measured rather than guessed from length, since a short description can still wrap and a long one
193+ // might fit.
194+ function FlagDescription ( { description } : { description : string } ) {
195+ const [ expanded , setExpanded ] = useState ( false )
196+ const [ overflowing , setOverflowing ] = useState ( false )
197+ const textRef = useRef < HTMLParagraphElement > ( null )
198+
199+ // Measure whether the collapsed description overflows one line, so we know to offer "Show more".
200+ // Skip while expanded — the clamp is off then, so a measurement would read as "fits" and wrongly
201+ // hide the toggle; `overflowing` keeps its collapsed value. The ResizeObserver re-measures when the
202+ // panel width changes, so narrowing the DevTools panel surfaces a newly-clamped description's toggle.
203+ useLayoutEffect ( ( ) => {
204+ const el = textRef . current
205+ if ( ! el || expanded ) {
206+ return
207+ }
208+ const measure = ( ) => setOverflowing ( el . scrollHeight > el . clientHeight )
209+ measure ( )
210+ const observer = new ResizeObserver ( measure )
211+ observer . observe ( el )
212+ return ( ) => observer . disconnect ( )
213+ } , [ description , expanded ] )
214+
215+ return (
216+ // Extra top margin gives the description a touch more separation from the key above it than the
217+ // name↔key gap, so the row reads as "title/key" then "description".
218+ < Box mt = { 4 } >
219+ < Text ref = { textRef } size = "xs" lineClamp = { expanded ? undefined : 1 } >
220+ { description }
221+ </ Text >
222+ { overflowing && (
223+ // Accent color in both states so it reads as the row's action. A hair smaller than the
224+ // description text and sitting right beneath it, so the two read as clearly distinct.
225+ < Anchor
226+ component = "button"
227+ type = "button"
228+ fz = { 10 }
229+ c = "violet"
230+ onClick = { ( ) => setExpanded ( ( value ) => ! value ) }
231+ style = { { display : 'inline-block' , marginTop : 0 } }
232+ >
233+ { expanded ? 'Show less' : 'Show more' }
234+ </ Anchor >
235+ ) }
236+ </ Box >
237+ )
238+ }
239+
172240function FlagKey ( { value } : { value : string } ) {
173241 return (
174242 < Group gap = { 4 } wrap = "nowrap" style = { { minWidth : 0 } } >
@@ -179,14 +247,24 @@ function FlagKey({ value }: { value: string }) {
179247 overflow : 'hidden' ,
180248 textOverflow : 'ellipsis' ,
181249 whiteSpace : 'nowrap' ,
250+ // Negate the chip's own horizontal padding so the key text lines up with the flag name above.
251+ paddingInline : 6 ,
252+ marginLeft : - 6 ,
182253 } }
183254 >
184255 { value }
185256 </ Code >
186257 < CopyButton value = { value } >
187258 { ( { copied, copy } ) => (
188259 < Tooltip label = { copied ? 'Copied' : 'Copy key' } withArrow >
189- < ActionIcon size = "xs" variant = "subtle" color = "gray" onClick = { copy } style = { { flexShrink : 0 } } >
260+ { /* Flip to violet on copy for a moment of feedback, then back to neutral grey. */ }
261+ < ActionIcon
262+ size = "xs"
263+ variant = "subtle"
264+ color = { copied ? 'violet' : 'gray' }
265+ onClick = { copy }
266+ style = { { flexShrink : 0 } }
267+ >
190268 < IconCopy size = { 12 } />
191269 </ ActionIcon >
192270 </ Tooltip >
0 commit comments