Skip to content

fix(ui): display-fidelity and layout-budget hardening (#67 #68 #73 #74) - #81

Merged
GeekCmore merged 4 commits into
mainfrom
fix/display-layout-fidelity
Sep 28, 2026
Merged

GeekCmore merged 4 commits into
mainfrom
fix/display-layout-fidelity

Conversation

@GeekCmore

@GeekCmore GeekCmore commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

fix(ui): display-fidelity and layout-budget hardening (#67 #68 #73 #74)

Summary

Unifies three boundaries behind the audit findings (UX-12/13/14/15/16/17/18/19/23/30) instead of patching each one:

  • Display values — internal ids/enums stop reaching the renderer: a settings select/multiselect projects only values the form can represent (off-list scalars become readable options; a hand-edited container reads as unset instead of a raw JSON id), the validator admission error shows a translated sentence while the JSON-pointer detail still goes to the report log, /plugin says update {version}, and clear-key on a non-writable credential no longer claims the provider was removed.
  • Width & overflow — one OVERFLOW_ELLIPSIS (…) constant marks every signalling truncation (top rule, panel title/footer, ScrollablePanel frame, degenerate-width clamp); ScrollablePanel reports (start-end/total) and degrades to body rows when the host grant cannot fit the frame; header/body lanes name the rows they drop; the bottom lane keeps a pane's fold affordance visible while clamping.
  • Editor-slot budget — screen.editorViewport subtracts the header/bottom lane rows through a width-derived measurement (measuredLaneRows()), and ScrollablePanel derives its body budget from that grant. The subagent panel and its inner transcript share one grant and never lag the dock by a frame; the 2-row status footer + open lanes case keeps the panel title.

What this PR does NOT do

An earlier revision of this description listed work that is not in this diff. To keep review accurate, those items are removed and should be tracked separately:

  • No shared core/format.ts. status-jobs.ts singular/plural, hours-aware durations, loader elapsedMs formatting, and displayKey function-key chords are unchanged.
  • The goal footer (status-goal.ts) still renders Goal {phase} · n/m · {activation} with armed/disarmed.
  • Immutable /version and /changelog still carry a Refresh action.
  • The Reminders badge capitalization is unchanged.
  • Todo pane rows still omit overflow: 'truncate' and the expanded list still has no size cap.
  • The plan-status ellipsis is unchanged.
  • The CJK-safe ghost hint (display-column measurement) already existed at the merge base; this PR only replaces the equivalent trimmed.length === 0 with trimmed === ''.

Issue status

Verification

pnpm run verify:full (exit 0): test:repo-workflow, typecheck, lint, diagrams:check, build, check:lib (53 lib claims), shots:check (36 shots current), check:agent-docs, check:examples, test:coverage (per-file 100% statements/branches/functions/lines; 3827 passed), smoke:happy (HAPPY_SMOKE_PASS).

New regression coverage:

  • pane-todo.spec.ts pins endsWithAffordance against the real todoNode (folded/expanded true, short list false).
  • plugin-surface-bridge-pane.spec.ts asserts the overflowKeepsTail classification per node shape instead of only pane registration.
  • surface-manager.spec.ts asserts the lane keeps the tail when a pane marks the affordance.
  • scrollable-panel.spec.ts covers the compact-height degrade, a zero grant, a non-finite grant, and the … degenerate-width marker.
  • terminal.spec.ts covers measuredLaneRows clamping.
  • settings-model.spec.ts asserts the projected value for malformed select/multiselect/boolean fields.

Manual acceptance (pending)

  1. PROFILE=mayfly-display-layout script/install-dev.sh, then dsh --profile mayfly-display-layout.
  2. Primary: a zh ghost hint (<文件路径>) stays inside the editor row; a long todo list folds with a visible Ctrl+T affordance; /settings shows a readable label for a stored preset that was deleted.
  3. Boundary: contribute a 2-row status entry and open a lane — the subagent panel title must remain visible; squeeze the terminal very short and confirm the panel does not spill past the editor slot.
  4. Nearby regression: zh/en switch, /help, /plugin, and the todo/queue panes unchanged.

Notes

…ayout

Display values (refs #67):
- settings selects/multiselects append off-list stored scalars as readable
  options, so a drifted value never renders its raw JSON id or trips a
  save error for an invisible selection.
- the goal footer hides completed goals, spells out the continuation flag,
  labels the round fraction, and matches the todo pane's tone.
- validator admission failures show a translated sentence; the JSON-pointer
  path stays in the report.
- shared core/format.ts (pluralize, compact durations) used by the jobs
  status, loader, and agent presentation; displayKey uppercases function-key
  chords.

Overflow and width (refs #68, #74):
- injectGhostHint measures display columns and clamps the row, so CJK/emoji
  hints cannot overflow render(width) or split a surrogate pair.
- one overflow marker for header/bottom lanes and a single `…` convention
  across chrome.
- todo rows truncate, the expanded list is capped, and the fold affordance
  survives lane clamping; ScrollablePanel shows its scroll position and
  degrades instead of vanishing below width 5.

Editor-slot budget (refs #68):
- the runtime exposes the applied header/bottom lane rows; screen.editorViewport
  subtracts them; ScrollablePanel derives its body budget from that one grant
  and the transcript renderer shares it.

Copy nits (refs #73):
- clear-key on a non-writable credential no longer claims the provider was
  removed; no Refresh on the immutable /version and /changelog; lowercase
  reminders badge; `…` in the stopping row and plan status; `[image]` is
  rendered once (attachment rows own it).

Verified with pnpm run verify:full (typecheck, lint, diagrams, build,
check:lib, shots, agent docs, examples, website build, coverage, happy smoke).
@GeekCmore
GeekCmore force-pushed the fix/display-layout-fidelity branch from d9dcafc to 76fa150 Compare September 28, 2026 09:13
…me minimum

Share one OVERFLOW_ELLIPSIS constant for signalling truncation, mark the ScrollablePanel degenerate-width clamp with it, and render body rows alone when the host grant cannot fit the frame so rows never spill past the grant.

Export endsWithAffordance and pin the fold-affordance contract against the real todo node; assert the overflowKeepsTail classification and the lane tail-keeping wiring instead of only pane registration.
surfaceLaneRows now measures the width-derived header/bottom lanes on demand through measuredLaneRows instead of reading values the dock writes after its children render, so editorViewport is never a frame stale. Over-counting lanes can only shrink the editor slot, never overflow it.
…help

Project select/multiselect values only for scalars the form can represent, so a hand-edited container no longer surfaces a raw JSON id, and use the shared capitalized Help title for the overlay while dropping the dead catalog key.
@GeekCmore
GeekCmore merged commit f36c64e into main Sep 28, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant