Filed by the objectui#8350 dev (branch claude/issue-8350-dedupe-emptiness-trims, PR #8375) while fixing that card. ⛔ Not claimed. Out of scope there: objectui#8350's fence is the dedupe ladder in record:details versus the page H1, and this is a different site with a different consumer.
What
packages/plugin-detail/src/DetailSection.tsx decides emptiness twice, both times with a raw value === null || value === undefined || value === '' test:
isEmptyValue (the row filter feeding emptyCount / the auto-hide heuristic / the reader's toggle);
- the
isEmpty branch inside displayValue (the one that draws the muted em-dash with the No value label).
Neither trims. @object-ui/core's recordDisplayValueAt — the definition getRecordDisplayName uses for the page H1, and now the definition record:details' dedupe ladder uses (objectui#8350 / PR #8375) — does. So a whitespace-only field value is EMPTY to both of those and FILLED to DetailSection, which is now the only reader in the record-detail render path still answering the old way.
Three consequences, all silent
- A blank row instead of the affordance. The em-dash +
No value span exists precisely so a reader can tell "this field has nothing" from "this page failed to render". A value of ' ' is FILLED, so it takes the cell-renderer path and paints a visually empty cell — the exact UI the em-dash was added to prevent, reached by a value the rest of the stack calls empty.
- It escapes the counter.
emptyCount skips it, so the toggle reads Show N empty fields with N one too low, and revealing them does not reveal this row (it was never hidden — it was never counted).
- It can arm auto-hide by itself.
shouldAutoHideEmpty requires filledCount > 0; the stated reason is that an all-empty section keeps its labels as a structural skeleton for a sparse or brand-new record. A section whose ONLY non-null value is ' ' has filledCount === 1, so the skeleton is suppressed and every genuinely empty row is hidden behind the toggle — on a page a reader would describe as blank.
Reachability is the same narrow authored-data shape as objectui#8350 (a field holding only spaces), which is why that card was p3; consequence 3 is the one that reaches furthest, because one such value changes what happens to every other row in the section.
Fix shape (a suggestion, not a ruling)
The same move objectui#8350 made one layer up: call the shared definition rather than re-spell the test. @object-ui/core now exports recordDisplayValueAt(record, field) for exactly this. ⚠️ Two things a taker should decide rather than assume:
- these two reads are about a RENDERED CELL, not about a title, so "empty" here may legitimately want to be a different predicate than the display-name one — that is the question to settle, and settling it is the value of this card;
- the two reads may not deserve the same answer as each other:
isEmptyValue feeds a layout heuristic, isEmpty feeds an affordance.
Whatever the ruling, the pin has to assert the RENDERED outcome — which row shows the em-dash, what N the toggle says, whether auto-hide fired — not the predicate in isolation.
Measured
Read on origin/main at 37149ec4f while implementing PR #8375. AUTO_HIDE_MIN_FIELDS is 4 on desktop / 3 on mobile and AUTO_HIDE_RATIO 0.25 / 0.2, so consequence 3 needs a section of at least 4 rows; PR #8375's fixtures deliberately stay at 3 rendered rows so auto-hide cannot fire and cannot confound that pin — which is how this divergence was noticed rather than assumed.
Dedup
search_issues twice, both non-empty (self-validating): the empty-section heuristic wording returned objectui#8350, objectui#7064 and objectui#7129 — the latter two are the hideEmpty DECLARED-KEY retirement, closed, and neither names the emptiness predicate; the whitespace-value wording returned objectui#7347, which is a grouping field NAME with surrounding whitespace in the kanban $select path, a different fact. None names this.
Related
objectui#8350 / PR #8375 (the same divergence one layer up, now closed there) · objectui#7129 (the heuristic's charter) · objectui#7064
Filed by the objectui#8350 dev (branch
claude/issue-8350-dedupe-emptiness-trims, PR #8375) while fixing that card. ⛔ Not claimed. Out of scope there: objectui#8350's fence is the dedupe ladder inrecord:detailsversus the page H1, and this is a different site with a different consumer.What
packages/plugin-detail/src/DetailSection.tsxdecides emptiness twice, both times with a rawvalue === null || value === undefined || value === ''test:isEmptyValue(the row filter feedingemptyCount/ the auto-hide heuristic / the reader's toggle);isEmptybranch insidedisplayValue(the one that draws the muted em-dash with theNo valuelabel).Neither trims.
@object-ui/core'srecordDisplayValueAt— the definitiongetRecordDisplayNameuses for the page H1, and now the definitionrecord:details' dedupe ladder uses (objectui#8350 / PR #8375) — does. So a whitespace-only field value is EMPTY to both of those and FILLED toDetailSection, which is now the only reader in the record-detail render path still answering the old way.Three consequences, all silent
No valuespan exists precisely so a reader can tell "this field has nothing" from "this page failed to render". A value of' 'is FILLED, so it takes the cell-renderer path and paints a visually empty cell — the exact UI the em-dash was added to prevent, reached by a value the rest of the stack calls empty.emptyCountskips it, so the toggle readsShow N empty fieldswith N one too low, and revealing them does not reveal this row (it was never hidden — it was never counted).shouldAutoHideEmptyrequiresfilledCount > 0; the stated reason is that an all-empty section keeps its labels as a structural skeleton for a sparse or brand-new record. A section whose ONLY non-null value is' 'hasfilledCount === 1, so the skeleton is suppressed and every genuinely empty row is hidden behind the toggle — on a page a reader would describe as blank.Reachability is the same narrow authored-data shape as objectui#8350 (a field holding only spaces), which is why that card was p3; consequence 3 is the one that reaches furthest, because one such value changes what happens to every other row in the section.
Fix shape (a suggestion, not a ruling)
The same move objectui#8350 made one layer up: call the shared definition rather than re-spell the test.⚠️ Two things a taker should decide rather than assume:
@object-ui/corenow exportsrecordDisplayValueAt(record, field)for exactly this.isEmptyValuefeeds a layout heuristic,isEmptyfeeds an affordance.Whatever the ruling, the pin has to assert the RENDERED outcome — which row shows the em-dash, what N the toggle says, whether auto-hide fired — not the predicate in isolation.
Measured
Read on
origin/mainat37149ec4fwhile implementing PR #8375.AUTO_HIDE_MIN_FIELDSis 4 on desktop / 3 on mobile andAUTO_HIDE_RATIO0.25 / 0.2, so consequence 3 needs a section of at least 4 rows; PR #8375's fixtures deliberately stay at 3 rendered rows so auto-hide cannot fire and cannot confound that pin — which is how this divergence was noticed rather than assumed.Dedup
search_issuestwice, both non-empty (self-validating): the empty-section heuristic wording returned objectui#8350, objectui#7064 and objectui#7129 — the latter two are thehideEmptyDECLARED-KEY retirement, closed, and neither names the emptiness predicate; the whitespace-value wording returned objectui#7347, which is a grouping field NAME with surrounding whitespace in the kanban$selectpath, a different fact. None names this.Related
objectui#8350 / PR #8375 (the same divergence one layer up, now closed there) · objectui#7129 (the heuristic's charter) · objectui#7064