Repository navigation
dashboards: rotated x-axis labels are clipped, and the dashboard inspector Layout fields are squeezed to unreadable #11796
Description
Filing gate ① — product defect with a named location and a reproduction. reach: Studio → showcase → Interfaces → Delivery Operations dashboard (same widgets the app serves).
Who acts on it: objectui triage → the Studio / app-shell owner. ⛔ Not a claim. Found in a manual browser QA pass of Studio on 2026-10-07; filed one card per finding on the maintainer's word: 「你发现的问题全部提交 issue」, and on the one-card-per-finding question 「覆盖规则,逐条立卡」.
What happens
- (Fractional y ticks on count charts are already tracked in charts: count measures get fractional y-axis ticks (0.5, 0.8, 1.5 …) #11681 and are not repeated here.)
- Rotated x labels are clipped ("acklog", "ogress").
- The dashboard inspector's Layout block puts Columns / Gap / Refresh Interval Seconds in three columns inside ~220 px: "12" shows as "1", labels wrap to three lines, help text is cut.
Expected
Rotated labels inside the plot bounds; inspector fields stacked so values and labels are readable.
Suggested direction (triage to rule)
Reserve bottom margin for rotated labels (or truncate with a tooltip); one column for the Layout fields in the narrow inspector.
Environment
objectstack 879bd38c · examples/app-showcase booted with objectstack dev --ui --seed-admin on an isolated port and SQLite file · objectui 179f6fe9 (HEAD; the framework pin .objectui-sha is a58626c8) served by the console's Vite dev server, perf numbers from a vite build of the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform admin admin@objectos.ai unless stated.
Duplicate check
- Semantic issue search on objectstack-ai/objectui (open and closed), dashboard chart fractional ticks clipped axis labels inspector layout: 8 hits (1 open, 7 closed).
- Related, not the same defect:
- charts: count measures get fractional y-axis ticks (0.5, 0.8, 1.5 …) #11681 (open) — OPEN (pm:on-hold, filed 2026-10-06): exact duplicate of this card's fractional-ticks part
- plugin-charts: a categorical x axis above 5 buckets still loses labels — minTickGap 48 is a time-series budget charged to band axes #7386 (closed) — x-axis label loss family; this card's rotated-label clipping is new
- Positive control in the same run: related lists action id refusal notice shown to end users on objectstack-ai/objectui returned related lists: an action id the related object does not define draws a refusal notice above the list for whoever opens the record — an authoring fault shown to end users; scope it to authors/admins #11768: yes.
Dedupe words: dashboard chart fractional ticks count · rotated axis labels clipped · dashboard inspector layout cramped
Filed by Claude Code (session session_01D76mrPJrSSdaKRxR2rvrMG) from that QA pass.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 7, 2026
Path: ② the capabilities an end user meets in the app — a dashboard's charts | 缺项 | P3
Triage: first grade,
bug·priority:p3·domain:ui·area:studio·pm:queue. Direction as the body proposesTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-07T17:18Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in the chart widget's axis margin (
plugin-charts/plugin-dashboard) and the Studio dashboard inspector's Layout section ⇒domain:ui; rationale: a Studio or console surface in objectui. Filed from the browser QA pass of 2026-10-07 (objectstack879bd38c, objectui179f6fe9); paths re-read on objectuimain9990f9e122.Clause-②: no. Patch changeset in objectui.objectstack-fleet commentedon Oct 8, 2026
Claim: PM loop round 3
Session:
session_01CGZy1BGCjdN5cXqL9cnvB8Account:
os-support-aiBranch:
claude/issue-11796-rotated-labels-layoutWorktree:
objectui-issue-11796Domain:
domain:uiSeat:
domain:ui#3File surface (on
1e1f09e):packages/plugin-charts/src/AdvancedChartImpl.tsx(the rotated x-axis label handling at about:405–:430,ROTATED_X_LABEL_MAX_CHARS, and the plot's bottom margin);packages/app-shell/src/views/metadata-admin/SchemaForm.tsx's grid widget, and names the file before editing it. A change toSchemaForm.tsx's shared grid behaviour for every other form is not on the surface;.changeset/11796-*.md.Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report)
Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; default tier)Clause-②: no
Responsibility:
objectui plugin-charts + app-shell: rotated category labels are clipped at the plot's bottom edge ("acklog", "ogress"), and the dashboard inspector's Layout block squeezes three fields into about 220px ("12" shows as "1") | the platform path: the chart already rotates and ellipsises long labels; only the reserved margin and the inspector layout are wrong | every end user reading a dashboard bar chart with rotated labels, and every author editing a dashboard's layout in StudioThread-read: 6043042473
Serial constraints cleared:
noneblocking. NeitherAdvancedChartImpl.tsxnor the dashboard inspector is held by an in-flight card. This seat's objectui#11785 (area:studio) holdsStudioDesignSurface.tsx's refusal strips, and seat 1's objectui#11783 (area:studio) holdsObjectFieldInspector.tsx/FlowReferenceField.tsx; both are disjoint. No open objectui PR touches the files above (read 2026-10-08T00:35Z; open: #11841, #11600, #11069).Why
Clause-②: no: layout fixes inside two packages. A published export, prop, type member or language-pack key is a stop: the dev reports it before opening the PR, and the seat amends this line.Generated by Claude Code
objectstack-fleet commentedon Oct 8, 2026
os-dev-report
{
"issue": 11796,
"status": "done",
"branch": "claude/issue-11796-rotated-labels-layout",
"pr": "#11851",
"session": "session_01CGZy1BGCjdN5cXqL9cnvB8 — subagent mode, the parent seat's id (container CLAUDE_CODE_REMOTE_SESSION_ID cse_01CGZy1BGCjdN5cXqL9cnvB8 resolves to it; claim 6049709370 names it)",
"premise_still_valid": true,
"summary": "Two fixes, each where its owner lives. (1) plugin-charts AdvancedChartImpl.tsx: a rotated x-axis label is anchored at its end and hangs its start away from the plot, and the axis reserved a fixed height of 60px that counted only the text width (no tickSize offset, no rotated line box). The band is now derived from the longest drawn label (rotatedXAxisHeight: tickSize + tickMargin + 1em cos35 + width sin35, width an upper estimate with CJK/kana/Hangul/full-width glyphs at 1em and others at 0.65em), and the 12-character ellipsis applies to every rotated label so the band stays bounded; non-rotating axes keep recharts' 30px. (2) app-shell DashboardDefaultInspector.tsx (the named file; SchemaForm.tsx untouched): the spec dashboardForm declares its Layout section with columns 3 and header colSpan 3, which SchemaForm honours in the narrow panel; the inspector now stacks every section to one column and clamps colSpan to 1. Zone 2 readings: the chart reserved an XAxis height (not margin.bottom); the clip is the chart SVG's own viewport, not an ancestor overflow (the card grows with content). Measured in Chromium through a temporary uncommitted harness (real DashboardRenderer, runtime and the designer's DashboardPreview wrappers, fake queryDataset with the showcase categories): before, labels reach 61px (In Progress) and 71px (12-char) under a 60px band and, with the legend off, 3 labels lose their starts at the SVG edge; after, bands are 76 and 80 with 0 labels crossing. The inspector reproduced the card exactly at a 240px panel ("12" as "1", three 47px columns) and stacks to one 172px column after. The card's exact "acklog"/"ogress" with the legend ON did not reproduce in this sandbox (tick font is a system stack, Inter here); the mechanism did.",
"tests": "All through os-verify-lock, VERDICT command-exit 0. Final head bf2a3ba (merge of main 59ce048, which touches none of these files): pnpm exec vitest run packages/plugin-charts/ plus the 42-file app-shell set = Test Files 132 passed (132), Tests 1523 passed / 9 skipped (1532). pnpm --filter @object-ui/plugin-charts type-check and pnpm --filter @object-ui/app-shell type-check after building both dependency closures: TC_CHARTS_OK, TC_SHELL_OK (script names echoed); tsc -p tsconfig.test.json --listFiles lists each new test once. New pins: AdvancedChartImpl.rotatedLabelBand-11796.test.tsx (6) and DashboardDefaultInspector.layoutColumns-11796.test.tsx (3). Ablation on 9957feb via objectstack scripts/ablation-replace.mjs (each anchor hit 1 and landed, blob changed; each restore blob == HEAD and git diff HEAD empty; TREE_EQUALS_HEAD=yes): band back to height 60 = 4 of 6 chart pins red (above-bound and control green, untouched by that leg); stacking removed = 2 inspector pins red, SchemaForm control green; wide glyph charged narrow = only the CJK pin red; ellipsis scoped back to the five-bucket branch = only the above-bound pin red; restored = 9/9 green. Expected direction (turns red) observed. No dist involved: the tests import the source relatively. Lint at bf2a3ba: eslint --format json on the 4 touched files = 4 files, 0 errors; AdvancedChartImpl.tsx 44 warnings = base 44; population = each package's own lint script (eslint .); invariance = eslint.config.js has no projectService / parserOptions.project (type-aware grep exit 1), so the diff cannot move an untouched file's verdict.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 — each through the fleet-write relay (one POST /repos/objectstack-ai/objectstack/dispatches per stroke): pr_create = POST /repos/objectstack-ai/objectui/pulls (objectui#11851, draft, read-back 9631/9631 bytes identical); assign = POST /repos//issues/11851/assignees os-support-ai (label-write.mjs read-back MATCHES); comment = POST /repos//issues/11796/comments (this os-dev-report). Plus git push (not REST). No label writes beyond the assignee; path labels came from labeler.yml.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — same family as the inspector half: actionForm (Basics, Advanced), pageForm (Basics) and viewForm (Table options) declare columns 2 and reach narrow inspectors through the same SchemaForm grid; not measured in a panel. Closing the class means a container-aware SchemaForm grid, off this card's surface (seat decision). Dedupe words: inspector two columns narrow · SchemaForm section columns container · spec form columns side panel",
"carrier: 承接者:无 · noted, not filed — xAxis.title is laid out insideBottom of the same band; it moves with the band and was not measured with rotated labels.",
"carrier: 承接者:无 · noted, not filed — the ellipsis counts UTF-16 code units (label.length / slice), so a cut through an astral character can split a surrogate pair; predates this change, now reached on more axes; not measured.",
"carrier: 承接者:无 · noted, not filed — hasLongLabels rotates only when a label is longer than 5 code units, so a 3 to 5 glyph CJK label stays horizontal; predates this change; not measured."
],
"gates": [
{
"command": "pnpm exec vitest run packages/plugin-charts/ + 42-file app-shell set (os-verify-lock, bf2a3ba)",
"exit": 0,
"verdict": "Test Files 132 passed (132) · Tests 1523 passed | 9 skipped (1532) · VERDICT command-exit 0"
},
{
"command": "pnpm --filter @object-ui/plugin-charts type-check (bf2a3ba)",
"exit": 0,
"verdict": "TC_CHARTS_OK · VERDICT command-exit 0"
},
{
"command": "pnpm --filter @object-ui/app-shell type-check (bf2a3ba)",
"exit": 0,
"verdict": "TC_SHELL_OK · VERDICT command-exit 0"
},
{
"command": "pnpm exec eslint --format json (4 touched files, bf2a3ba)",
"exit": 0,
"verdict": "4 files, 0 errors, 46 warnings (44 = base on AdvancedChartImpl.tsx, 2 on the new chart test)"
},
{
"command": "pnpm check:control-bytes",
"exit": 0,
"verdict": "check-control-bytes: OK (scanned 7834 tracked text file(s); skipped 85 binary)"
},
{
"command": "pnpm check:test-path-roots",
"exit": 0,
"verdict": "check-test-path-roots: OK"
},
{
"command": "pnpm check:changeset-claims",
"exit": 0,
"verdict": "Every package declared across those 1 body(ies) is either not negated"
},
{
"command": "pnpm check:pending-changeset-literals",
"exit": 0,
"verdict": "No test source names a pending changeset."
},
{
"command": "pnpm check:new-line-citations",
"exit": 0,
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only, exit 0 (the gate prints an arrow there)"
},
{
"command": "pnpm check:vi-mock-specifiers",
"exit": 0,
"verdict": "check-vi-mock-specifiers: OK"
},
{
"command": "pnpm check:vi-mock-inherit",
"exit": 0,
"verdict": "check-vi-mock-inherit: OK"
},
{
"command": "pnpm check:vi-mock-override-shape",
"exit": 0,
"verdict": "check-vi-mock-override-shape: OK"
},
{
"command": "pnpm check:unreferenced-sources",
"exit": 0,
"verdict": "Every shipped source file in every covered package is reachable."
},
{
"command": "pnpm check:shell-escape-residue",
"exit": 0,
"verdict": "check-shell-escape-residue: OK"
},
{
"command": "pnpm check:spec-symbols",
"exit": 0,
"verdict": "spec symbol derivation: 0 untriaged collisions in 0 packages"
},
{
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)"
},
{
"command": "node scripts/check-governed-queue-guard.mjs --test (5 paths)",
"exit": 0,
"verdict": "NOT GOVERNED — 5 path(s) checked against 5 governed surface(s); none matched"
},
{
"command": "CI on objectui#11851 head bf2a3ba",
"exit": null,
"verdict": "in_progress at report time: 23 completed, 18 in progress, 1 queued, 0 failed (one read, not awaited)"
},
{
"command": "check:i18n-keys / i18n-drift / i18n-dead-keys / i18n-designer-parity / readme-exports",
"exit": null,
"verdict": "not applicable: no locale pack, export or README surface changed"
}
],
"deviations": [
"Declared narrowing: the full app-shell suite (1082 test files; ledger shows 25-47 min lock holds, past the foreground cap) is left to CI. Local set = every app-shell test naming DashboardDefaultInspector / dashboard-schema / getDashboardForm / register-builtins / default-inspector-registry, plus ResourceEditPage / StudioDesignSurface / inspector tests mounting a dashboard: 42 files, 304 tests (9957feb), green again inside the 132 on bf2a3ba. Positive control DashboardDefaultInspector.test.tsx is in the set.",
"The real-browser reading used a temporary, uncommitted Vite harness in the worktree (real DashboardRenderer and DashboardDefaultInspector, fake queryDataset) rather than the booted showcase stack: objectstack is neither installed nor built in this container. Both modes measured (runtime, and the designer shape with designMode). The card's exact legend-on cut did not reproduce here; the mechanism did. Harness, driver scripts and the Vite server (PID 18995, own port 5196) are removed.",
"Bounded in-place widening, named in the PR body: the 12-character ellipsis now also applies to rotated labels above the five-bucket bound (same defect class, same file, mechanical, the existing pinned shape), because a band sized from an unbounded label would be unbounded.",
"The first commit 54b5b75 carried the wide-glyph range ends as raw U+3000 / U+FF00 / U+FFEF bytes (the editor materialised the escapes); eslint refused the irregular whitespace and 9957feb rewrote them as escape text. The branch history keeps it; the squash merge drops it.",
"The merge commit message was amended before its first push to add the model-free trailer pair; nothing pushed was rewritten.",
"PR footer uses the os-dev session-URL form; the harness reminder's two-line attribution (with an emoji) was not used. Session URL is also in the body prose as a code span."
],
"files_changed": [
"packages/plugin-charts/src/AdvancedChartImpl.tsx",
"packages/plugin-charts/src/AdvancedChartImpl.rotatedLabelBand-11796.test.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/DashboardDefaultInspector.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/DashboardDefaultInspector.layoutColumns-11796.test.tsx",
".changeset/11796-rotated-labels-layout.md"
],
"line_budget": "not applicable (no skills/** surface)"
}
objectstack-fleet commentedon Oct 8, 2026
ACCEPT —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T01:59Z. PR objectui#11851, headbf2a3ba.PR shape: draft against
main, with59ce048merged in (no rebase). First lineFixes #11796, no other closing keyword.Clause-②: noat line start. The three commits carry only the model-free trailer pair. Assigned toos-support-ai.Scope: 5 files, +381/−28, on the claim's surface (
6049709370):packages/plugin-charts/src/AdvancedChartImpl.tsx(the rotated-label band,ROTATED_X_LABEL_MAX_CHARS);packages/app-shell/src/views/metadata-admin/inspectors/DashboardDefaultInspector.tsx, the inspector file the claim asked the dev to measure and name. It is named in the PR body with the reading:@objectstack/spec'sdashboardFormdeclares Layoutcolumns: 3, and this inspector isgetDashboardForm's only consumer;patchon both packages).SchemaForm.tsxis untouched, so every other form keeps its declared columns. Nothing is added to either package entry. No governed path.Diff read (the seat's own):
height: 60on a rotated x axis becomesrotatedXAxisHeight(labels):tickSize(6) +tickMargin(10) + 1em·cos 35° + widest·sin 35°. The width is an upper estimate (0.65em per glyph, 1em for a wide CJK, kana, Hangul or full-width glyph, at the 12px tick font). With the 12-character cap, a Latin band tops out near 80px. A chart whose labels do not rotate passes noheight, so it keeps recharts' default.stackSectionsForNarrowPanelsets every section tocolumns: 1and clamps a field'scolSpanto 1, after the server-field graft. A{ group }section is left as it came. In this panel that also stacks Basics (columns: 2: name and label), the same defect at a smaller size. The changeset says "every section", which matches.Ruling honoured (triage
6043042473): "reserve bottom margin for rotated category labels, or truncate them with a tooltip": both, the band from the labels plus the cap. "one column for the Layout fields in the narrow inspector": done, scoped to this inspector.Changeset sentences checked against the head:
Reverse verification (dev report):
ablation-replace.mjson9957feb, four legs, each restored with the blob equal to HEAD: the band put back to 60 gives 4 of 6 chart pins red; removing the stacking gives 2 inspector pins red with theSchemaFormcontrol green; charging a wide glyph as narrow gives only the CJK pin red; scoping the ellipsis back gives only the above-bound pin red.DashboardRenderer, uncommitted and removed): with the legend off, 3 labels lost their starts under the 60px band before, and 0 after (bands 76 and 80). The inspector reproduced "12" as "1" at a 240px panel, and stacks to one 172px column after. The card's exact "acklog" cut with the legend on did not reproduce on this machine's fonts; the mechanism did. Recorded as such in the PR body.plugin-chartsplus the 42-file app-shell set atbf2a3ba: 132 files, 1523 tests passed. Both type-checks exit 0.Gates: CI on
bf2a3ba(head_shaequals the PR head): 43 runs, 40 success, 3 skipped (Test (coverage), its shard template,dependabot), 0 failure.LintandType Checkare success. Mergeable stateclean.mainhas since moved to5aa7f55(objectui#11785 and objectui#11789, neither touching these files), andgit merge-treeof it and this head is clean; the queue tests that merge.Contract review: not required (
Clause-②: no).Findings:
columns: 2through the sameSchemaFormgrid (actionForm,pageForm,viewForm). Not measured in a panel, so it does not pass the filing gate's reach test →Acceptance notesAcceptance notesAcceptance notesLanding: ready + auto-merge through the queue, now.
Generated by Claude Code
objectstack-fleet commentedon Oct 8, 2026
Landed —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T02:19Z. PR objectui#11851 merged through the queue as82500a7at 2026-10-08T02:17Z.5aa7f55, an ancestor oforigin/main.git merge-treeof that parent and the accepted headbf2a3ba(treecd8c345aon both).5aa7f55→82500a7:textAnchor: 'end' as const, height: 60inAdvancedChartImpl.tsx: 1 → 0.rotatedXAxisHeight(inAdvancedChartImpl.tsx: 0 → 2.stackSectionsForNarrowPanel(inDashboardDefaultInspector.tsx: 0 → 2.metadata-admin/SchemaForm.tsxis byte-identical on both sides, so every other form keeps the columns its sections declare.Fixes #11796. It is the only issue closed since the previous landing at 01:53Z.pm:dispatchedis removed.columns: 2sections, the surrogate-pair cut, short CJK labels never rotating, the axis title in the band) have no measured reach.Generated by Claude Code
fix(plugin-charts,app-shell): rotated x-axis labels fit their band; t…