fix(panel): take the tag out of the fixed-width row that holds the total #22
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/chip-unbounded-tag"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Reported: a longer tag could still push the total time text far to the right.
Correct, and the residual gap was already documented in
fmt.luau: "aproportional font has no cell, so
MMMMMMMMMMMMis twelve cells and still toowide". Clipping to a cell budget cannot be made right, so the tag left the row.
Why no budget can work here
The scope row is a fixed
CHROME_Wwhose last child is the total. Free space isgrow-only (
flex.cpp:532clamps at zero), arrangement never clamps (:737), anda node does not clip its children (
node.h:211). Every child is bounded by textthis plugin or its translator writes — except a tag, which is whatever the user
typed at a shell.
Fmt.colscharged Wide/Fullwidth two and everything else one. That fixed CJK andHangul and left the general case: twelve narrow characters fit where twelve wide
ones did not. Nothing at this plugin API measures text, so the 12 was calibrated
against
Mon 31/08+All timein DejaVu Sans at one chrome. A calibration isnot a bound.
Why the chip could not be capped in place
ui.buttontakes neithermaxWidthnormaxLines(
ui_tree_reconciler.cpp:471-475). A barewidthpins min and max and armssetEllipsize(button.cpp:707) butButtonnever callssetMaxLines, soeffectiveMaxLinesis 0 (label.cpp:503) and Pango gets a 500-line budgetunder
PANGO_WRAP_WORD_CHAR. A width-capped button wraps.ui.labeldoes elide —maxWidth+maxLines >= 1reachespango_layout_set_ellipsize(END)and the reported width is that budget(
label.cpp:487-497,:630) — but a label takes noonClick.kFlexhasminWidthand nomaxWidth, so a tinted row hugging its textcannot be capped. The only bounded-and-tinted shape is
flexGrow, measured atits whole assigned share, so it would span the row instead of hugging.
What changed
the total at
DUR_W— nothing a tag can reach.maxWidth = RIGHT_Wand
maxLines = 1: a real renderer ellipsis, any script, any length. It wasunbounded before and could overhang the panel edge.
scope buttons, where a lone glyph read as a third scope. Fixed square at
SEARCH_H, matching the field; tooltip names the whole tag, untruncated.flexGrow = 1instead ofCHROME_W - 20, so the layoutsubtracts the button's 28 px rather than a constant that would have to track it.
Fmt.clip,Fmt.cols, the Wide-range table andCHIP_TAG_MAXare deleted —86 lines out of
fmt.luau. Net −51 lines.clipTagstays: it is a 120-byte cap for a hard API limit (a label over 512bytes raises and takes the handler down), not a layout guess.
Verification
Suite:
13 states, 1954 field comparisons/ok. Lint:0 errors, 0 warnings.The old textual assertions are replaced by a geometric one: state 5b renders five
tags —
a,Coding Test Practice,MMMMMMMMMMMM, a CJK tag, and 400Ws — andasserts the scope row's shape is identical across all of them, so a tag
reaching that row in any spelling fails there.
25 mutations, no survivors: putting the tag back on the button, unsquaring it,
drifting its height from the field, giving the field a width, dropping either
flexGrow, showing the button unconditionally or never, emptying the tooltip,making clear not clear, dropping the caption's
maxWidth/maxLines, wrappinginstead of eliding, dropping
DUR_W, and stopping the spacer growing.On screen, with a validated method (the panel is translucent over a live
wallpaper, so raw pixel compare is invalid — masks were checked stable across
repeat captures of an unchanged tag first): the scope row's ink is byte-identical
across
MMMMMMMMMMMM,Coding Test Practice, a CJK tag, a Hangul tag, 24@sand 400
Ws, and the total's own region is identical in every case. The onlypixels that move in that row are 18 antialiasing pixels inside
All time, whichappear when the caption loads a CJK fallback font and bumps the renderer's
text-metrics generation.
Also verified live with real pointer input: the tooltip reads
Clear tag filter: Coding Test Practice, clicking the✕clears the filter and the field growsback into its space, and the caption elides a 400-character tag with a real
…at the column edge instead of overhanging.
Review: the test half (
test.luau)Scope:
test.luauonly. The C++ citations areShellRev22's.Method: ran the suite (
13 states, 1954 field comparisons/ok, reproduced), then a69-mutation battery against
timew/panel.luauin a/tmpcopy of this branch.34 survived. Five of those are shell-inert (they set a prop the reconciler does not
carry for that node kind, so the shell logs a skip and nothing moves) and one is
by design, leaving 28 survivors that change real, shell-visible behaviour with the
suite green. Four of them are the reported bug or its direct analogue.
Three remedies below are not suggestions — I applied each, confirmed it stays green on
this branch, and counted what it kills.
blocking —
headerShape()does not recurse, and the reported bug fits in the gapheaderShape(:2518) walksipairs(scopeKids())and reads eight props off each directchild. It never descends. Replace the spacer with a grow-row wrapping an unbounded tag
label and nothing in the file notices:
Suite:
13 states, 1954 field comparisons/ok. The tree under the 400-Wfixture:That is a 400-character user string, unbounded, inside the fixed-
CHROME_Wrow, ahead ofthe total at
DUR_W— the exact geometry the PR body says is now unreachable. Everystructural pin passes:
#scopeKids() == 4✔,scopeKids()[3].props.flexGrow == 1✔,scopeKids()[4].props.width == 56✔, and the shape string is identical across all fivetags because the
kind/prop change is the same for all five.ui.rowandui.labelneed no exotic props for this. Same blindness, bounded variant(
maxWidth = 40on the nested label) also survives.Remedy, verified. Swap the eight-prop concat for a recursive one that sorts every
non-function prop of every descendant. Green on this branch; kills 15 of my survivors,
including this one:
It is also strictly cheaper than the current version: no prop list to keep in step with
the API. The eight-prop list is already short of
maxWidth,minWidth,fontWeight,color,tooltip,textAlign,paddingH,controlSizeandkey, and four of thoseare honoured on the nodes this row actually builds —
s06(maxWidth = DUR_W + #m.filteron the total;
maxWidthis akLabelprop and the PR's own citation says it is what thelabel reports as its width, so at minimum the
width == 56pin stops describing therendered width),
s08(fontWeightswitched on tag length),s25(colorswitched ontag length) and
s19(tooltip = m.filteron a scope button) all survive today and alldie under the recursive version.
blocking — the row that now carries the tag has no shape guard at all
setFiltermirrors the tag intostate.search(panel.luau:639), andbuildSearchputs that into the field's
value. So after this PR the user's unbounded string lives inthe search row — also a fixed
CHROME_Wrow, also with a content-sized button as itslast child. The suite gained
headerShape()for the scope row and nothing for this one.Unconditional, no length trick:
Survives. Under the 400-
Wfixture that is two gaps of 406 px inside a 287 px row.r19(glyphSize = 12 + #m.filter),r21/r22(controlSizeswitched on tag length)and
r23(magnifiersize = 14 + #m.filter) survive the same way.The five per-tag asserts inside the loop (:2560-2577) are all relational —
shape == first,width == height,tooltip == tr(...),flexGrow == 1— so anytag-derived value that preserves the relation is invisible by construction.
Remedy, verified. A
searchShape()companion overrightColumn().children[4],skipping the two props whose job is to carry the tag (both already asserted whole
elsewhere), compared across the loop exactly like
headerShape:Green on this branch; kills
r19,r21,r22,r23,r25and all three of thelength-conditional survivors below.
blocking —
check()runs for one tag in five, so every absolute assertion only ever sees 20 characterscheck()sits insideif tag == LONG_TAG(:2545).compare()therefore runs against a20-character tag and nothing else. Every absolute claim about the new arrangement —
chip.props.text == nil,chip.props.glyph == "x",chip.props.height == search.props.height,search.props.width == nil,#header == 4,#searchRow— is only ever tested in that one regime. The 400-Wfixture existsprecisely to cover the other one, and never reaches
compare(). Three survivors:text = if #m.filter > 100 then m.filter else nilon the clear buttonwidthpinned at 26 — the PR's own "width-capped button wraps", visibly broken.chip.props.text == nilnever sees it.width = if #m.filter > 100 then 100 else nilon the fieldsearch.props.width == nilnever sees it; the loop only checksflexGrow.width/height = if #m.filter > 100 then 200 else SEARCH_Hon the clear buttoncompare().Remedy, verified. A two-line reorder — hoist
check()above theifand name thestate after the tag. Suite goes to
17 states, 2358 field comparisons/ok, and allthree die immediately:
blocking — the caption's elide budget is asserted as a type, not a value
:2157 is
same(type(right.children[1].props.maxWidth), "number", ...). Bounding thatcaption is the PR's answer to where the tag lives, and the number that bounds it is
unpinned. Four survivors:
maxWidth = RIGHT_W * 4,maxWidth = 100000,maxWidth = 1,and the one that matters:
Tag-derived, and by the PR's own reading of
label.cpp:487-497/:630the reported widthis that budget — so this restores the unbounded overhang the caption change exists to
prevent, silently. Note the deleted 5b block pinned
CHROME_W 287,gap 4,DUR_W 56and
fontSize 11as absolute numbers off the tree; the replacement kept onlyDUR_W.Remedy, verified. One line, relational so it needs no export, green on this branch,
kills all four:
(
right.children[6]is the scroll, whose width is already pinned toRIGHT_Wat :1861.)advisory —
shape == firstis not a tautology, but it is close, and it can never be moreInverting the assert on an unmutated branch prints the answer:
Byte-identical, and carrying no tag material at all — it is
constant == constant. Itstill earns its keep: across 69 mutations it is the sole killer of exactly two
(
height = CHIP_H + #m.filteron a scope button,width = #m.filter * 8on the spacer);both survive when I stub the assert to
true. So: a narrow guard, not a vacuous one.What it can never catch is the tag-independent geometry error, because a cross-tag
comparison is structurally blind to anything constant. Even with the recursive fix
applied, these still survive:
textAlign = "start"on the total (right edge no longerflush inside its 56 px),
gap = 4 -> 12on the scope row (the deleted block pinned thisone),
flexGrow = 1added to the total (it eats the slack and the spacer stops mattering),justify = "end"on the row,maxWidth = 40on the total (which makes thewidth == 56pin a statement about a prop rather than about a rendered width). Those want absolute
pins of the kind the old block had. The shape loop is a good addition; it is not a
replacement for them, and the PR's
-51 linesis partly paid for out of that.advisory — the state census is honest; one figure in a comment is not
Confirmed by running both revisions:
mainprints13 states, 1870 field comparisons,this branch
13 states, 1954. The old 5b had exactly onecheck()(itsWIDE_TAGre-open had none), so 13 → 13 dropped nothing. Paint counts are honest too — doubling
setFilter("")inside the clear handler dies at :2858 withclearing the filter painted 2 times, not 1, andonClick = "clearFilter"dies at :2176.But
test.luau:4474still reads "That is what "1870 field comparisons" does notcover". That comment's whole job is to make the printed figure mean something, and this
PR moved the figure to 1954 without moving the comment.
advisory — positional indices are pinned; the diagnostic on one path is not
7 for 7. Appending after the clear button, appending after the total, prepending to the
search row, moving the button to index 1, removing the field, swapping the scope buttons,
swapping spacer and total — all die, at :2171, :2169, :1815, :1815, :1815, :2195, :2198.
#header == 4at :2169 and the#searchRow == 3gate at :2168 both bite. Good.One nit: when the button is missing entirely,
chipis nil and :2174 dies withattempt to index nil with 'props'. Asame(#searchRow, 3, ...)at the top of theelsebranchwould name the cause instead.
advisory —
search.props.width == nilis a real assertionNot vacuous. Removing the field from the row dies at :1815 on the
placeholderassert,so
searchcannot silently resolve to another node; andflexGrow == 1on that same node(:2192) fails if the builder emits neither prop. It holds as written.
advisory — the tag's residency in the field is untested
patch.search = patch.filter(panel.luau:639, pre-existing, not in this diff) is whatputs the tag in the field, and the PR names the field as one of the two honest homes for
it. Both
patch.search = state.search(no mirror) andpatch.search = string.sub(patch.filter, 1, 12)(mirror re-clipped to the budget this PRdeletes) survive the suite. The second one is the deleted bug wearing a different hat.
Also surviving: adding
apply({ searchKey = state.searchKey + 1 })to the clear handler,which remounts the field on every clear — the identity section samples fixed states only.
advisory — for the integrator, the five survivors not worth chasing
tooltip/paddingHon a label,minWidthon a spacer or a button, and children on aspacer are all outside that kind's
knownProps(or, for the spacer, outsidechildContainer), so the shell skips them loudly and geometry does not move. Theysurvived my battery but they are not holes.
advisory — the deletion side is clean
The 39 lines of
Fmt.clip/Fmt.colstests go with the functions;Fmt.colshas noremaining caller in the suite,
panel.CHIP_TAG_MAXno remaining reader, andpanel.filter_chipis gone from theENtable alongside the key. No orphans.Verdict: REQUEST_CHANGES
The PR's own summary claims "25 mutations, no survivors". Sixty-nine aimed at the same
code produce twenty-eight, and the first of them puts a 400-character user string back
inside the fixed-width row that holds the total — the reported bug — with the suite
green. The load-bearing claim is "a tag reaching that row in any spelling fails there",
and
headerShapeonly checks one spelling: eight named props on a direct child. Thestructural change looks right; what defends it does not yet.
Four fixes, all verified green on this branch: recursive
shapeOf, asearchShape()companion, hoisting
check()out of theif(17 states / 2358 comparisons), and pinningthe caption's
maxWidthto the scroll's width. Together they kill 19 of the 28. Theother nine are tag-independent geometry (
textAlign,gap,justify, a constantmaxWidth,flexGrowon the total), the lost tint, the twosetFilter-mirror ones andthe
searchKeyremount — those want a couple of absolute pins put back, and the mirrorasserted at all.
Shell-behaviour review: are the citations true, and was there a cheaper fix?
Scope: every
flex.cpp/node.h/ui_tree_reconciler.cpp/button.cpp/label.*/cairo_text_renderer.cpp/input.cppcitation in the PR body and in the new commentsabove
DOT_W, inbuildSearch,buildScopeandbuildTrend, checked line by lineagainst
/nix/store/7pgw3p2dhkn8sfpnli7qiyzs9zyhr01d-source, plus a live render of boththe shipped panel and of the shape the PR says cannot exist.
30 cited claims checked. 27 are exactly right. 3 are wrong. The shipped code is
correct — I measured it — and every wrong claim is a justification, not a behaviour. In
a file where the comments are the design record, that still needs fixing before merge.
Method note: the probe ran as a throwaway plugin from
/tmpunder its own source(
prreview/probe, since removed), so nothing here touched this checkout or hot-reloadedthe live widget. Pixel positions are from
grimcaptures measured with a threshold scan,not eyeballed.
blocking — "a tinted container that hugs its text cannot be capped" is false, and so is "no cheaper in-place bound existed"
panel.luaunote aboveDOT_W:Both halves of the premise are true —
kFlexreally hasminWidthand nomaxWidth(
ui_tree_reconciler.cpp:449-453;Flex::setMaxWidthexists atflex.cpp:301but onlythe
widthprop reaches it,:1108-1110), and aflexGrowchild really is measured atits whole assigned share (
flex.cpp:538→:487-490→:36-42setExactWidth, withlabel.cpp:567/:630). The conclusion does not follow, because the cap does not haveto be on the container. Put it on the inner label and the container inherits the bound:
Every link is in the source you already cite:
kFlexcarriesfill,radius,paddingHandonClick(ui_tree_reconciler.cpp:449-453).A row with
onClickis wrapped inClickWrap, whosedoMeasureforwards the child'smeasurement verbatim and takes the child's size as its own (
:383-390,:403-411), sothe wrapper costs no geometry.
widthand nominWidth, the flex hugs: `contentMain = padStart + childrenMain(flex.cpp:662-672`).totalGrowis 0 soflex.cpp:510callsmeasureItem(item, false, 0.0F)— thereforehasAssignedWidthisfalse at
label.cpp:567andfinalWidth = max(measuredWidth, m_minWidth)(:630),where
measuredWidth = min(metrics.width, measureMaxWidth)(label.cpp:565— thisis the line that actually does the capping, and it is the one line of the chain the PR
does not cite).
maxWidth > 0withmaxLines = 1giveslineBudget = 1andpango_layout_set_ellipsize(END)(cairo_text_renderer.cpp:447-451).Measured, not argued. Three of these chips inside a fixed
width = 287row whose lastchild is a
width = 56, textAlign = "end"total, with a 3 px reference rule at exactly287:WWWWWWWWW… xCoding Test PracticeCoding Test Pra… x日本語のタグはとても長い(12 cp)日本語のタグは… xIdentical, 3 px inside the row edge, in every spelling — a bound, not a calibration, and
it needs no
Fmt.cols, no East Asian Width table and no per-font measurement, becausePango does the measuring. So the tag could have stayed in the scope row.
This matters twice over:
left the row" — is only half true. Clipping to a cell budget cannot be made right;
that part is correct and well argued. But the conclusion drawn from it ("so the tag
left the row") rests on the impossibility claim above, and that is false.
panel.luau:1518-1521says, in the list rows, that"a clickable row wrapper, which is available (:371, :449)" — the same
:449the newnote cites for the absence of a bound. Two comments in one file now disagree.
What makes the shipped fix defensible is the reason the PR deleted. The old
buildScopecomment said a wrapper's radius "could only ever disagree with the hover rectangle drawn
inside it" — and there is a real cost in that neighbourhood:
Button::setRadius(Style::scaledRadiusMd())(
button.cpp:187) isradiusMd × 1.0 × g_cornerRadiusScale(style.cpp:64,:68), i.e. ittracks the user's corner-radius scale and not
m_scale, while a flexradiusisv * m_scaleand nothing else (ui_tree_reconciler.cpp:942). Aradius = 6chip besidethe two scope buttons therefore matches them only when
m_scale == cornerRadiusScale,which is to say at defaults. That is a genuine, nameable, cosmetic reason to prefer moving
the tag out. Please put that argument back and drop the geometric one, or state plainly
that the row-chip is available and the tag left the row by choice.
blocking — "It was unbounded before and could overhang the panel edge" is false; it wrapped
PR body and
panel.luau:1179-1182:ui.columndefaults toalign = "stretch"(ui_tree_reconciler.cpp:646— the very linethis file cites at
panel.luau:1591), so on the arrange pass the column hands each childan exact width (
flex.cpp:493-494→:44-50), andsetExactWidthsetshasMaxWidth(
node.cpp:59-62→:49-52).Label::measureWithConstraintsthen takesmeasureMaxWidth = constraints.maxWidthatlabel.cpp:496-498even with nomaxWidthprop. With
maxLinesunset that is themaxWidthPxScaled > 0,maxLines == 0case atcairo_text_renderer.cpp:437/:447→lineBudget = kHardMaxLines(500) underPANGO_WRAP_WORD_CHAR. The old caption wrapped.Measured: a
fontSize = 13, fontWeight = "bold"label with a 60-Wtag and nomaxWidth/
maxLines, in awidth = 287column, rendered 4 wrapped lines, ink right edgex = 966 against a column edge at x = 975. Inside it, not over it. With the PR's
maxWidth+maxLines = 1the same string is one line ending in a real….So the change is an improvement, but it fixes a wrap that grows the column downward and
pushes the chart and the whole history block with it — not an overhang. The sentence
"Nothing sits to its right for it to push" reads as if nothing was being pushed; in fact
everything below was. Reword; the mechanism you describe is not the one in the renderer.
advisory —
panel.luau:1198states the opposite of:1521, and it is the wrong onekFlexincludesonClick(ui_tree_reconciler.cpp:453) and bothrowandcolumnroutethrough
wrapClickablewhen it is present (:647-648,:656-657). Pre-existing, notintroduced here — but it is the claim the new note above
DOT_Winherits, and it is whythat note reached a false conclusion. Worth correcting in the same pass.
advisory — a label can report a width wider than its
maxWidth; the qualifier is missinglabel.cpp:626-628: on theisIconGlyphbranchfinalWidth = max({inkWidth, measuredWidth, m_minWidth}), andinkWidth(:577) is not capped bymeasureMaxWidth. Likewise them_autoScrollbranches (:613-625,:642-654) forcemeasureMaxWidth = 0at:501before re-capping the box. Neither is reachable for this caption —
isIconGlyphneedsStringUtils::isSinglePrivateUseGlyphover the whole string (:576) and the captionalways carries the
panel.trend_tagprefix;autoScrollhas nokLabelprop(
:457-460) — so the shipped claim holds. But "the width it measures is that budget" isstated flatly, and it is true only on the
:630non-icon, non-scroll, non-assigned branch.One clause fixes it.
advisory — the new
flexGrowfield collapses to 0, not tokMinWidth, at the endpointflex.cpp:532clampsremainingat zero, so if the search row's fixed children ever sumpast
CHROME_Wthe grower's share is 0, andInput::doMeasuretakesassignW = max(constraints.maxWidth, minFromHint)=max(0, 0)= 0(
input.cpp:1859-1860) —kMinWidth = 48(:45,:1858) is only the unconstrainedfallback and never binds here, because
widthis gone som_minLayoutWidthis 0(
ui_tree_reconciler.cpp:1611-1612). Not reachable today: glyph 14 + button 26 + two 6 pxgaps = 52 against 287, and all four scale by the same
m_scaleasCHROME_W, so the ratiois scale-invariant. But "the field yields the space instead of the row overflowing"
understates the endpoint — the field yields all of it and vanishes. A
minWidthon thefield would be the honest guard; a sentence naming the endpoint is the cheap one.
advisory —
flex.cpp:737is the weaker citation for "arrangement never clamps":737iscursor += item.main;. The claim is about code that isn't there, which a linenumber can't show.
flex.cpp:690-699is the citation that carries it:arrangedChildrenMainis summed from the children's own measured mains and
effectiveGap/cursorare derivedfrom it through
std::maxguards that only ever widen.Verified correct (and how)
Every one of these is exactly what the source says:
flex.cpp:532—const float remaining = std::max(0.0F, containerMain - fixedMain);✔node.h:211—bool m_clipChildren = false;✔ (and nokFlex/kBoxprop reaches it)ui_tree_reconciler.cpp:471-475—kButtonhas neithermaxWidthnormaxLines✔;:457-460—kLabelhas both ✔:1404-1407— a barewidthon a button callssetMinWidthandsetMaxWidth✔button.cpp:707—applyLabelMaxWidtharmssetEllipsize(End)✔ (:698-711), andButtonnever callssetMaxLinesanywhere ✔ (only external callers do, e.g.home_tab.cpp:612)label.h:143—int m_userMaxLines = 0;✔ (exact line)label.cpp:503—effectiveMaxLines = m_autoScroll ? 1 : m_userMaxLines✔cairo_text_renderer.cpp:436-451—kHardMaxLines = 500,lineBudget = maxLines > 0 ? min(maxLines, 500) : 500,PANGO_WRAP_WORD_CHAR✔width = 120, height = 22, fontSize = 11ghost button with"Filter: " .. 30×"W"renders
Filter:on a line above the 22 px box and furtherWlines above and belowit, painted outside the button's bounds with nothing clipping them. Exactly "visibly
broken rather than merely unspecified". Good call, and good instinct to check it.
cairo_text_renderer.cpp:448-451—maxWidth+maxLines >= 1reachespango_layout_set_ellipsize(PANGO_ELLIPSIZE_END)✔; and live, a 400-char tag elides atx = 1226 against a
RIGHT_W = 305column ending at x = 1228 ✔flex.cpp:487-501+:523-538— growers measured with an exact main constraint at theirshare ✔;
label.cpp:630hasAssignedWidthforces it ✔button.cpp:628before:637—selectedwins ✔. Stronger than stated: even if theGhost palette had no
selectedentry,:636'sisHovered || isSelectedfires onisSelectedalone, so there is no hover delta either way. (isPressedat:624stilloutranks it, so the button does keep press feedback.)
Style::scaledRadiusMd()isradiusMd × localScale × g_cornerRadiusScale(
style.cpp:64,:68),Buttoncalls it with the defaultlocalScale = 1.0(
button.cpp:187), and the reconciler'sscaled()isv * m_scalewith no cornerscale (
ui_tree_reconciler.cpp:942) ✔glyph = "x"resolves ✔ —assets/fonts/tabler.jsonhasx→U+EB55;closeis onlyan alias to
x(glyph_registry.cpp:27), so"x"is the native name and hits:261-264, not thekMissingGlyphskull at:266-267. Confirmed on screen: a real ✕.glyphSizeis honoured for a button ✔ —ui_tree_reconciler.cpp:1322-1323→Button::setGlyphSize(button.cpp:292-294)kInputacceptsflexGrow✔ (:479-483), and an Input under an exact main constrainttakes
assignW = max(constraints.maxWidth, minFromHint)(input.cpp:1859-1860) — widthis constraint-driven and never text-driven, so no placeholder or value can overflow it.
Confirmed live with a 400-character value: field width unchanged, text clipped to the
viewport.
widthstillsets the flex's
minWidth(:1109), so the row reportsCHROME_WfromconstrainFlexWidth(flex.cpp:679→:410) even on a pass where main is unknown, andarrange re-enters with an exact width (
flex.cpp:751-757) before the share is computed.x 1187..1212(w = 26) andy 215..240(h = 26), and the search field's frame bordersits at
y = 215andy = 240too. Same 26 px, pixel-aligned top and bottom, in boththe short-tag and 400-char captures.
x = 1210withCoding Test Practiceandx = 1210with 400Ws, clear button bbox byte-identicalacross the two.
Fmt.clip,Fmt.cols,CHIP_TAG_MAXandpanel.filter_chiphave noremaining references anywhere in
timew/,panel.clear_filtertakes{tag}anden.jsonis the only locale.noctalia plugins lint timew→0 errors, 0 warnings.Verdict
REQUEST_CHANGES, on the comments only. The geometry ships correct and I have the pixels
to say so: the scope row's shape is tag-independent, the caption elides at the column edge,
the clear button is a true square matched to the field, and the field grows and yields
without collapsing. Nothing here asks you to change a line of Lua that runs.
What needs to change is that the PR argues impossibility where the answer is availability.
A bounded, tinted, hugging, clickable chip exists at this plugin API — cap the inner
ui.label, not the flex — and I rendered one holding 400Ws inside a fixed row withoutmoving the total by a pixel. The reason to prefer what you shipped is the radius mismatch
you deleted (
button.cpp:187vsui_tree_reconciler.cpp:942), not a geometric wall. Andthe caption's old failure was a wrap that pushed the chart down, not an overhang past the
edge. Fix those two claims and the
:1198line they lean on, and this is a clean merge.887481640bto3a8924a1dcBoth reviews actioned. Amended to
3a8924a. Suite went13 states, 1954→17 states, 2358 field comparisons, lint0 errors, 0 warnings.ShellRev22's blocker is correct and it invalidates my stated reason
I claimed a bounded chip carrying the tag was impossible here. It is not. I
checked
kFlexformaxWidth, found none, and concluded the container could notbe capped — never considering that the cap belongs on the inner label, whose
capped measurement the hugging row then adopts. Verified independently before
rewriting:
label.cpp:565—measuredWidth = measureMaxWidth > 0 ? min(metrics.width, measureMaxWidth) : metrics.width. The line that does the capping, and I never cited it.flex.cpp:510—measureItem(item, false, 0.0F): a non-growing child gets no main-axis constraint on the measure pass, sohasAssignedWidthis false and the row hugs the capped label instead of spanning.So
ui.row({ fill, radius, onClick }, { ui.label({ maxWidth, maxLines = 1 }) })is a tinted, hugging, clickable, bounded chip. The reviewer's probe-plugin
measurement (total ink at an identical x for 400
Ws, the reported tag, and aCJK tag) settles it.
The design does not change — the clear control belongs beside the field holding
the tag, and a flex-built chip's
radiusskipsg_cornerRadiusScale(
ui_tree_reconciler.cpp:1081-1085via:942againstbutton.cpp:187/style.cpp:64), so it matches sibling buttons only at the default setting. Butthe justification did change, in
panel.luau, the README and above: from afalse impossibility to a design choice with a named cost.
Second blocker, also correct: the caption wrapped, it did not overhang.
ui.columnsetsFlexAlign::Stretch(:646), so the child got an exact width,took it as its wrap budget (
label.cpp:496-498), and withmaxLinesunset landedin the 500-line
PANGO_WRAP_WORD_CHARbranch — four lines for a 60-character tag,shoving the chart down.
maxLines = 1is the fix; my reason for it was wrong.Also fixed: the pre-existing
panel.luauclaim that "only box, image and buttonaccept onClick" (
kFlexhas it at:453; onlyui.labelgenuinely lacks one) —the same mistake that led the new note astray. And the citations:
label.cpp:565added,
flex.cpp:737→:690-699for "arrangement never clamps".SuiteRev22's blocker is mine and it was a real hole
headerShape()did not recurse, so replacing the spacer withui.row({ flexGrow = 1 }, { ui.label({ text = m.filter }) })put 400 unboundedcharacters back in the fixed row ahead of the total while the suite printed
ok.Replaced with a recursive walk over every descendant's non-function props —
shorter than the 8-prop list it replaces and nothing to maintain. Both the
nested mutant and a raw-filter variant now die.
Actioned in full:
shapeOf, kills the nested-label classsearchShape()companion, skipping onlyvalue/tooltip/key— the three props that legitimately carry the tagcheck()only ran forLONG_TAGcompare()(hence 17 states)type(...) == "number"Re-ran the reviewer's survivors myself:
s09,s09b,r19,t05,r03,r08all die. Two still live and I am accepting both as outside this PR's claim, not
fixing them:
tooltip = labelon a scope button andmaxWidth = 56on the totalare tag-independent — they change the row for every tag equally, so no
cross-tag comparison can see them, which is the documented limit of this
technique rather than a gap in it.
Not actioned, with reasons
clearisnil-indexed at:2174with a bad message. The absent-button case is already caught two lines up bysame(chip, nil, ...), so the crash is unreachable from a real regression; adding a guard would only make a dead branch prettier.Input"yields ALL the space" advisory. Correct and unreachable: 52 px of fixed children against 287, scale-invariant. Recorded, not defended.