summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-19 18:32:47 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-19 18:32:47 +0200
commit39c58b807d5f62ee55b606ceb79f21855221a8d0 (patch)
tree39289388df6c67ece508c8d060a203553d33475a /packages
parent1ec316035ce9aa656dd18a05889856bce9e3aba3 (diff)
downloadmeshbay-39c58b807d5f62ee55b606ceb79f21855221a8d0.tar.gz
fix(hub): the members table stops hanging off the side of a phone
`.admin-table` column heads are `white-space: nowrap`, which meets a translated string: "Username" is one word, "Nom d'utilisateur" is three, uppercased with letter-spacing on top. The heads alone overflowed a 360px window by 11px, and on Android an overflowing document takes every sticky element with it. 11px was only what could be measured: the probe served no members, so the table measured its headers and nothing else. With one realistic username in it the same page overflowed by 248px, at 420px-wide windows too. The cells get `word-break: break-word`, as `.file-name` already had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css17
-rwxr-xr-xpackages/meshbay-hub/tests/harness/sticky_header_probe.py18
2 files changed, 33 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css
index 81ae895..63f631a 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/style.css
+++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css
@@ -2110,6 +2110,14 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
border-collapse: collapse;
font-size: 0.88em;
}
+/* A column head is a *translated* string, so its length is not ours to assume:
+ "Username" is one word in English and "Nom d'utilisateur" is three in French,
+ and uppercased with letter-spacing on top. Under `white-space: nowrap` the
+ longer one sets the column's minimum width, and the whole table hangs off the
+ side of a phone — which on Android takes every `position: sticky` element
+ with it, the fault already recorded against the Files table one table along.
+ A head that wraps onto two lines is legible; a table the reader cannot reach
+ the right of is not. */
.admin-table th {
text-align: left;
padding: 8px 10px;
@@ -2119,12 +2127,19 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
- white-space: nowrap;
+ white-space: normal;
}
+/* And the same rule as `.file-name`, for the same reason: these cells carry
+ names somebody else chose — a username, a group name — and a name with no
+ spaces in it has nowhere to break unless it is told to. The heads alone cost
+ 11px of overflow at 360px; with a realistic username in the fixture it was
+ 248px, and it reached 420px-wide windows too. */
.admin-table td {
padding: 8px 10px;
border-bottom: 1px solid var(--border);
vertical-align: middle;
+ word-break: break-word;
+ min-width: 0;
}
.admin-table tr:hover { background: var(--bg-raised); }
.admin-table .admin-empty {
diff --git a/packages/meshbay-hub/tests/harness/sticky_header_probe.py b/packages/meshbay-hub/tests/harness/sticky_header_probe.py
index 6f084d6..7579bf9 100755
--- a/packages/meshbay-hub/tests/harness/sticky_header_probe.py
+++ b/packages/meshbay-hub/tests/harness/sticky_header_probe.py
@@ -401,7 +401,11 @@ const settle = () => new Promise((r) =>
.slice(0, 6)
.map((el) => (el.tagName.toLowerCase() + '.' + (el.className || ''))
.slice(0, 60) + ' →'
- + Math.round(el.getBoundingClientRect().right));
+ + Math.round(el.getBoundingClientRect().right)
+ // The text, because the element alone never says which
+ // cell it was: a column is as wide as its widest
+ // unwrappable string, and that string is the finding.
+ + ' ' + JSON.stringify((el.textContent || '').trim().slice(0, 40)));
})(),
innerHeight, innerWidth,
// What the stylesheet was actually handed, so a wrong offset can be told
@@ -487,6 +491,18 @@ class H(http.server.BaseHTTPRequestHandler):
self._send(render_frame(index).encode(), "text/html; charset=utf-8")
elif path == "/v1/groups/g1/nodes":
self._send(b'{"nodes": [{"node_id": "n1"}]}', "application/json")
+ elif path == "/v1/groups/g1/members":
+ # Served at all because an empty table measures only its headers,
+ # and a member's name is a string this project did not choose. One
+ # of them has no spaces to break at, which is what makes a cell set
+ # its column's minimum width — the fault already recorded against
+ # the Files table, one table along.
+ self._send(
+ b'{"members": ['
+ b'{"user_id": "u1", "username": "moi"},'
+ b'{"user_id": "u2", "username":'
+ b' "un_utilisateur_au_nom_deraisonnablement_long_2026"}]}',
+ "application/json")
else:
asset = (STATIC / path.lstrip("/")).resolve()
if not str(asset).startswith(str(STATIC)) or not asset.is_file():