You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
object-metric: a money KPI's value is cut with an ellipsis (1,290,00… / 2,017,50…) at 1024 and 1440 px inside a page:card — viewport-based spans give a narrowed column's tile too little width #12942
Filing gate ①: a reproduced, user-visible defect. reach: a public door, the console page /_console/apps/app.objectstack.hotcrm/page/sales_home_page on @objectstack/*18.0.0-next.3 (console objectui 5e15928b4631). It was measured by the objectstack-ai/hotcrm#2125 dev (os-dev report on that card, session_01BDgipbNowaGa139em8Fhoq), and the maintainer had flagged the page as ugly (「这个页面这么丑」). Filed by the hotcrm repo:hotcrm seat (os-project-manager, same session). ⛔ Not a claim. Grade, domain:* and routing are triage's.
Reader who acts: the objectui triage / domain:ui seat.
What happens
On Sales Home, the Key Performance Indicators card holds four object-metric tiles inside a page:card. The money values are cut with an ellipsis, so the tile hides the figure it exists to show:
Revenue (Won):1,290,00… at 1024 px, 1,290,0… at 1440 px.
Pipeline Value:2,017,50… at 1024 px, 2,017,5… at 1440 px.
The value cell is font-bold … whitespace-nowrap text-ellipsis tabular-nums text-xl @[160px]:text-2xl. Its scrollWidth / clientWidth reads 126 / 106 at 1024 px and 152 / 124 at 1440 px.
Layout context, read on the same build:
page:card lays its children in a 12-column grid with spans by type (object-metric: col-span-12 md:col-span-6 xl:col-span-3). So the tiles sit one row at 1440 (about 174 px each) and 2 × 2 at 1024 (about 156 px each), in a main column the page's aside narrows to 376 px.
The breakpoints are viewport-based, so a tile in a narrowed column takes a span too small for its value.
What done looks like
A metric tile never truncates its primary value. Either the value scales down (container query) before it would ellipsize, or the tile's span follows its container rather than the viewport. A rendered test pins a seven-digit money value at the narrowest span.
Dedupe
MCP search_issues on objectui, "object-metric value truncated ellipsis KPI tile number cut off tabular-nums": 9 hits, all closed, none about the value truncating. The nearest are #12623 (the title truncating), #12704 (tile height) and #11254 (number format).
packages/plugin-dashboard/src/metricTile.ts:32METRIC_TILE_VALUE_CLASS still clips the value with overflow-hidden text-ellipsis whitespace-nowrap at text-xl / @[160px]:text-2xl.
packages/components/src/renderers/layout/page-block-span.ts:37 maps object-metric to the viewport-based TILE span.
Grade p2. A KPI tile hides the figure it exists to show. It was measured on 18.0.0-next.3 on HotCRM's Sales Home, a page the maintainer called ugly. The value is still in the DOM, so it holds no release.
Route: the value scales before it would clip (a container-query step below text-xl), so ⛔ a money value is never ellipsized.
Making the tile's span follow its container instead of the viewport changes every page's grid, so it is not taken here. If the dev finds scaling alone cannot fit a seven-digit value at the narrowest span, they report it before widening the scope.
Scope = the card's Done when: a rendered test pins a seven-digit money value at the narrowest span, with before and after screenshots at 1024 px and 1440 px.
Claim: PM loop round 5 (priority:p2)
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12942-metric-value-scale
Worktree: objectui-issue-12942
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/plugin-dashboard/src/metricTile.ts (METRIC_TILE_VALUE_CLASS and its container-query steps) and the tile renderer(s) that read it under packages/plugin-dashboard/src/, with tests beside them. Plus one .changeset/12942-*.md for @object-ui/plugin-dashboard. ⛔ Not packages/components/src/renderers/layout/page-block-span.ts (triage ruled the viewport span out of scope). Stop on breach; explain in the report.
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
Clause-②: no
Responsibility: @object-ui/plugin-dashboard's METRIC_TILE_VALUE_CLASS clips the value with text-ellipsis at text-xl / @[160px]:text-2xl | none: no platform path scales a metric value today | every viewer of an object-metric tile in a narrowed column; HotCRM's Sales Home (objectstack-ai/hotcrm#2125) today.
Thread-read: 6247342683
Serial constraints cleared: no open objectui PR touches packages/plugin-dashboard/. The in-flight claims (objectui#12943, #12946: @objectstack/spec ranges; #12724, #12725: chatbot and Studio surfaces) are disjoint.
Claimed at: 2026-10-18T08:07Z
os-dev-report
{
"issue": 12942,
"status": "done",
"branch": "claude/issue-12942-metric-value-scale",
"pr": "#12950",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB (parent's; subagent run)",
"premise_still_valid": true,
"summary": "metricTile.ts now has a value size ladder (METRIC_TILE_VALUE_LADDER plus metricTileValueClass, metricTileValueEm and metricTileValueTitle). The plain tiles (MetricWidget, so object-metric too, and MetricCard) pick a rung by how wide the value draws. A short value (4.5 em or less) keeps today's text-xl @[160px]:text-2xl face. A longer one starts at 14 px and steps up at container widths ceil(48 + maxEmpx), where 48 px is the Card's value inset. Past the last rung it stays 14 px, and the value carries its full figure as a title tooltip. Browser reading on Chromium with Inter: 1,290,000.00 went from 126/106 clipped (20 px) to 106/106 (14 px) at a 156 px tile, and from 152/124 clipped (24 px) to 124/124 (18 px) at a 174 px tile. The before figures match the card exactly. The span is unchanged and DatasetWidget is untouched (no Card inset; noted in Acceptance notes).",
"tests": "On 0f9795d. New metricTileValueScale-12942.test.tsx: 8 passed. It pins (a) object-metric with fallbackValue 1290000 and format 0,0.00 on the scaled rung (14 px at a 154 px container, 18 px at 172), not @[160px]:text-2xl, with the title set; and (b) CONTROL: 4,210 keeps METRIC_TILE_VALUE_CLASS with no title. It also re-derives every ladder step from the formula. Reverse check on the committed tree (MetricWidget reverted to METRIC_TILE_VALUE_CLASS; injected=1, removed_left=0 by grep -c): 2 failed / 6 passed with the control green. Restored with git checkout HEAD --; git diff HEAD = 0 bytes; blob hash 897bbcf2 matches HEAD. Targeted vitest: 36 plugin-dashboard files naming METRIC_TILE_VALUE_CLASS, metricTile, MetricWidget, MetricCard or object-metric, 36 files / 378 tests passed (lock VERDICT command-exit 0). Closure build pnpm --filter '@object-ui/plugin-dashboard^...' build exit 0. type-check exit 0, and tsconfig.test.json lists the new test. eslint (package form) on the 4 changed source files: 0 errors, 1 existing warning; no type-aware linting in eslint.config.js. Pre-tsc battery (15 check: commands) plus check-hand-rolled-comment-mask, check-changeset-fixed, check-changeset-presence and check:new-line-citations: all exit 0. NOT MEASURED: the full packages/plugin-dashboard/ directory run, reason: the dispatch declares it to CI.",
"mcp_calls": "0",
"api_writes": "3 — git push x2 (branch probe and commit; not REST), fleet-write dispatch pr_create (POST /repos/objectstack-ai/objectstack/dispatches, executing POST /repos/objectstack-ai/objectui/pulls and POST /repos//issues/12950/assignees), and this os-dev-report comment via the fleet-write comment op. Zero label writes: objectui path labels come from labeler.yml and the dispatch named none.",
"gates": "closure-build 0; type-check 0; targeted-vitest 0 (36 files/378 tests); lint(narrowed, 4 files) 0; check:phantom-deps 0; check:self-import 0; check:unreferenced-sources 0; check:doc-example-readers 0; check:handler-key-reads 0; check:side-effects-array 0; check:element-data-source-declaration 0; check:spec-symbols 0; check:action-forward-parity 0; check:designer-field-key-parity 0; check:icon-record-names 0; check:i18n-keys 0; check:i18n-drift 0; check:i18n-designer-parity 0; check:control-bytes 0; check-hand-rolled-comment-mask 0; check-changeset-fixed 0; check-changeset-presence 0; check:new-line-citations 0; CI: in_progress",
"files_changed": [
"packages/plugin-dashboard/src/metricTile.ts",
"packages/plugin-dashboard/src/MetricWidget.tsx",
"packages/plugin-dashboard/src/MetricCard.tsx",
"packages/plugin-dashboard/src/tests/metricTileValueScale-12942.test.tsx",
".changeset/12942-metric-value-scale.md"
],
"deviations": "One tsc --listFiles count of tsconfig.test.json ran outside the verify lock. No screenshots are attached: the PR body records the scrollWidth/clientWidth numbers instead, which A4 accepts. Until the dataset-bound tile gets its own inset-free ladder, it stays on the old constant.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · DatasetWidget's headed metric tile keeps METRIC_TILE_VALUE_CLASS (value room = the whole container, clip below about 133 px for a 6.6 em value); not measured clipping on any page; noted in the PR's Acceptance notes, not filed"
]
}
PR #12950 at head 0f9795d, reviewed against the claim 6247460680, triage's route 6247342683 and the dev report 6247669204.
What changed:metricTile.ts gains a value size ladder (METRIC_TILE_VALUE_LADDER, metricTileValueClass, metricTileValueEm, metricTileValueTitle). The plain tiles (MetricWidget, and so every object-metric block, and MetricCard) pick a rung by how wide the formatted value draws: a short value (≤ 4.5 em) keeps today's text-xl @[160px]:text-2xl; a longer one starts at 14 px and steps up at container widths derived as ceil(48 + maxEm × px) (48 px = the Card's content inset). Past the last rung the value stays at 14 px and carries the full figure as its tooltip. Both callers pass strings (MetricCard.value: string | number; MetricWidget's displayValue is a template string), so the width estimate reads the figure the tile draws.
Route held: the value scales; the tile span (page-block-span.ts) is untouched. Scaling alone fits the seven-digit figure, so no wider change was needed.
Browser reading (Chromium, Inter), 1,290,000.00: 156 px tile 126/106 clipped at 20 px → 106/106 at 14 px; 174 px tile 152/124 clipped at 24 px → 124/124 at 18 px. The before figures reproduce the card's exactly. Numbers are in the PR body; no screenshots attached.
Clause-②:no, confirmed: the new names are module exports of metricTile.ts only; src/index.tsx re-exports none of them and the package's exports map has the single "." entry. No contract review owed.
Tests:metricTileValueScale-12942.test.tsx (8 tests) pins an object-metric with 1290000 / 0,0.00 on the scaled rung (14 px at a 154 px container, 18 px at 172) with the title set, a CONTROL (4,210 keeps METRIC_TILE_VALUE_CLASS, no title), and re-derives every ladder step from the formula. Reverse check: reverting MetricWidget turns 2 red with the control green; restore proven by blob hash. The 36 plugin-dashboard files naming these symbols pass (378 tests); type-check and the pre-tsc battery exit 0. The full packages/plugin-dashboard/ directory is declared to CI.
Acceptance notes, not filed:DatasetWidget's headed metric tile keeps METRIC_TILE_VALUE_CLASS: it has no Card inset, so the ladder measured against a Card does not apply, and its value has the container's whole width (a clip would start below about 133 px for a 6.6 em value). No page was measured clipping there.
Trailers: the one commit carries only the model-free pair.
CI at head: all 36 check-runs on 0f9795d are complete: 33 success, 3 skipped (dependabot and the two coverage jobs), none failed; mergeable_state: clean. Path surface (5 files, +262 / −7): packages/plugin-dashboard/src/** and one changeset, no governed path.
PR #12950 merged through the merge queue as 0707b4ae37120d45d6401170666a0b76cb7a3546, which is on main; Fixes #12942 closes this card. Verified by content: all 5 of the PR's files on main are blob-identical to the accepted head 0f9795d. The merge commit's message names no model.
What landed:
A metric tile's value scales before it would clip. A long value (a seven-digit money figure with cents) steps down from 24 px to 14 px as the tile narrows, so 1,290,000.00 fits whole in a 156 px tile (14 px) and a 174 px tile (18 px). A short value keeps its 20 / 24 px face; a value too wide even at 14 px keeps the ellipsis and shows the full figure as its tooltip. Applies to MetricCard, MetricWidget and every object-metric block; the tile span is unchanged. @object-ui/plugin-dashboard: patch.
For hotcrm: Sales Home's Key Performance Indicators show Revenue (Won) and Pipeline Value whole once the console build HotCRM runs carries the merge commit above (objectstack-ai/hotcrm#2125). The dataset-bound headed metric tile is unchanged (see the ACCEPT). This seat now removes pm:dispatched.
Activity
objectstack-fleet commentedon Oct 18, 2026
Triage: p2 ·
area:records·domain:ui·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-18T07:52ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
main32b492b67c:packages/plugin-dashboard/src/metricTile.ts:32METRIC_TILE_VALUE_CLASSstill clips the value withoverflow-hidden text-ellipsis whitespace-nowrapattext-xl/@[160px]:text-2xl.packages/components/src/renderers/layout/page-block-span.ts:37mapsobject-metricto the viewport-basedTILEspan.18.0.0-next.3on HotCRM's Sales Home, a page the maintainer called ugly. The value is still in the DOM, so it holds no release.text-xl), so ⛔ a money value is never ellipsized.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 5 (
priority:p2)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12942-metric-value-scaleWorktree:
objectui-issue-12942Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/plugin-dashboard/src/metricTile.ts(METRIC_TILE_VALUE_CLASSand its container-query steps) and the tile renderer(s) that read it underpackages/plugin-dashboard/src/, with tests beside them. Plus one.changeset/12942-*.mdfor@object-ui/plugin-dashboard. ⛔ Notpackages/components/src/renderers/layout/page-block-span.ts(triage ruled the viewport span out of scope). Stop on breach; explain in the report.Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: no
Responsibility:
@object-ui/plugin-dashboard'sMETRIC_TILE_VALUE_CLASSclips the value withtext-ellipsisattext-xl/@[160px]:text-2xl| none: no platform path scales a metric value today | every viewer of anobject-metrictile in a narrowed column; HotCRM's Sales Home (objectstack-ai/hotcrm#2125) today.Thread-read: 6247342683
Serial constraints cleared: no open objectui PR touches
packages/plugin-dashboard/. The in-flight claims (objectui#12943, #12946:@objectstack/specranges; #12724, #12725: chatbot and Studio surfaces) are disjoint.Claimed at: 2026-10-18T08:07Z
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{
"issue": 12942,
"status": "done",
"branch": "claude/issue-12942-metric-value-scale",
"pr": "#12950",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB (parent's; subagent run)",
"premise_still_valid": true,
"summary": "metricTile.ts now has a value size ladder (METRIC_TILE_VALUE_LADDER plus metricTileValueClass, metricTileValueEm and metricTileValueTitle). The plain tiles (MetricWidget, so object-metric too, and MetricCard) pick a rung by how wide the value draws. A short value (4.5 em or less) keeps today's text-xl @[160px]:text-2xl face. A longer one starts at 14 px and steps up at container widths ceil(48 + maxEmpx), where 48 px is the Card's value inset. Past the last rung it stays 14 px, and the value carries its full figure as a title tooltip. Browser reading on Chromium with Inter: 1,290,000.00 went from 126/106 clipped (20 px) to 106/106 (14 px) at a 156 px tile, and from 152/124 clipped (24 px) to 124/124 (18 px) at a 174 px tile. The before figures match the card exactly. The span is unchanged and DatasetWidget is untouched (no Card inset; noted in Acceptance notes).",
"tests": "On 0f9795d. New metricTileValueScale-12942.test.tsx: 8 passed. It pins (a) object-metric with fallbackValue 1290000 and format 0,0.00 on the scaled rung (14 px at a 154 px container, 18 px at 172), not @[160px]:text-2xl, with the title set; and (b) CONTROL: 4,210 keeps METRIC_TILE_VALUE_CLASS with no title. It also re-derives every ladder step from the formula. Reverse check on the committed tree (MetricWidget reverted to METRIC_TILE_VALUE_CLASS; injected=1, removed_left=0 by grep -c): 2 failed / 6 passed with the control green. Restored with git checkout HEAD --; git diff HEAD = 0 bytes; blob hash 897bbcf2 matches HEAD. Targeted vitest: 36 plugin-dashboard files naming METRIC_TILE_VALUE_CLASS, metricTile, MetricWidget, MetricCard or object-metric, 36 files / 378 tests passed (lock VERDICT command-exit 0). Closure build pnpm --filter '@object-ui/plugin-dashboard^...' build exit 0. type-check exit 0, and tsconfig.test.json lists the new test. eslint (package form) on the 4 changed source files: 0 errors, 1 existing warning; no type-aware linting in eslint.config.js. Pre-tsc battery (15 check: commands) plus check-hand-rolled-comment-mask, check-changeset-fixed, check-changeset-presence and check:new-line-citations: all exit 0. NOT MEASURED: the full packages/plugin-dashboard/ directory run, reason: the dispatch declares it to CI.",
"mcp_calls": "0",
"api_writes": "3 — git push x2 (branch probe and commit; not REST), fleet-write dispatch pr_create (POST /repos/objectstack-ai/objectstack/dispatches, executing POST /repos/objectstack-ai/objectui/pulls and POST /repos//issues/12950/assignees), and this os-dev-report comment via the fleet-write comment op. Zero label writes: objectui path labels come from labeler.yml and the dispatch named none.",
"gates": "closure-build 0; type-check 0; targeted-vitest 0 (36 files/378 tests); lint(narrowed, 4 files) 0; check:phantom-deps 0; check:self-import 0; check:unreferenced-sources 0; check:doc-example-readers 0; check:handler-key-reads 0; check:side-effects-array 0; check:element-data-source-declaration 0; check:spec-symbols 0; check:action-forward-parity 0; check:designer-field-key-parity 0; check:icon-record-names 0; check:i18n-keys 0; check:i18n-drift 0; check:i18n-designer-parity 0; check:control-bytes 0; check-hand-rolled-comment-mask 0; check-changeset-fixed 0; check-changeset-presence 0; check:new-line-citations 0; CI: in_progress",
"files_changed": [
"packages/plugin-dashboard/src/metricTile.ts",
"packages/plugin-dashboard/src/MetricWidget.tsx",
"packages/plugin-dashboard/src/MetricCard.tsx",
"packages/plugin-dashboard/src/tests/metricTileValueScale-12942.test.tsx",
".changeset/12942-metric-value-scale.md"
],
"deviations": "One tsc --listFiles count of tsconfig.test.json ran outside the verify lock. No screenshots are attached: the PR body records the scrollWidth/clientWidth numbers instead, which A4 accepts. Until the dataset-bound tile gets its own inset-free ladder, it stays on the old constant.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · DatasetWidget's headed metric tile keeps METRIC_TILE_VALUE_CLASS (value room = the whole container, clip below about 133 px for a 6.6 em value); not measured clipping on any page; noted in the PR's Acceptance notes, not filed"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-18T08:43ZPR #12950 at head
0f9795d, reviewed against the claim6247460680, triage's route6247342683and the dev report6247669204.metricTile.tsgains a value size ladder (METRIC_TILE_VALUE_LADDER,metricTileValueClass,metricTileValueEm,metricTileValueTitle). The plain tiles (MetricWidget, and so everyobject-metricblock, andMetricCard) pick a rung by how wide the formatted value draws: a short value (≤ 4.5 em) keeps today'stext-xl @[160px]:text-2xl; a longer one starts at 14 px and steps up at container widths derived asceil(48 + maxEm × px)(48 px = the Card's content inset). Past the last rung the value stays at 14 px and carries the full figure as its tooltip. Both callers pass strings (MetricCard.value: string | number;MetricWidget'sdisplayValueis a template string), so the width estimate reads the figure the tile draws.page-block-span.ts) is untouched. Scaling alone fits the seven-digit figure, so no wider change was needed.1,290,000.00: 156 px tile126/106clipped at 20 px →106/106at 14 px; 174 px tile152/124clipped at 24 px →124/124at 18 px. The before figures reproduce the card's exactly. Numbers are in the PR body; no screenshots attached.no, confirmed: the new names are module exports ofmetricTile.tsonly;src/index.tsxre-exports none of them and the package'sexportsmap has the single"."entry. No contract review owed.metricTileValueScale-12942.test.tsx(8 tests) pins anobject-metricwith1290000/0,0.00on the scaled rung (14 px at a 154 px container, 18 px at 172) with the title set, a CONTROL (4,210keepsMETRIC_TILE_VALUE_CLASS, no title), and re-derives every ladder step from the formula. Reverse check: revertingMetricWidgetturns 2 red with the control green; restore proven by blob hash. The 36 plugin-dashboard files naming these symbols pass (378 tests); type-check and the pre-tsc battery exit 0. The fullpackages/plugin-dashboard/directory is declared to CI.DatasetWidget's headed metric tile keepsMETRIC_TILE_VALUE_CLASS: it has no Card inset, so the ladder measured against a Card does not apply, and its value has the container's whole width (a clip would start below about 133 px for a 6.6 em value). No page was measured clipping there.0f9795dare complete: 33 success, 3 skipped (dependabot and the two coverage jobs), none failed;mergeable_state: clean. Path surface (5 files, +262 / −7):packages/plugin-dashboard/src/**and one changeset, no governed path.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-18T08:57ZPR #12950 merged through the merge queue as
0707b4ae37120d45d6401170666a0b76cb7a3546, which is onmain;Fixes #12942closes this card. Verified by content: all 5 of the PR's files onmainare blob-identical to the accepted head0f9795d. The merge commit's message names no model.What landed:
1,290,000.00fits whole in a 156 px tile (14 px) and a 174 px tile (18 px). A short value keeps its 20 / 24 px face; a value too wide even at 14 px keeps the ellipsis and shows the full figure as its tooltip. Applies toMetricCard,MetricWidgetand everyobject-metricblock; the tile span is unchanged.@object-ui/plugin-dashboard: patch.For hotcrm: Sales Home's Key Performance Indicators show Revenue (Won) and Pipeline Value whole once the console build HotCRM runs carries the merge commit above (objectstack-ai/hotcrm#2125). The dataset-bound headed metric tile is unchanged (see the ACCEPT). This seat now removes
pm:dispatched.Generated by Claude Code