summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/style.css
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-09 14:28:40 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-09 14:28:40 +0200
commit7e2d078fe1870d256ae47781bee6ac4f454edf24 (patch)
tree05689049fd48f01ebbdb9995d5189cba15cee052 /packages/meshbay-hub/src/meshbay_hub/static/style.css
parent813d18424ec57963bb56e6f40824a2db0ccce50d (diff)
parente6f895c473a0b19e7b186889c1836d3945bc880b (diff)
downloadmeshbay-7e2d078fe1870d256ae47781bee6ac4f454edf24.tar.gz
Merge branch 'fix/large-download-paths'
Concurrent-transfer limits, with the queue, the pause and the flag day. A node now caps how many transfers it runs at once (8 downloads, 8 uploads, node-wide) and how many one member may run in one group (2 by default, operator-signed). Beyond that the node answers "queued" and the client waits its turn, visibly, in the transfers panel — and a slot that frees starts whatever is next, skipping past a member who is at their own cap rather than letting them stall everyone behind them. Browsing is never subject to a slot: not the poster grid, not the covers, not opening a photo to look at it. That is structural — a transfer is what the transfers widget shows — and the exemption is bounded rather than open, at two files in flight per session, because an exemption with no bound is a leaseless branch under another name. Transfers can be cancelled, and now paused and resumed. A paused one holds nothing: its slot goes back at once and resuming rejoins the queue at the tail. Uploads survive the connection that started them and resume where the node stopped, asked for inside the seal rather than on a clear message. What they leave behind when they are abandoned is reaped, which closes a disk leak that predates this work. MNP 3.0 makes the lease compulsory and refuses 2.x at the handshake, with the desktop client checking `client.minimum` before connecting so an un-updated one says "update" instead of failing every connection in a protocol vocabulary. Fourteen defects were found on the way, eight of them by a person clicking Download and pasting a console — none of which 2075 tests could reach. Section 12 of ~/next/improve-downloads.md is that report, including the three this work introduced itself and the one that turned out to be caused by an instruction to hard-reload after each deployment. Node suite 1209 passed, hub suite 866 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HCGdheDLxGReuKHga3BtST
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/style.css')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css88
1 files changed, 88 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css
index dfed44b..5e08a6a 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/style.css
+++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css
@@ -1975,6 +1975,30 @@ a.transfer-name {
display: flex;
}
.transfer-cancel:hover { color: var(--error); }
+/* Same shape as cancel, and beside it: pausing and cancelling are the two
+ things a person does to a transfer, and one of them is not destructive. */
+.transfer-pause {
+ background: none;
+ border: none;
+ color: var(--text-dim);
+ cursor: pointer;
+ padding: 0 2px;
+ display: flex;
+}
+.transfer-pause:hover { color: var(--accent); }
+/* Not a button: there is nothing to click. Dimmer than the cancel beside it,
+ and it carries its explanation in a tooltip rather than in the row, which
+ would be four lines of prose in a panel that has none. */
+.transfer-nopause {
+ color: var(--text-dim);
+ opacity: .45;
+ padding: 0 2px;
+ display: flex;
+ cursor: help;
+}
+/* A paused bar keeps its fill -- what was written is still on disk -- but stops
+ looking like something in progress. */
+.dl-fill.dl-paused { background: var(--text-dim); }
.transfer-meta {
display: flex;
justify-content: space-between;
@@ -4243,3 +4267,67 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; }
color: var(--warn);
margin-bottom: 2px;
}
+
+/* A transfer waiting for a slot. Deliberately not a progress bar at 0%: it is
+ not stalled and nothing is wrong, and a bar that never moves is exactly how a
+ queue comes to look like a hang. The stripes say "not yet", not "broken". */
+.dl-progress.dl-waiting {
+ background-color: var(--bg-raised);
+ background-image: repeating-linear-gradient(
+ 45deg,
+ var(--accent-bg) 0 8px,
+ transparent 8px 16px);
+ animation: dl-waiting-slide 1.1s linear infinite;
+}
+@keyframes dl-waiting-slide {
+ from { background-position: 0 0; }
+ to { background-position: 22.6px 0; }
+}
+/* The state has to survive without the motion: someone who asked for less of it
+ still needs to see that this row is waiting rather than stopped. */
+@media (prefers-reduced-motion: reduce) {
+ .dl-progress.dl-waiting { animation: none; }
+}
+
+/* ── Transfers panel (grouped) ───────────────────────────────────────────── */
+
+.transfer-head-title { font-weight: 600; }
+.transfer-head-summary {
+ margin-left: auto;
+ margin-right: .5rem;
+ font-size: .85em;
+ color: var(--text-dim);
+ /* Never wraps to a second line: it is the one part of the header that grows
+ with what is happening, and a header that changes height as transfers come
+ and go pushes every row under it. */
+ white-space: nowrap;
+}
+.transfer-group + .transfer-group { border-top: 1px solid var(--border); }
+.transfer-group-head {
+ padding: .35rem .6rem .2rem;
+ font-size: .78em;
+ text-transform: uppercase;
+ letter-spacing: .04em;
+ color: var(--text-dim);
+}
+/* Finished rows recede rather than disappear: somebody who just downloaded
+ three files wants to see that all three are there. */
+.transfer-item.transfer-done .transfer-name,
+.transfer-item.transfer-cancelled .transfer-name { color: var(--text-dim); }
+
+/* Announced, not shown. The transfers panel needs a live region that says what
+ changed state without drawing anything — used with aria-live, so it must
+ stay in the accessibility tree: `display: none` would remove it from there
+ too and announce nothing at all, which is the usual way this is got wrong. */
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ white-space: nowrap;
+ border: 0;
+}