Conversation
- Route on-device commands over the companion protocol: clock/clock sync, ver, board, advert/floodadv, reboot, and typed get/set - Fall bare "set <key> <value>" and "get <key>" through to the companion custom-var frames when the key is not a typed setting, with "get custom" to dump all vars and a leading underscore escape hatch for name collisions - Add getTime/setTime session ops and handle device errors from custom-var frames - Gate destructive commands like reboot behind a yes/no confirmation - Extend tab-completion with typed keys plus live custom-var names on bare get/set, prefetched once per connection - Localize across 9 locales; cover parser, executor, and completion with tests
ZephCore exposes a virtual "V-contact" that streams through GET_CONTACTS but occupies no real contact-table slot. MC1 treated it like an ordinary contact, so its own cleanup paths acted on it: the full-sync prune and the overwrite-oldest (0x8F) handler deleted the V row and its messages, a `set v.contact off` push spuriously cleared the storage-full flag, config import counted its phantom slot against capacity, and the remove/delete paths could CMD_REMOVE it and turn the firmware feature off. - Add VContactIdentity to derive the ZephCore loopback admin contact key - Skip the full-sync prune and the overwrite-oldest storage-full clear for the V-contact - Exclude the V-key from config-import capacity accounting - Block remove/removeLocal/bulk-remove and hide the remove/delete UI for the V-contact - Drop the stale V-contact after an identity rotation
refactor: chat page Rework the chat view for a full-height, edge-to-edge layout on iOS 26 and clean up the overall styling. - make the chat window take full height / edge-to-edge on iOS 26 - restyle the compose bar and icons; send button pops in when a message is ready to send (Signal-style) - add a liquid-glass header bubble for the chat name - unify chat bubbles with in-bubble state/footer and a send-count footer - add date dividers and a new-messages divider; open chats scrolled to it - migrate to swiftui-messaging-ui and pin MessagingUI to an exact revision - add cache prewarming and build link previews synchronously to avoid shimmer/layout flashes on first view - improve chat scrolling, keyboard dismiss, and gesture handling
- Chats could open above the divider or jump to the bottom right after opening - The library now anchors the divider until the user scrolls - The first geometry report consumes the divider target instead of arming append-follow - Retire the target after use so a theme rebuild doesn't re-jump to it - Pin MessagingUI to the fork revision with the anchor window
- Read the firmware version from the owner-info response. Nodes predating owner-info return an empty string, which maps to nil so the row stays hidden rather than showing a blank value - Keep the firmware row out of the owner-text branch: firmware comes back even when owner_info is empty - Add a footer to the section, matching the other telemetry sections - Test the firmware capture and the empty-string case
- The prime paths in AppState.prefetchConversation and ChatPrewarmRefresher built a whole ChatViewModel just to throw it away. They now use ChatTimelinePrimer, so the .prime writer role has one caller. - Move per-message bake state (preview/image caches, New Messages divider) to ChatMessageBakeState, which is not Observable: redraw is decided by the Equatable MessageItem. - primeInitialMessages and primeInitialChannelMessages ran the same fetch, divider, filter, write and bake sequence once per conversation kind. Collapse that core into ChatTimelinePopulator, which the primer and both prime methods call; the channel path keeps its own extras. - Sender resolution takes a ChatSenderTables snapshot; the observed mention-picker tables stay on the view model. - ChatViewModel.configure drops its role parameter and the writerRole property; bindCoordinator always binds .interactive. - The primer installs rebakeRow as its renderItemRebuilder, so a reload draining while it owns the writer rebakes the rows it refreshes. - ChatTimelinePrimerTests cover the open-races-prime case, channel sender names, a prime denied while open, and a reload draining mid-prime.
recoverOrphanedLoadingState reset previewStates to .idle without rebuilding the row's item. A caller that recovers and then returns without fetching leaves an item built from .loading, which carries no fetch-task id, so the cell never re-arms and the row shimmers with no fetch behind it. Reset and rebake together, matching the sibling resetLoadingStateAfterCancellation.
The channel type repeated what the title and avatar already show. The region is the fact worth the space: it decides who actually receives a flood send. - Show "Region: <name>" as the second line, with "(default)" when the region comes from the radio's default - Hide the line entirely when no region resolves - Center the title and second line in the shared navigation header - Drop the subtitle accessibility label, now that there's no separator for VoiceOver to misread
Add NodeLocationFix.primaryFix(from:) to extract the first valid GPS LPP data point, dropping (0,0) null-island and out-of-range fixes via a new CLLocationCoordinate2D.isValidFix helper. Add latitude/longitude columns to NodeStatusSnapshot and its DTO, both nil unless a valid fix was captured.
- Capture the node's primary GPS position on status snapshots and expose the live location - Thread that location through the snapshot capture path and node status route - Add NodeLocationMapView plus a location history section with a path-preview thumbnail - Draw the plotted path polyline and pins in the map snapshot, framed to their bounds - Build node-location paths through LocationPathMapBuilder with camera-bounds fitting - Add RemoteNodes location map localization keys across all nine locales - Cover location capture and path building with unit tests
- Capture and persist GPS altitude with each node fix, dropping values outside a plausible range - Add a report list under the History map: one row per fix with recency, timestamp, coordinates, and altitude - Show a callout when a report pin on the full-screen map is tapped - Tapping a report row opens the map with that pin already selected and its callout up - Tighten the initial and center-all camera so the path fills the screen instead of sitting in the middle - Seed a sample GPS track in the simulator so the History screens have content
- Nav bar never observes the timeline's collection view, so it stayed on its default transparent scroll-edge appearance and the title read over passing messages. Pin it opaque, using the theme canvas. - Compose field takes an outline instead of a grey fill. Grey on an opaque bar is near-invisible in light mode and clashes with themed canvases. - glassTitleCapsule is now contentScrollsUnderBar; it picks between treatments rather than requesting a capsule. - iOS 26 untouched.
- Sender name now sits 3pt above its bubble instead of 5pt - Room bubbles match the main chat: they had a 4pt gap and indented the name to the bubble's text inset instead of its leading edge - Both bubbles place the name through one senderNamePlacement helper in SenderNameMetrics, so the geometry cannot drift between them
- a warm coordinator kept the previous session's divider, and a reopen fed it to the list as the one-shot scroll target before the fresh page landed - withhold the timeline until this session's divider resolves in the on-screen items; present at the bottom when there's no backlog or the load settles without one - delete ChatRenderState.newMessagesDividerItemID so the stale read is unwritable - test the warm-reopen composition through the real bake path
- position the initial scroll target with real input-bar chrome insets
- SwiftUI can keep a popped destination's @State alive, so the closed chat's view model never deallocated and its interactive bind kept denying every arrival-time prime refresh - performCleanup now vacates the slot via releaseWriter(owner:); the load task rebinds on the next appearance - release is identity-checked so a stale view's teardown can't evict a successor that already bound
- the pushed DTO's count can go stale during the push transition, misplacing the New Messages divider even on a cold open - populate re-fetches the count from the store (it also sizes the first page) and falls back to the DTO if the lookup misses - new ChatTimelineFreshnessTests: populate freshness, the production prewarm hook chain, and writer release on close
…bly module - ChatTimeline owns the bake state, the writer capability, and the populate/rebake entry points; ChatViewModel and ChatTimelinePrimer drive one implementation, role-parameterized. Rebake hooks install atomically with bind and capture the timeline weakly, replacing the duplicated rebuild closures in the view model and the primer - loadOlder() owns the paging sequence: spinner bracket, offset math, reaction filter, dedupe against admitted rows, prepend + cross-page reorder, and the spinner-before-rebake ordering; the view model wrapper keeps sender registration, reaction indexing, and the error banner - admit(_:) owns dedupe against the loaded window and the same-frame append of message plus baked render item; appendMessageIfNew keeps only the channel-sender bookkeeping - stageOpen captures the open's unread count and resets the per-open latches and divider, so the anchor can only come from this session's bake; firstSnapshot/consumeAnchor own the withhold-vs-present decision and the one-shot latch, and the conversation view drops its anchor @State triad - apply(_:) pairs every bake mutation with the rebake of its affected rows in one call, so a fetch result can never land without its redraw; clearBakeState/removeBakeState own conversation-switch and message-deletion cleanup - applyStatusUpdate, enqueueReload, removeMessage, and updateMessage replace raw writer calls across the view model extensions; the writer stays reachable only for the shared reaction-indexing core and test seams - timeline.conversation is assigned by stageOpen/open and directly on DTO refresh, replacing the currentContact/currentChannel didSet mirrors; a channel refresh now updates currentChannel and the timeline alongside the contact path - ChatTimelineTests exercise open/loadOlder through the module surface
- deleteContact now removes the contact's messages, reactions, repeats, and pending sends in the same transactional save - the cascade keys on the contact ID value, so orphaned data is cleaned up even when the row is already gone - drop the paired deleteMessagesForContact call at all five delete sites - mirror the cascade in MockPersistenceStore and add real-store cascade tests
- Move the transient-vs-escalate classification out of BLEStateMachine into a value-typed ReconnectPolicy: failure tallies, encryption-timeout majority, discovery-extension budget, error mapping, and bond grace - Give each question its own resolve method so no call site carries an unreachable fallback arm - Seed and push bond-verification dates from ConnectionManager instead of the provider closure, keyed off the store's bond slot since a WiFi connection overwrites the connection slot without touching the bond - Drop the in-memory verification in clearPersistedConnection so a forget/re-pair can't inherit stale grace - Merge the three stringly diagnostic properties into one linkDiagnostics snapshot read in a single actor hop; BLEPhaseKind keeps log text intact - Port the pure classification suites to ReconnectPolicyTests; the driven-machine suites stay as integration coverage
- give flood timeouts extra grace for the response's return trip across the mesh - don't let the timeout tail overwrite a response that lands right at sleep expiry - re-present the discovery alert when a response arrives while the timeout alert is up; alert text is frozen at presentation - add view model tests for discovery response handling
- match the response's key prefix against the open contact before resolving the view's discovery, so a straggler from a discovery on another contact can't finish this one - an empty wire prefix never matches rather than matching every key
- Drop 110 unused SwiftGen keys across all 9 locales - Remove 4 stringsdict-only plurals; delete empty Chats.stringsdict - Keep live partial danger-zone string and Onboarding region.title - Regenerate L10n.swift
- Ship it.lproj for app and widget string families - List Italian in TRANSLATIONS.md and Intent locale tests
Firmware's suggested timeout only models the direct path, but repeater replies flood until a return path is learned. Floor status/telemetry/ neighbor timeouts at 12s (per SessionConfiguration, so tests can pass 0).
* Add contact profile pictures Contacts can now have a custom profile picture set from Photos or Files, overriding the auto-generated initials avatar. Co-authored-by: romeo <romeo@romeos-Mac-mini.local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Italian translation #371
- Stopping a session cancels its receive loop, which killed the transport's shared AsyncStream storage. Every rebuild over a live link then read a dead stream and timed out its handshake, so auto-reconnect stayed down until the app came to the foreground - BLEStateMachine.renewDataStream() re-vends the stream; rebuildSession calls transport.refreshDataStream() between stopping the old session and building the new one - New regression gate rebuilds over a single-slot responder transport and fails without the refresh call
- Forget clears only that radio's bond stamp; holder-matched store clear keeps other radios' shields - Keep a live GATT link through a few app-layer rebuild failures so background recovery can still wake - Refresh bond age from RSSI only while the app session is live; never create a stamp from RSSI alone - Cover preserve budget, claim single-flight, and dead-stack bond-shield gates in tests
- present the first snapshot only when the on-screen item was baked with the divider row, not when the divider message id is in the item index - skip the divider when an open staged zero unread: it already presented at the bottom, and a late divider grows a visible row and pushes the last message under the input bar - firstSnapshotDecision takes a scalar dividerRowOnScreen fact; the call site computes it from items and the index map - pagination tests now stage the open like ChatConversationView does
- Point path contact load at offlineDataStore instead of live services - Clear hop-name caches when the store is nil - Seed the nil-store unit test so clear is actually asserted
- Add both packages to the Settings Acknowledgements list - Include Apache 2.0 (MessagingUI) and BSD 2-Clause (MapLibre) license texts
- Derive PSK from secretless #name URLs; explicit secret still wins - Optional region_scope as local flood preference only - Share/QR always emit name+secret; region only for .region - Tests for golden hashes, precedence, and existing-channel open
- Drop txPower state, UI row, and get/set tx CLI calls - Load and apply radio with get/set radio only - Update footer and apply-failed copy in all locales - Cover the single-command path in view model tests
- Add Settings → Maps for basemap appearance and the show-discovered toggle - Union Discover pins with nodes on the map, with callout Add and coord checks - Back up the new prefs and cover load, appearance, and import in tests
Make multi-hop status, telemetry, neighbours, and login reliable by retransmitting inside the waiter instead of path-reset/flood fallback and late salvage. - Retransmit binary requests until a reply or the overall budget elapses - Space resends at max(floor, 2× firmware suggestedTimeoutMs) - Accept only the latest messageSent tag (companion single pending slot) - Prefer dedicated status/telemetry commands where available - Timeout is terminal for the section: no salvage-to-UI or grace spinner - Map MeshCoreError.timeout to RemoteNodeError.timeout for section errors - Keep one-shot path reset only for CLI
- Remove per-row dwell prewarm that started and cancelled tasks on every LazyVStack cell appear - Keep top-N warm after load so recent chats still open settled - Add regression tests for no per-row warm task and hosted scroll budget
- Treat timeout as terminal; do not re-present success after failure - Wait at least 20s; flood grace 8s for return-leg jitter - Resend while waiting at max(5s, 2× firmware est) - Cover terminal late response and budget math in tests
- Add Support me above Continue; open Support settings then dismiss - Push Settings detail on compact from selectedSetting - Ship 1.3 notes: improved chats, contact photos, discovered nodes - Drop pre-1.3 keys; translate all new strings
- Present a sheet when a tip or All Themes purchase verifies - Gate on purchase returning true only; remove tip checkmark flash - Localize title and body in all 10 locales
- Channel outgoing rows no longer force the new-sender flag - Same-cluster follow-ups use 2pt top padding on both sides - Cover consecutive outgoing in DisplayFlagsTests
- Add Filter menu on main, trace path, and neighbor SNR maps - Persist MapFilterState per host; migrate the old discovered toggle - Re-filter warm pin caches without a full reload - Keep path hops visible under filters; bind hops by full key first
- Replace richerMaps with mapFilters in all locales - Focus copy on favorites, discovered nodes, and node type
- Open GitHub Issues or email from Settings → About → Feedback - Move diagnostics log export onto that screen - Add Feedback strings in all locales
- Add releaseNotesURL on WhatsNewRelease - Show Full release notes link on the sheet - Point v1.3 catalog entry at the GitHub tag
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.