Skip to content

fix(ui): recover the text that truncate hides - #288

Merged
antosubash merged 3 commits into
mainfrom
fix/truncate-sweep
Sep 10, 2026
Merged

antosubash merged 3 commits into
mainfrom
fix/truncate-sweep

Conversation

@antosubash

@antosubash antosubash commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Follow-up sweep after the landing hero fix in #286. Grepped all truncate uses in .tsx, then measured each in a browser rather than trusting the class list — truncate is usually correct, and the landing bug was specific: the hidden text was needed verbatim and nothing could recover it.

Split out of #286, which merged before this commit was pushed. Rebuilt on top of the hi-fi pages rewrite (#303) — see Re-applied after #303 below.

Measured, not assumed — and the worst case is not mobile

Surface Shown Hidden Recovery
settings store value @1440px 267px 519px (66%) none in-place
settings store description @1440px 267px 226px (46%) none in-place
doctor migration msg @375px ~150px up to 26px (15%) none

The settings row hides two thirds of a realistic connection string on a desktop monitor, with no title and no way to read it without opening the editor — so the list cannot be scanned for the value you are looking for.

What changed

Adds title where the value is arbitrary-length and the row is the only place it appears: the settings store value, doctor migration messages, user names + addresses, in-flight upload filenames and their failure text, and the admin card's url (its only disambiguator when two tools share a label).

title is a partial remedy — it does nothing on touch. It is the right one here because these are scanning surfaces where the layout should stay a fixed grid; the full value stays reachable through the row's own detail route, and the tooltip removes the need to travel for a glance. It would have been the wrong fix for the landing hero, which is why that one got wrapping and a working copy button instead.

Re-applied after #303

The hi-fi pages rewrite landed while this sat open and moved or rewrote every surface the sweep touched, so the fix was re-applied to where the text now lives rather than to the conflict text:

Was Now
Doctor.tsx migrations block pages/components/doctor/MigrationsCard.tsx
UploadProgressRows.tsx pages/components/UploadsCard.tsx
settings Browse.tsx table pages/components/StoreTable.tsx
UserRow.tsx rewritten in place as a stretched row link

Admin.tsx merged clean and keeps its title. Two things changed on the way: the settings description column no longer exists, so only the value carries a title (and it repeats what is rendered, which keeps a masked secret masked); and the uploads row now truncates the failure text too, so that gets the same treatment — both strings come from the server and are unbounded.

The users row needed more than a moved attribute

Main's UserRow is a link stretched over the whole row by a before:inset-0 overlay, and the overlay answers the hover. Measured in Chromium with elementFromPoint, walking up to the first ancestor carrying a title — exactly what a tooltip does:

Hovering title on the link (naive port) Both lines lifted (shipped)
the name member name member name
the address line member name — its own title never fires address
the Last login cell member name — follows the pointer across the row none
the kebab none none

So the naive port is wrong twice over: it announces the name over cells it has nothing to do with, and the address line's tooltip is dead on arrival. Both lines are lifted back above the overlay with relative — already the file's own device for keeping the kebab clickable. The name is wrapped rather than lifted whole, so it stays inside the anchor and still opens the user; the anchor is still the element that clips, ellipsis confirmed at 170px.

One cost, stated plainly: the address line is no longer part of the row-wide click target. The rest of the row, the name included, still opens the user.

Deliberately unchanged

  • CopyableId was already correct — working copy handler, plus title and aria-label carrying the full value. It is the model for the rest; the landing hero had hand-rolled a worse version of a component that already existed.
  • StoreCards and UserCards — the phone variants of the two tables above. title does nothing on touch, so adding it there would be noise, by this PR's own argument.
  • Permission keys in the role editor — measured 0px clipped across all 19 at 375px; the longest is 23 characters and the column fits it. I expected this to be the worst case (granting the wrong permission is consequential) and it was not.
  • Sidebar identity, module names, status labels, branding preview, the vendored shadcn sidebar, and the dev-only placeholder fixture: short, fixed-vocabulary, decorative, or your own identity.
  • Doctor's "run command" block truncates hardcoded short commands that do not clip at any width tested. It has no copy affordance at all, which is a missing feature rather than hidden text, so it is left for its own change.

Verification

make lint clean · 2973 Python passed · 463 JS passed. The hit-testing above was measured on a standalone replica of the row's exact overlay markup, in headless Chromium, before and after the fix.

https://claude.ai/code/session_011URawzYPb8nL2gfGh2yFVn

Follow-up sweep after the landing hero. Grepped all 25 `truncate` uses in
`.tsx`, then measured each in a browser rather than trusting the class list —
`truncate` is usually correct, and the landing bug was specific: the hidden
text was needed verbatim and nothing could recover it.

Measured, not assumed. The worst case is not mobile:

  settings store, 1440px   value        267px shown, 519px hidden (66%)
                           description  267px shown, 226px hidden (46%)
  doctor, 375px            migration msg          up to 26px hidden (15%)

