Repository navigation
dashboards: rotated x-axis labels are clipped, and the dashboard inspector Layout fields are squeezed to unreadable #11796
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatarea:studioChanging a running app without code — authoring, publish, docs and the portalChanging a running app without code — authoring, publish, docs and the portal
on Oct 7, 2026 objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsPath: ② 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.- Why p3: labels are clipped and fields are cramped, but the data is right.
- Direction:
- reserve bottom margin for rotated category labels, or truncate them with a tooltip
- one column for the Layout fields in the narrow inspector
Clause-②: no. Patch changeset in objectui.
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 3
Session:session_01CGZy1BGCjdN5cXqL9cnvB8
Account:os-support-ai
Branch:claude/issue-11796-rotated-labels-layout
Worktree:objectui-issue-11796
Domain:domain:ui
Seat:domain:ui#3
File surface (on1e1f09e):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);- the Studio dashboard inspector's Layout block (Columns / Gap / Refresh Interval Seconds): the dev measures whether it is laid out by the dashboard type's form layout metadata or by
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; - the tests beside them;
.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 Studio
Thread-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 commented
on Oct 8, 2026 ContributorAuthorMore actionsos-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 commented
on Oct 8, 2026 ContributorAuthorMore actionsACCEPT —
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;- two new pin files and the changeset (
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):
- The fixed
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. - The 12-character ellipsis now applies to every rotated label, not only at five buckets or fewer. That is the bounded widening the PR body names: it is the constant the claim put on the surface, and an unbounded label would need an unbounded band. The full name stays on the tooltip.
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.
- The fixed
-
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:
- "The axis now reserves the room its longest drawn label needs at the rotation angle … with a wide glyph (Chinese, Japanese, Korean) counted a full em";
- "Every rotated label is now shortened with an ellipsis past 12 characters, also on axes with more than five categories";
- "Charts whose labels do not rotate keep the axis height they had";
- "The inspector now renders every section of the form in one column, in the declared field order. Other forms keep the column layout their sections declare".
-
Reverse verification (dev report):
- Ablation through
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. - Real-browser reading in Chromium (a temporary harness with the real
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.
- Ablation through
-
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:
- Other narrow inspectors may feed spec sections with
columns: 2through the sameSchemaFormgrid (actionForm,pageForm,viewForm). Not measured in a panel, so it does not pass the filing gate's reach test →Acceptance notes - The ellipsis counts UTF-16 code units, so a cut can split a surrogate pair. This predates the change and is not measured →
Acceptance notes - A 3–5 glyph CJK label never rotates (the rotation trigger counts code units), and the x-axis title sits in the same band. Neither is measured →
Acceptance notes
Landing: ready + auto-merge through the queue, now.
Generated by Claude Code
-
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-08T02:19Z. PR objectui#11851 merged through the queue as82500a7at 2026-10-08T02:17Z.- The merge:
- One parent,
5aa7f55, an ancestor oforigin/main. - The landed tree equals
git merge-treeof that parent and the accepted headbf2a3ba(treecd8c345aon both). - 5 files, +381/−28.
- One parent,
- Content check,
5aa7f55→82500a7:- The fixed band
textAnchor: 'end' as const, height: 60inAdvancedChartImpl.tsx: 1 → 0. rotatedXAxisHeight(inAdvancedChartImpl.tsx: 0 → 2.stackSectionsForNarrowPanel(inDashboardDefaultInspector.tsx: 0 → 2.- Control:
metadata-admin/SchemaForm.tsxis byte-identical on both sides, so every other form keeps the columns its sections declare.
- The fixed band
- Closures: the PR's only closing keyword was
Fixes #11796. It is the only issue closed since the previous landing at 01:53Z.pm:dispatchedis removed. - Follow-ups: none filed. The four Acceptance notes (other inspectors'
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
- The merge:
- added a commit that references this issue
on Oct 9, 2026
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
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-showcasebooted withobjectstack dev --ui --seed-adminon an isolated port and SQLite file · objectui179f6fe9(HEAD; the framework pin.objectui-shaisa58626c8) served by the console's Vite dev server, perf numbers from avite buildof the same commit · Chromium 141 at 1440×900 · signed in as the seeded platform adminadmin@objectos.aiunless stated.Duplicate check
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