Receivers hard-cap reassembly at MAX_FRAGMENTS_PER_ID (256), but the
generic send path fragmented packets with no caller cap (0xFFFF), so
broadcast file transfers above ~120 KB were fully transmitted yet
undeliverable. FragmentingPacketSender now caps fragmentation at
MAX_FRAGMENTS_PER_ID and reports failure via a new
TransferProgressEvent.failed flag, which surfaces as
DeliveryStatus.Failed in the UI and as a file_send error in the debug
test hook instead of an indefinite wait.
Adds FragmentingPacketSenderTest and a file_oversize mesh-lab scenario
asserting sender-side rejection.
Debug-only broadcast receiver (app/src/debug) exposes mesh operations over
ADB: scan, connect, Noise handshake, DMs, broadcast, announce, file
send/receive with SHA-256 verification, BLE toggle, state dumps, and raw
packet injection. tools/release_gate/mesh_lab.py orchestrates scenarios
(dm, broadcast, file, file_private, raw) on two live devices and emits
evidence JSON.
Align the background location footer with the battery optimization layout so primary, check-again, and skip stay at the same height when navigating between them.
Co-authored-by: Cursor <cursoragent@cursor.com>
Addresses review feedback: the size-cap error was posted to the main
mesh timeline, so a user sending from a private chat or channel never
saw it. rejectIfOversized now posts to the private conversation
(addPrivateMessageNoUnread) or channel (addChannelMessage) the send
originated from, falling back to the main timeline for public sends.
Adds regression tests for both routings.
- MediaSendingManager: surface a chat system message when a picked file
exceeds the ~10 MiB send cap instead of silently dropping the send
(voice/image/file paths), completing the 'user-visible failure'
requirement of the rollout gate
- BinaryProtocol: catch Exception instead of Throwable in decodeCore so
OutOfMemoryError is never swallowed and masked as a parse failure
- docs/file_transfer.md: mark the compressed-expansion rollout gate
resolved; support for legacy >10 MiB compressed transfers is
explicitly ended
Extract PeerColorStyle so each palette owns saturation/value, keeping
hues stable while dark mode stays bright-but-muted and light mode avoids
neon labels. New themes only need to supply their own style.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep disable at the sheet footer so enabling is next to nearby channels while turning location off stays a secondary action.
Co-authored-by: Cursor <cursoragent@cursor.com>
- header star wobbles and its outline turns orange when the peer
favorites you; it only fills once you favorite them back
- mirror the 'favorited you' system notice into the private
conversation (mesh path), matching the main chat
- expose reactive peerFavoritedUs state driven by
FavoritesPersistenceService; keep system notices silent
(no unread badge, read receipt or push)
Show ic_spec_lock_open while idle or handshaking, then Crossfade to the
closed lock on success or failure — timed with the existing tint wash so
the shackle settling reads as one smooth transition.
Co-authored-by: Cursor <cursoragent@cursor.com>
Match private-chat Noise status to the Tor globe treatment: one lock
glyph with an orange pulse while handshaking, then green or red with
smooth tint cross-fades — no sync/recycle swap.
Co-authored-by: Cursor <cursoragent@cursor.com>