<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/test_transfers.py, branch 0.7</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.7</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.7'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-08-23T13:15:35Z</updated>
<entry>
<title>feat(hub): split the group UI into a pluggable "applications" architecture</title>
<updated>2026-08-23T13:15:35Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-23T13:15:35Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=9f02ee2c09652abf1308bdfa4a3eec4e9ca9ac83'/>
<id>urn:sha1:9f02ee2c09652abf1308bdfa4a3eec4e9ca9ac83</id>
<content type='text'>
GroupPage's 6620-line app.js carried Chat and Files wedged in directly, with
no way to add another group-level app without touching the shell itself. It
is now app.js (routing, non-group pages) plus nine focused files — apps.js
(the registry), chat-app.js, files-app.js, video-player.js, group-page.js
(the shell), group-settings.js, hub-client.js, icon.js and file-utils.js —
with docs/apps.md as the checklist for adding one (Videos/Music/Photos are
sketched there, not built).

Node side gained the matching enablement mechanism, mirroring
member_upload exactly: a roster setting, a signed apps_enabled op enforced
by _has_admin_authority, exposed in the handshake ack. Operators toggle
applications per group from Settings, which also gained a small reorder:
Invite, Pairing, Applications, Shared directories, Uploads, danger zone,
Your devices, Members.

Two bugs surfaced during the split, both missing an import across the new
file boundary and invisible to node --check or a module-load probe since
they only throw when the code path actually runs:

- group-page.js called onRefreshAuth on a stale-token handshake rejection,
  but app.js never imported refreshAccessToken from hub-client.js — so a
  brand new member (including a group's own creator) hit "Not a member of
  this group" and the retry silently failed, throwing before it could
  refresh the token.
- chat-app.js called getLocale() for message timestamps without importing
  it from i18n.js. Opening Chat on a group with real messages threw mid-
  render; uncaught, that appears to wedge Preact's render scheduler, so
  every button on the page stopped responding until reload.

Caught the second class of bug with a proper no-undef audit across all
split files (a temporarily installed ESLint 9, since the system one is too
old to parse this codebase's syntax) rather than trusting grep. 827 tests
pass; 6 new ones cover the apps_enabled policy.

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_016SF6RKNBKg9qejmoMJ9ybA
</content>
</entry>
<entry>
<title>fix(ui): name the section after its button, drop the folder slash, colour the widget</title>
<updated>2026-08-18T15:11:00Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-18T15:11:00Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=2ef5498ab1509a93691b87b4cc5d9b52bb3f52dc'/>
<id>urn:sha1:2ef5498ab1509a93691b87b4cc5d9b52bb3f52dc</id>
<content type='text'>
**"Zone sensible" said nothing.** The heading is now the name of the action in
it — "Quitter le groupe", or "Supprimer le groupe" for the owner, who sees a
different button. Worth stating because it is not quite what was asked for: a
fixed "Quitter le groupe" would have sat above a delete button for whoever owns
the group. The red goes with it; only the button is red, which is where the
warning belongs. `members.danger_title` is gone from all ten locales rather than
left behind unread.

**A folder name no longer ends in a slash.** The folder icon in the cell beside
it already says what it is.

**The transfers widget turns green while transfers run.** The badge counts them,
but a count has to be read; colour is what carries from across the room, which
is the point of a widget in the nav bar rather than on the page. Derived from
the live list on every render, so there is no state that can forget to clear
when the last transfer ends.

The class is set in `app.js` and coloured in `style.css` — either alone does
nothing and neither fails loudly, so there is a test for each half.

848 tests pass.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(files): transfers that outlive the page, and selection instead of per-row menus</title>
<updated>2026-08-15T11:13:08Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-08-15T11:13:08Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=41e2b79cb1bc9d188853aeff5a55cd2237268587'/>
<id>urn:sha1:41e2b79cb1bc9d188853aeff5a55cd2237268587</id>
<content type='text'>
Downloads and uploads were state inside GroupPage. Leaving a group
unmounted the component, its cleanup closed the DataChannel, and a
half-written file was all you had — which is also why only one thing
could be in flight at a time.

They live in a module-level store now. A group page hands its transport
over on the way out rather than closing it, and the last transfer using
it closes it; signing out is the one thing that cancels everything,
because those transfers are moving data on a token about to stop being
ours. The store is plain JavaScript with no browser globals, so
test_transfers.py runs it under Node and pins the parts that are timing
and lifetime rather than markup: that a cancel stops the work instead of
greying out a row, that a stalled transfer reads as stalled rather than
reporting its own historical average, and that a released transport is
closed by the last transfer and not before.

The widget by the bell shows each transfer with its rate and a cancel
button, so the Files panel no longer carries progress bars — you can
watch a 40 GB archive from the chat, or from another group.

Selection replaces the per-row menu: a Select toggle puts checkboxes on
files and folders, and ⋮ Actions acts on what is ticked. Ticks survive
walking into another folder, so a selection can span directories.
Downloads start together and run together. Videos offer Play only — View
did the same thing, which is the sort of duplication that makes people
wonder what the difference is.

Uploads had to become parallel-safe for any of this to mean anything:
their acks were matched by arrival order, so two at once credited each
other's progress. The node names the file in every ack, so they are keyed
by name now — with the same file twice refused, since the node keys its
own upload state that way too.

Two mistakes worth recording. The selection column went into the body
rows and not the header, because that edit matched nothing and I had not
made it assert; the columns were misaligned until a screenshot showed it.
And the Actions menu opened leftwards from a button at the right edge of
the toolbar, half of it off-screen.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