The settings row hides two thirds of a realistic connection string on a
desktop monitor, with no `title` and no way to read it without opening the
editor — so the list cannot be scanned for the value you are looking for.

Adds `title` to the cases where the value is arbitrary-length and the row is
the only place it appears: settings value + description, migration messages,
user names + addresses, in-flight upload filenames, and the admin card's url
(its only disambiguator when two tools share a label).

`title` is a partial remedy — it does nothing on touch. It is the right one
here because these are scanning surfaces where the layout should stay a fixed
grid; the full value stays reachable through the row's own detail route, and
the tooltip removes the need to travel for a glance.

Deliberately unchanged:

- `CopyableId` — already correct, and the model for the rest: working copy
  handler, plus `title` and `aria-label` carrying the full value. The landing
  hero had hand-rolled a worse version of this component.
- Permission keys in the role editor — measured 0px clipped across all 19 at
  375px; the longest is 23 characters and the column fits it.
- Sidebar identity, module names, status labels, the branding preview, the
  vendored shadcn sidebar, and the dev-only placeholder fixture: short,
  fixed-vocabulary, decorative, or your own identity.
- Doctor's "run command" block truncates four hardcoded short commands that
  do not clip at any width tested. It has no copy affordance, which is a
  missing feature rather than hidden text, so it is left for its own change.

`Doctor.tsx` is now exactly at the 300-line cap. The next edit there needs a
split, not a shorter comment.

Verified: make lint clean, 2152 passed / 2 skipped, 127 JS passed. The
settings fix re-measured against the same connection string that exposed it —
still clipped by design, now carrying the full text in `title`.

Claude-Session: https://claude.ai/code/session_01JJtbN97VhtDr28Fuy5JKEF
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 27, 2026

Copy link
Copy Markdown

Deploying simple-module-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: f475305
Status: ✅  Deploy successful!
Preview URL: https://6d31c631.simple-module-python.pages.dev
Branch Preview URL: https://fix-truncate-sweep.simple-module-python.pages.dev

View logs

@antosubash

Copy link
Copy Markdown
Owner Author

/pi fix the conflicts and make the PR ready to merge

@antosubash antosubash added the pi-ready Approved for the headless Pi worker label Sep 1, 2026
@antosubash antosubash removed the pi-ready Approved for the headless Pi worker label Sep 1, 2026
@antosubash

Copy link
Copy Markdown
Owner Author

/pi instruction processed manually because this PR was not associated with a worker-created issue job.

  • Merged the freshly fetched origin/main into fix/truncate-sweep without rebasing.
  • Resolved the Doctor.tsx conflict by preserving the current split MigrationsCard design and moving the full migration-message title onto that production component.
  • Removed the transient pi-ready PR label; it is issue-only and does not bootstrap arbitrary PR adoption.

Validation:

  • make ci-js-lint and make ci-js-typecheck passed.
  • make test-js: 139 passed.
  • 300-line file-size guard passed.
  • Focused review: no P0/P1 findings.
  • All GitHub PR checks passed, including Python tests, JS checks/build, E2E, performance guards, package build, and Cloudflare Pages.

The hi-fi pages rewrite (#303) landed while this sat open, and it moved or
rewrote every surface the sweep touched. Re-applied the fix to where the text
now lives rather than to the conflict text:

  Doctor.tsx migrations block  -> pages/components/doctor/MigrationsCard.tsx
  UploadProgressRows.tsx       -> pages/components/UploadsCard.tsx
  settings Browse.tsx table    -> pages/components/StoreTable.tsx
  users UserRow.tsx            -> rewritten in place as a stretched row link

Admin.tsx merged clean and keeps its `title`.

Two things changed on the way:

- The settings description column no longer exists, so only the value carries
  a `title`. It repeats what is rendered, which keeps a masked secret masked.
- The uploads row now truncates the failure text too, so that gets the same
  treatment; both strings come from the server and are unbounded.

The users row needed more than a moved attribute. Main's `UserRow` is a link
stretched over the whole row by a `before:inset-0` overlay, and the overlay
answers the hover. Measured in Chromium with elementFromPoint: a `title` on
the link is announced over every cell in the row — hovering Last login pops
the member's name — and the `title` on the address line below never fires at
all, because the overlay sits on top of it. Both lines are therefore lifted
back above the overlay with `relative`, which was already the file's own
device for keeping the kebab clickable. The name is wrapped rather than
lifted whole so it stays inside the anchor and still opens the user; the
anchor still clips, ellipsis confirmed at 170px. The one cost is that the
address line is no longer part of the row-wide click target.

Skipped, consistent with the original reasoning: StoreCards and UserCards are
the phone variants of these two tables, and `title` does nothing on touch.

make lint clean, 2973 Python passed, 463 JS passed.

Claude-Session: https://claude.ai/code/session_011URawzYPb8nL2gfGh2yFVn
@antosubash
antosubash merged commit 35dfd54 into main Sep 10, 2026
13 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