diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-08 22:54:16 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-08 22:54:16 +0200 |
| commit | 1a495f5ed3f8a55222d406152c833882264dc377 (patch) | |
| tree | 0c048544cba200fe5ba939d45edd311b2fc69e59 /packages/meshbay-hub/src/meshbay_hub/static/locales | |
| parent | 6803447a8a5cc7a612d08bb858394fd7ae1b049c (diff) | |
| download | meshbay-1a495f5ed3f8a55222d406152c833882264dc377.tar.gz | |
feat(hub): client-side transfer leases and the transfers panel
Steps 5 and 6 of ~/next/improve-downloads.md. The node has handed out slots
since step 2 and nothing asked for one; now the client does, and the panel shows
what is happening.
`transport.openTransfer()` returns a Lease: `acquire()` resolves when the node
grants, `release()` gives it back exactly once, and nothing else in the client
speaks to the node about slots. Whether a node hands out slots is read from the
handshake ack rather than guessed from a timeout — "no answer yet" and "this
node will never answer" are indistinguishable in time, and guessing wrong either
stalls every download or defeats the cap.
Two things exist only because a queue can lie: a watchdog re-asks when a pushed
grant does not arrive (the node is idempotent on `tr`, so asking again is free),
and a grant for a transfer the page has forgotten is handed straight back rather
than held until the node's deadline.
The slot is asked for **after** there is somewhere to write, and that ordering
is load-bearing: opening a target takes thirty seconds of streamed-download
timeouts, or as long as somebody leaves a Save As dialog open, and a grant not
taken up in time is revoked. Moving it earlier looked better and broke three
downloads into one. Pinned by a test.
The panel groups by state — running, waiting, finished — rather than re-sorting a
flat list, so a row moves only when its own state does. The ETA is withheld until
the speed window holds real measurement: a figure from the first two chunks
swings between four seconds and an hour, and people plan around the first number
they see. One live region announces state changes and not progress.
Three silent paths closed on the way: a download refused for want of a user
gesture (a browser grants one file picker per gesture, and downloading three
files is one gesture) now falls back to the streamed path, which needs none; a
click with no connection says so instead of doing nothing at all; and a queued
transfer counts as busy, so a transport is never closed under one that is
waiting for a grant that could then never arrive.
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/locales')
10 files changed, 120 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index 8ed1ef0..f5c0c78 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -162,6 +162,8 @@ export default { 'group.mkdir': 'Neuer Ordner', 'group.mkdir_prompt': 'Name des neuen Ordners', 'group.mkdir_offline': 'Nicht mit dem Node verbunden.', + 'group.download_offline': 'Keine Verbindung zum Node — der Download kann nicht starten. Die Verbindung wird automatisch wiederhergestellt; versuchen Sie es gleich erneut.', + 'group.download_write_stalled': 'Die Datei wird nicht mehr auf die Festplatte geschrieben ({seconds} s ohne Fortschritt). Der Download wurde abgebrochen statt hängen gelassen; versuchen Sie es erneut.', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -576,6 +578,16 @@ export default { 'transfers.open': 'Öffnen', 'transfers.done': 'Abgeschlossen', 'transfers.cancelled': 'Abgebrochen', + 'transfers.waiting_own_slots': 'Wartet — Ihre Plätze sind belegt', + 'transfers.waiting_node': 'Wartet — {n} davor', + 'transfers.summary': '{running} laufend · {waiting} wartend', + 'transfers.group_running': 'Laufend', + 'transfers.group_waiting': 'Wartend', + 'transfers.group_finished': 'Abgeschlossen', + 'transfers.cancel_one': '{name} abbrechen', + 'transfers.eta_seconds': 'noch {n} s', + 'transfers.eta_minutes': 'noch {n} Min.', + 'transfers.eta_hours': 'noch {n} Std.', 'transfers.failed': 'Fehlgeschlagen', 'group.select': 'Auswählen', 'group.select_done': 'Fertig', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index 285ff7a..e3ed844 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -162,6 +162,8 @@ export default { 'group.mkdir': 'New folder', 'group.mkdir_prompt': 'New folder name', 'group.mkdir_offline': 'Not connected to the node.', + 'group.download_offline': 'Not connected to the node — the download cannot start. It reconnects on its own; try again in a moment.', + 'group.download_write_stalled': 'The file stopped being written to disk ({seconds}s with no progress). The download was stopped rather than left hanging; try it again.', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -692,6 +694,16 @@ export default { 'transfers.open': 'Open', 'transfers.done': 'Finished', 'transfers.cancelled': 'Cancelled', + 'transfers.waiting_own_slots': 'Waiting — your slots are busy', + 'transfers.waiting_node': 'Waiting — {n} ahead', + 'transfers.summary': '{running} running · {waiting} waiting', + 'transfers.group_running': 'Running', + 'transfers.group_waiting': 'Waiting', + 'transfers.group_finished': 'Finished', + 'transfers.cancel_one': 'Cancel {name}', + 'transfers.eta_seconds': '{n}s left', + 'transfers.eta_minutes': '{n} min left', + 'transfers.eta_hours': '{n} h left', 'transfers.failed': 'Failed', 'group.select': 'Select', 'group.select_done': 'Done', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index f74c763..c509599 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -160,6 +160,8 @@ export default { 'group.mkdir': 'Nueva carpeta', 'group.mkdir_prompt': 'Nombre de la nueva carpeta', 'group.mkdir_offline': 'Sin conexión con el nodo.', + 'group.download_offline': 'Sin conexión con el nodo — la descarga no puede empezar. Se reconecta sola; inténtelo de nuevo en un momento.', + 'group.download_write_stalled': 'El archivo dejó de escribirse en el disco ({seconds} s sin avance). La descarga se detuvo en lugar de quedarse colgada; inténtelo de nuevo.', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -572,6 +574,16 @@ export default { 'transfers.open': 'Abrir', 'transfers.done': 'Terminada', 'transfers.cancelled': 'Cancelada', + 'transfers.waiting_own_slots': 'En espera — sus espacios están ocupados', + 'transfers.waiting_node': 'En espera — {n} por delante', + 'transfers.summary': '{running} en curso · {waiting} en espera', + 'transfers.group_running': 'En curso', + 'transfers.group_waiting': 'En espera', + 'transfers.group_finished': 'Finalizados', + 'transfers.cancel_one': 'Cancelar {name}', + 'transfers.eta_seconds': 'quedan {n} s', + 'transfers.eta_minutes': 'quedan {n} min', + 'transfers.eta_hours': 'quedan {n} h', 'transfers.failed': 'Fallida', 'group.select': 'Seleccionar', 'group.select_done': 'Listo', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 0c5103f..49a1071 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -161,6 +161,8 @@ export default { 'group.mkdir': 'Nouveau dossier', 'group.mkdir_prompt': 'Nom du nouveau dossier', 'group.mkdir_offline': 'Non connecté au nœud.', + 'group.download_offline': 'Pas de connexion au node — le téléchargement ne peut pas démarrer. La reconnexion est automatique, réessayez dans un instant.', + 'group.download_write_stalled': 'L\'écriture du fichier sur le disque s\'est arrêtée ({seconds} s sans progression). Le téléchargement a été interrompu plutôt que laissé en suspens ; réessayez.', 'device.add_title': 'Ce navigateur n’est pas encore lié à ce nœud', 'device.add_hint': 'Votre compte est connu ici, mais ce navigateur détient une autre clé. Approuvez-le depuis un appareil déjà lié — sans passer par l’opérateur.', 'device.add_btn': 'Obtenir un code de liaison', @@ -575,6 +577,16 @@ export default { 'transfers.open': 'Ouvrir', 'transfers.done': 'Terminé', 'transfers.cancelled': 'Annulé', + 'transfers.waiting_own_slots': 'En attente — vos slots sont occupés', + 'transfers.waiting_node': 'En attente — {n} devant', + 'transfers.summary': '{running} en cours · {waiting} en attente', + 'transfers.group_running': 'En cours', + 'transfers.group_waiting': 'En attente', + 'transfers.group_finished': 'Terminés', + 'transfers.cancel_one': 'Annuler {name}', + 'transfers.eta_seconds': '{n} s restantes', + 'transfers.eta_minutes': '{n} min restantes', + 'transfers.eta_hours': '{n} h restantes', 'transfers.failed': 'Échec', 'group.select': 'Sélectionner', 'group.select_done': 'Terminé', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index ebac541..8f1ef1e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -161,6 +161,8 @@ export default { 'group.mkdir': 'Nuova cartella', 'group.mkdir_prompt': 'Nome della nuova cartella', 'group.mkdir_offline': 'Non connesso al nodo.', + 'group.download_offline': 'Nessuna connessione al nodo — il download non può iniziare. La riconnessione è automatica, riprovi tra poco.', + 'group.download_write_stalled': 'Il file ha smesso di essere scritto su disco ({seconds} s senza progressi). Il download è stato interrotto invece di restare bloccato; riprovi.', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -575,6 +577,16 @@ export default { 'transfers.open': 'Apri', 'transfers.done': 'Completato', 'transfers.cancelled': 'Annullato', + 'transfers.waiting_own_slots': 'In attesa — i suoi posti sono occupati', + 'transfers.waiting_node': 'In attesa — {n} prima', + 'transfers.summary': '{running} in corso · {waiting} in attesa', + 'transfers.group_running': 'In corso', + 'transfers.group_waiting': 'In attesa', + 'transfers.group_finished': 'Completati', + 'transfers.cancel_one': 'Annulla {name}', + 'transfers.eta_seconds': '{n} s rimanenti', + 'transfers.eta_minutes': '{n} min rimanenti', + 'transfers.eta_hours': '{n} h rimanenti', 'transfers.failed': 'Non riuscito', 'group.select': 'Seleziona', 'group.select_done': 'Fine', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index 1b49ddb..7ea9789 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -159,6 +159,8 @@ export default { 'group.mkdir': '新しいフォルダー', 'group.mkdir_prompt': '新しいフォルダー名', 'group.mkdir_offline': 'ノードに接続していません。', + 'group.download_offline': 'ノードに接続していません — ダウンロードを開始できません。再接続は自動で行われます。少し待って再試行してください。', + 'group.download_write_stalled': 'ファイルのディスクへの書き込みが止まりました({seconds} 秒間進みません)。ぶら下がったままにせず中止しました。もう一度お試しください。', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -567,6 +569,16 @@ export default { 'transfers.open': '開く', 'transfers.done': '完了', 'transfers.cancelled': 'キャンセル済み', + 'transfers.waiting_own_slots': '待機中 — 自分の枠がすべて使用中です', + 'transfers.waiting_node': '待機中 — 前に {n} 件', + 'transfers.summary': '実行中 {running} · 待機中 {waiting}', + 'transfers.group_running': '実行中', + 'transfers.group_waiting': '待機中', + 'transfers.group_finished': '完了', + 'transfers.cancel_one': '{name} をキャンセル', + 'transfers.eta_seconds': '残り {n} 秒', + 'transfers.eta_minutes': '残り {n} 分', + 'transfers.eta_hours': '残り {n} 時間', 'transfers.failed': '失敗', 'group.select': '選択', 'group.select_done': '完了', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 7cf4cd4..74cd269 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -162,6 +162,8 @@ export default { 'group.mkdir': 'Nieuwe map', 'group.mkdir_prompt': 'Naam van de nieuwe map', 'group.mkdir_offline': 'Niet verbonden met de node.', + 'group.download_offline': 'Geen verbinding met de node — de download kan niet starten. Er wordt automatisch opnieuw verbonden; probeer het zo weer.', + 'group.download_write_stalled': 'Het bestand wordt niet meer naar schijf geschreven ({seconds} s zonder voortgang). De download is gestopt in plaats van te blijven hangen; probeer het opnieuw.', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -576,6 +578,16 @@ export default { 'transfers.open': 'Openen', 'transfers.done': 'Voltooid', 'transfers.cancelled': 'Geannuleerd', + 'transfers.waiting_own_slots': 'Wacht — uw plaatsen zijn bezet', + 'transfers.waiting_node': 'Wacht — {n} ervoor', + 'transfers.summary': '{running} bezig · {waiting} wachtend', + 'transfers.group_running': 'Bezig', + 'transfers.group_waiting': 'Wachtend', + 'transfers.group_finished': 'Voltooid', + 'transfers.cancel_one': '{name} annuleren', + 'transfers.eta_seconds': 'nog {n} s', + 'transfers.eta_minutes': 'nog {n} min', + 'transfers.eta_hours': 'nog {n} u', 'transfers.failed': 'Mislukt', 'group.select': 'Selecteren', 'group.select_done': 'Klaar', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index 6edffd5..584e5f2 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -165,6 +165,8 @@ export default { 'group.mkdir': 'Nowy folder', 'group.mkdir_prompt': 'Nazwa nowego folderu', 'group.mkdir_offline': 'Brak połączenia z węzłem.', + 'group.download_offline': 'Brak połączenia z węzłem — pobieranie nie może się rozpocząć. Połączenie wróci samo; proszę spróbować za chwilę.', + 'group.download_write_stalled': 'Plik przestał być zapisywany na dysk ({seconds} s bez postępu). Pobieranie zostało przerwane, zamiast wisieć w nieskończoność; proszę spróbować ponownie.', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -588,6 +590,16 @@ export default { 'transfers.open': 'Otwórz', 'transfers.done': 'Zakończony', 'transfers.cancelled': 'Anulowany', + 'transfers.waiting_own_slots': 'Oczekiwanie — Twoje miejsca są zajęte', + 'transfers.waiting_node': 'Oczekiwanie — {n} przed', + 'transfers.summary': '{running} w toku · {waiting} oczekuje', + 'transfers.group_running': 'W toku', + 'transfers.group_waiting': 'Oczekuje', + 'transfers.group_finished': 'Zakończone', + 'transfers.cancel_one': 'Anuluj {name}', + 'transfers.eta_seconds': 'pozostało {n} s', + 'transfers.eta_minutes': 'pozostało {n} min', + 'transfers.eta_hours': 'pozostało {n} godz.', 'transfers.failed': 'Nie powiódł się', 'group.select': 'Zaznacz', 'group.select_done': 'Gotowe', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index 3a6fa22..b034117 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -162,6 +162,8 @@ export default { 'group.mkdir': 'Nova pasta', 'group.mkdir_prompt': 'Nome da nova pasta', 'group.mkdir_offline': 'Sem conexão com o nó.', + 'group.download_offline': 'Sem conexão com o nó — o download não pode começar. Ele reconecta sozinho; tente de novo em instantes.', + 'group.download_write_stalled': 'O arquivo parou de ser gravado no disco ({seconds}s sem progresso). O download foi interrompido em vez de ficar travado; tente de novo.', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -574,6 +576,16 @@ export default { 'transfers.open': 'Abrir', 'transfers.done': 'Concluída', 'transfers.cancelled': 'Cancelada', + 'transfers.waiting_own_slots': 'Aguardando — seus espaços estão ocupados', + 'transfers.waiting_node': 'Aguardando — {n} na frente', + 'transfers.summary': '{running} em andamento · {waiting} aguardando', + 'transfers.group_running': 'Em andamento', + 'transfers.group_waiting': 'Aguardando', + 'transfers.group_finished': 'Concluídos', + 'transfers.cancel_one': 'Cancelar {name}', + 'transfers.eta_seconds': 'faltam {n} s', + 'transfers.eta_minutes': 'faltam {n} min', + 'transfers.eta_hours': 'faltam {n} h', 'transfers.failed': 'Falhou', 'group.select': 'Selecionar', 'group.select_done': 'Concluir', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index f43ef72..ffe2cb0 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -158,6 +158,8 @@ export default { 'group.mkdir': '新建文件夹', 'group.mkdir_prompt': '新文件夹名称', 'group.mkdir_offline': '未连接到节点。', + 'group.download_offline': '未连接到节点 — 无法开始下载。连接会自动恢复,请稍后重试。', + 'group.download_write_stalled': '文件停止写入磁盘({seconds} 秒无进展)。已中止下载而不是让它一直卡住,请重试。', 'device.add_title': 'This browser is not linked to this node yet', 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.', 'device.add_btn': 'Get a linking code', @@ -555,6 +557,16 @@ export default { 'transfers.open': '打开', 'transfers.done': '已完成', 'transfers.cancelled': '已取消', + 'transfers.waiting_own_slots': '等待中 — 您的通道已占满', + 'transfers.waiting_node': '等待中 — 前面还有 {n} 个', + 'transfers.summary': '进行中 {running} · 等待中 {waiting}', + 'transfers.group_running': '进行中', + 'transfers.group_waiting': '等待中', + 'transfers.group_finished': '已完成', + 'transfers.cancel_one': '取消 {name}', + 'transfers.eta_seconds': '剩余 {n} 秒', + 'transfers.eta_minutes': '剩余 {n} 分钟', + 'transfers.eta_hours': '剩余 {n} 小时', 'transfers.failed': '失败', 'group.select': '选择', 'group.select_done': '完成', |