The Home "Map" page preview only plotted each trail vertex as a single
pixel, leaving gaps wherever the streaming simplifier collapsed a straight
stretch to its two endpoints — so a route looked like scattered dots instead
of a path. The stale "gfx isn't available this early in the TU" comment no
longer holds: GfxUtils.h is already reachable here, so draw the same
Bresenham line the full Trail map uses.
Extracted the shared "walk points, draw a line, break on TRAIL_FLAG_SEG_START"
loop into gfx::drawTrail() (GfxUtils.h) so the full map and the mini-map
preview don't carry two copies of it; they differ only in their projection
and what (if anything) they draw at a segment break.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Beeper row is now hidden in Leave-only mode (homing only applies when
approaching a target), so drop the "works independently of arrive/leave"
wording and note the Arrive/Both-only availability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The proximity beeper only fires inside the radius and ticks faster toward
the centre — a homing aid that only makes sense when "arrive" is part of the
alert mode. In pure "leave" mode it's contradictory (silent outside, slows
as you near the exit), so hide the Beeper row there and gate the beeper
engine on the mode, so a stale beeper=ON pref carried over from arrive mode
can't keep chirping after switching to leave.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A [LOC] DM to a room server is delivered as a plain DM, not reposted to the
room's members, so a room server can't act as a live-share broadcast target.
The main contact picker already lists CHAT contacts only, but a favourited
room server still showed up in the LEFT/RIGHT quick-cycle — skip it there,
and reject a room in commitPickTargetDM as a backstop if the room-mode list
is ever reachable during target pick.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
setTarget() always set locator_has_target=1; there was no path back to
"none" once a target was picked. Add a synthetic "(none)" entry that's
always first in the target picker/cycle set, backed by new
UITask::clearTarget().
Reorder ICON_MAP_TARGET rows so the flag sits at the top of the pole
rather than mid-staff, reading more clearly as a flag at mini-icon scale.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 1024-point trail buffer (16 KB in .bss, since ui_task is a global)
doubled the static footprint of the GPS trail for little practical gain on
a RAM-tight nRF52 build. Revert CAPACITY to 512 (8 KB) and sync the Tools
docs. Heap headroom is better spent elsewhere; in-stream simplification
already lets 512 vertices cover a long route.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
QuickMsgScreen held the on-device scrollback in two RAM rings, each slot a
full MSG_TEXT_BUF: CH_HIST_MAX=96 + DM_HIST_MAX=64 made the screen object
~31 KB on the heap (allocated at boot, never freed). On the Wio variant
(MAX_CONTACTS=350, OFFLINE_QUEUE_SIZE=256) that left only ~3 KB free heap,
so allocations in the input/menu path failed and entering Diagnostics hung
the device.
Halve the rings to CH_HIST_MAX=48 / DM_HIST_MAX=32, recovering ~14 KB of
free heap (measured 3->17 KB). History is RAM-only (lost on reboot anyway);
the cost is shorter on-device scrollback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
saveContacts/saveChannels/savePrefs truncated the live file up front and
then wrote in place, so a crash, reset or full flash mid-save wiped the
whole file — observed as all contacts disappearing after a UI hang.
Write to a temp file and only swap it in (via LittleFS atomic rename) once
every record has written cleanly; on any failure keep the previous good
file and drop the temp. savePrefs gates the swap on the tail-sentinel write
succeeding (writes return 0 once flash fills, so a good sentinel means the
whole record fit). Contacts/channels live on the multi-MB QSPIFlash, so the
transient second copy fits easily.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switch the screenshot convention from positional numbers (tls_scr_8) to
self-documenting names (locator, locator_picker, map_target, …). Names say what
they show, so a shot can be re-captured or inserted without renumbering, and a
reference can't silently point at the wrong image. Per-doc directories keep the
names unambiguous, so no prefix is needed (overview_oled.png lives in each
screen's own folder).
git mv on every existing PNG + ref updates across all six solo_features docs;
the pending placeholders adopt the same scheme.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reviewed the user-facing solo_features docs for screens/sub-views described in
prose but never shown, and added placeholder image tables (repo's OLED|E-Ink
convention) with "screenshot pending" capture notes:
- Tools: Live Share, Waypoints navigation view, Add-by-coords form, Compass,
Diagnostics, Repeater (tls_scr_11..16).
- Settings: Radio preset popup / frequency digit editor (set_scr_3).
- Clock: Dashboard Config (clk_scr_3).
- Favourites Dial: pin-to-dial contact picker (fav_dial_2).
PNGs just need dropping in under the listed names; tables render as broken
images until then (the repo's existing in-progress convention).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Locator: widened target picker (favourites + any contact with a resolvable
position, last-advertised fallback, age tags), "Set as target" shortcuts from
Nearby/Waypoints, and the target flag on the map. README "Geo-alert" bullet
renamed/expanded to Locator.
- Trail: 512 -> 1024 points, plus a note that the track is simplified as it's
recorded.
- Map: nearest-contact distance moved to the status line as an arrow; bottom-
left now always shows the scale tick; target flag noted on Map + Trail Map.
- Left placeholder screenshot tables (tls_scr_8/9/10) with "pending" notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Setting a Locator/Nav target had no spatial feedback — you could arm a
destination but never see where it is. Draw it as a flag marker (ICON_MAP_TARGET)
on both the home Map preview and the full Trail map, folded into each view's
bounding box so a destination you set never sits off-frame, and drawn last so it
stays legible atop a coincident waypoint or contact.
activeTargetPos() resolves the target once (waypoint coords, or a person via
resolvePersonPos), gated only on a target being set — independent of whether the
Locator alert is enabled — so it doubles as the engine's resolver
(locatorDistance now delegates to it).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three notions of "set a target" had drifted apart: LocatorScreen's picker
hand-wrote the prefs fields and deferred the save, while the Nearby/Waypoints
popups wrote the same fields again and saved immediately, and the live>last-
advertised position precedence was duplicated between the engine and the picker.
- setTarget() is now the single definition of the active target (fields +
re-arm). setTargetNow() wraps it for popups (immediate save + confirm); the
Locator screen keeps its deferred save so LEFT/RIGHT cycling doesn't thrash
flash. applyTarget() delegates instead of re-writing the fields.
- resolvePersonPos() is the single live-share / last-advertised precedence,
used by both locatorDistance() and the picker.
- Consistent action verb "Set as target" in Nearby and Waypoints menus.
No behaviour change for the user beyond the relabel; pure consolidation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live Share was the only "leave it on and forget" broadcast mode with no
at-a-glance cue, unlike auto-advert/trail/repeater which all blink in the top
bar. Reuses the diamond already used for a live-tracked contact on the map,
so the glyph already means "sharing position" elsewhere in the UI. Also
fixes the Tools-list "Live Share" row to use ICON_GPS instead of ICON_ADVERT
(which collided with the Auto-Advert row) — currently inert since per-row
icons are disabled there, but correct for whenever they're re-enabled.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
buildTargets() kept iterating every contact after _targets filled up, doing a
lookup + dedup pass per row with no effect (addPersonTarget already caps at
TARGET_MAX). Bound the loop instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The streaming simplifier tested the previous candidate against a line that
re-aimed at the newest sample each step. That test point always sits beside
the moving chord endpoint, where cross-track is ~0, so a long gentle curve
slipped through one sub-min_delta step at a time and collapsed into a single
chord deviating arbitrarily far from the real track.
Switch to a fixed-corridor (Reumann-Witkam) test: anchor the line at the last
committed vertex aimed at the first sample of the run (_dir), and test each
*newest* sample against that fixed direction. Deviation is now bounded to
~min_delta, while a straight road still costs only its two endpoints.
Also count the uncommitted candidate (_pending) in totalDistanceMeters() so
the live distance/speed readout no longer stalls across a long straight run,
where the whole stretch is held as one pending point until a bend commits it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
addPoint() now holds the latest accepted sample as a pending candidate
instead of committing it immediately. It only becomes a stored vertex once a
later sample proves it was a real bend (cross-track deviation from the last
vertex exceeds the min-delta tolerance) — so a straight stretch costs just
its two endpoints no matter how long it is, while turns still get a vertex
roughly every min-delta of deviation. This stretches the 1024-point buffer
over much longer routes without losing shape on curves.
The pending candidate is flushed before any segment break (setActive(false),
setPaused(true), writeTo()) so the last stretch of a run is never silently
dropped just because no bend forced a commit; readFrom()/clear() drop a
leftover candidate that belonged to the session being replaced.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RAM headroom allows it: +8KB resident (TrailStore::_buf), pushing static RAM
to 73.0%/75.0% on GAT562/WioTrackerL1Eink respectively. On-disk snapshot
format is unaffected (count is a uint16_t, already asserted to fit far past
this); an old 512-point snapshot still loads fine on this firmware.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Locator's person target now resolves any known contact's position, not just
an active live [LOC] share: locatorDistance() falls back to the contact's
last-advertised gps_lat/gps_lon when there's no current live share, so a
rarely-updating but stationary node (a repeater, or someone who shared a fix
once) still works as a target. LocatorScreen's target picker is widened to
match (favourites always offered, plus every other contact with a currently
resolvable position), and shows a compact age tag for non-live entries so
staleness is visible.
Also adds a quick "Set Locator target" action directly from Nearby Nodes' and
Waypoints' own popup menus (UITask::setLocatorTarget()), so picking a target
doesn't require a detour through Tools > Locator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure rename, no behavior change: GeoAlertScreen -> LocatorScreen (file +
class), NodePrefs geo_alert_* fields -> locator_*, UITask engine functions
(evaluateGeoAlert/fireGeoAlert/geoAlertDistance/geoProximityBeeper) and state
-> evaluateLocator/fireLocator/locatorDistance/locatorProximityBeeper, Tools
menu entry and screen header text -> "Locator", plus matching docs/README
updates. NodePrefs field renames don't affect on-disk layout (DataStore
serializes by position, not name).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the bottom-left "->dist" text inside the map preview with a proper
arrow mini-icon, moved onto the status line alongside the tracked-node count
(e.g. "Track:3 ->120m"). The map's bottom-left corner now always shows the
scale tick instead of conditionally falling back to it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GeoAlertScreen::openPicker() is reachable directly from handleInput()'s
Target row, bypassing the _prefs null-check every other path in this
screen goes through via activate(). DataStore's loc_share_channel_idx
was the only loc_share_* field without a clamp-on-load, unlike its
sibling index fields (harmless today since getChannel() bounds-checks
it, but inconsistent with the established defensive pattern).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Map preview lost the GPS-fix line and scale bar; document the
->dist label / scale-tick fallback and the new top-bar GPS fix icon
instead.
- Tools is now grouped into collapsible Location/Comms/System
sections (same fold model as Settings) rather than a flat list.
- Live Share's Track loc now also picks up [LOC] shares posted in
room-server messages, keyed by name like channel shares.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The status-bar GPS icon appeared whenever the board had GPS hardware,
even if the user had turned GPS off in settings, implying a search
that wasn't actually happening.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adafruit_USBD_CDC::write() blocks as long as DTR is asserted, even if
the host stopped draining the FIFO. Every writeFrame() call and the
GPX trail export wrote directly to Serial, so a serial monitor left
open but idle could stall the main loop until the 30s watchdog reset
the device.
streamWriteUntil() caps the wait with an absolute deadline shared
across chained writes, and only enters the wait loop when
availableForWrite() is actually exhausted, so a healthy host sees no
behavior change. The GPX export latches after the first stall to
avoid paying the timeout once per write call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Status bar: add a boxed/plain GPS reticle icon reflecting LocationProvider
fix state, shown only on boards that have a location provider. Drop the
now-redundant "Fix:y/n" text from the map page's info line.
- Home minimap: replace the bottom-left scale bar with a "->dist" label to
the nearest live-tracked point when one is available, falling back to a
smaller single-tick scale indicator otherwise. Less likely to be covered
by a plotted point than the old full-width bar, and more useful at a
glance. Drop the now-duplicate nearest-distance text from the map page's
info line.
- Tools screen: comment out per-row/section icons (kept for easy restore) —
they don't fit visually in that list for now.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two receive→UI wiring gaps of the same class as the room-history fix:
- onSignedMessageRecv (room posts) didn't parse [LOC] live-location shares,
while DM (onMessageRecv) and channel (onChannelMessageRecv) both do — so a
guest sharing position in a room never appeared on the map/nearby. Resolve
the signed sender prefix to a name and track by name, mirroring the channel
path (unverified: only a 4-byte prefix is available here).
- UIEventType::roomMessage was defined but never emitted; room notifications
rode on contactMessage and always played the global DM melody. Emit
roomMessage for room posts and handle it explicitly (default DM melody, no
per-sender lookup since the author varies); drop the now-duplicate dead case.
Build verified: WioTrackerL1 solo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
constrain() is a min/max macro and NaN compares false against both bounds,
so a corrupted or layout-shifted prefs file that decodes a float field as
NaN/inf would pass it straight through to setParams() and could hang the
radio at boot (stuck after "Loading...", recoverable only by erasing flash).
Reset freq/bw/airtime_factor/rx_delay_base to safe defaults before the
existing constrain() so a non-finite value can never reach the driver.
Note: a defensive hardening of the boot path, not a confirmed root cause for
the reported brick.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the home carousel's page-indicator dots with a small per-page glyph
set (uniform 5x5, smaller than the menu/status icons), the current page
underlined rather than inverted (an inverted box read too much like the top
status bar). Icons follow buildVisibleOrder, so custom page order and
hide/show toggles from Settings carry over automatically; the row pitch
shrinks to fit when many pages are enabled, and the band geometry scales with
the font (identical layout to the old dots at 1x, clears header/content on 2x
e-ink).
Adds 13 ICON_PG_* glyphs (clock, star, recent, radio, bluetooth, advert,
gps, sensors, settings, map, tools, msg, power).
Builds verified: WioTrackerL1 + e-ink solo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Incoming room-server posts fired the notification and reached the app via
the offline queue, but never appeared when the room was opened directly on
the device: the on-device history (addDMMsg) was gated to ADV_TYPE_CHAT only,
while rooms (ADV_TYPE_ROOM) share that same history list (keyed by the
server's pubkey). Include ADV_TYPE_ROOM so room posts are stored too.
A room carries many guests, so prefix each stored post with its author —
resolved from the signed message's sender pubkey prefix (fallback: short
hex) — and have the DM history view split "Sender: text" for room contacts
so each line is attributed to the guest who wrote it, instead of showing the
room server's name for every message.
Builds verified: WioTrackerL1 + GAT562 30S solo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The flat 10-item Tools list grew crowded, so tools are now grouped into
collapsible sections (Location / Comms / System) that fold in place — the
same model as Settings.
Extracted that fold-in-place behaviour into a reusable AccordionList helper
(owns the collapse mask, flattened visible-row list, selection and scroll;
host supplies section sizes + two row painters) and migrated SettingsScreen
onto it, dropping its hand-rolled buildVis/visIndexOf/sectionIndex and five
state fields. AccordionList joins drawList/DigitEditor/NavView as a shared
UI element used by two screens.
Added four mini-icons (bot, note, chart, gear) so every Tools row and
section header carries a glyph.
Builds verified: WioTrackerL1 + e-ink solo (Flash 61.0%).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the OLED solo size optimisation (44ae2745) on the remaining solo
builds so CI artifacts ship the reduced firmware too. The nRF52 Arduino
core defaults to -Ofast (≈ -O3 + -ffast-math); overriding with
build_unflags = -Ofast + -Os reclaims ~200 KB with no feature loss.
E-ink solo: Flash 62.1% (440140 B)
GAT562 solo_ble: SUCCESS
GAT562 solo_dual: Flash 61.9% (441164 B)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The nRF52 Arduino core compiles with -Ofast (≈ -O3 + -ffast-math) by
default, which had pushed this feature-rich solo build to ~90% flash.
Unflag -Ofast and build with -Os: flash drops 90.1% → 60.9% (638 KB →
431 KB, ~200 KB reclaimed) with no loss of functionality and a negligible
speed cost for a UI/mesh workload. -Os also avoids fast-math float
reassociation, so it's marginally safer for the geo/distance maths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A channel [LOC] broadcast already carries the sender (sendGroupMessage
prepends "<name>: "), but the DM path sent a bare "[LOC]<lat>,<lon>" with
no name in it. Embed the node name as a trailing token after the
coordinate so a DM share is self-describing in any chat client;
parseLocShare ignores the trailing text, so receivers are unaffected.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The label collision-avoidance reserved waypoint label slots first, so a
tracked contact's name was dropped whenever it sat near a waypoint —
leaving only the waypoint label. Label live-tracked contacts first: on a
live map the moving person's name should win the slot over a nearby static
waypoint.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Home Map page mini-preview gains a north mini-icon and a fillRect scale
bar with a round-distance label (metric/imperial), and its status line now
shows the distance to the nearest tracked contact alongside fix/track.
Trail map labels no longer smear in dense clusters: each placed label
reserves a rect and a new one tries four offsets around its marker,
skipping (marker still shown) when none fit without overlap.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The stored list only re-merged + re-sorted periodically under the "recent"
sort; under distance sort it refreshed only on entry. So a [LOC] share
arriving while the list was open (sorted by distance) wasn't re-sorted and
the now-nearest node never moved to the top. Refresh on a timer under both
sorts, keeping the highlighted node selected across the rebuild. Factor the
identity-preserving reselection into refreshKeepingSelection(), shared by
the list and the detail/nav refresh paths.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enter on the Target row now opens a picker instead of only cycling: it
lists favourites first (the quick path), then active verified live
senders, then waypoints — UP/DOWN + Enter to choose. This lets you arm an
alert against a person ahead of time by pubkey, without waiting for them
to share a [LOC] (the previous cycle only offered currently-active live
senders, so a person you hadn't received a fresh share from couldn't be
selected at all). LEFT/RIGHT still quick-cycles the same unified set.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A position-sharing node that isn't a stored contact was appended to the
Nearby list regardless of the active type filter, so a companion would
show up (and, having a live distance, sort to the top) under the Repeater
/ Room / Sensor filters. Gate the append on the type filter — a non-contact
[LOC] sender is treated as a companion, so it only appears under All/Comp.
Also drop the "share must be newer than the advert" guard when overlaying
a share onto a stored contact: a [LOC] message is an explicit current
position, so it always defines the pin and the distance used for proximity
sorting (recency for the time sort stays the most recent of advert/share).
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the text keyboard for the "Add by coords" Lat/Lon fields with the
shared digit-by-digit DigitEditor (the radio-frequency widget): Enter
opens the editor on the magnitude (LEFT/RIGHT move the cursor, UP/DOWN
change the digit), and LEFT/RIGHT on the row still toggles the hemisphere
when the editor is closed. The keyboard never fit numeric entry — no
minus, easy to type stray letters. Label still uses the keyboard. Docs
updated.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The proximity beeper is its own opt-in toggle, so enabling it is an
explicit "I want to hear this" — drop the isBuzzerQuiet() guard so it
ticks even when the global buzzer is muted (manual Off or BT auto-mute).
The arrive/leave crossing alert has no dedicated toggle and keeps
respecting the mute. Docs updated.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the live-following additions in the Tools docs: Geo Alert target
can be a waypoint or a live contact (Near/Away wording, follows a moving
person), the ♦ broadcast marker and "Sharing pos:" in Nearby, the
ETA line in the navigate view, and the home Map page Hold-Enter one-shot
share.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Builds on live location sharing:
- Nearby "Navigate" now follows a moving live target: the navigate view
re-reads the shared position every second and re-locates the entry by
name for non-contact senders, so it no longer freezes or drops out
(previously only refreshed for stored contacts entered via detail).
- NavView gains a closing-speed / ETA line (EtaTracker), shown in the
Nearby navigate view while approaching.
- Geo Alert target can be a live contact, not just a waypoint: the engine
re-reads the contact's latest [LOC] position each evaluation (keyed by
pubkey prefix) so the geofence follows a moving person; alert wording
switches to Near/Away. The picker cycles waypoints + verified live
contacts (shown with an '@' prefix). Schema bumped 0xC0DE0014 -> 0x15.
- Nearby marks a position-broadcasting node with the map's ♦ diamond
(was a cryptic '*'/'~' prefix) and spells out "Sharing pos:" in detail.
- Home Map page Hold-Enter is a one-shot position share: pushes to the
Live Share target while auto-sharing is on, else opens the picker.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tools Screen docs gain Live Share (send/track [LOC] positions, target +
movement/heartbeat gates) and Geo Alert (waypoint geofence, arrive/leave
modes, proximity beeper) sections, plus the Trail auto-pause setting, the
"Share my pos" action, live-tracked contacts on the map, and the home Map
page note. README feature lists updated to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review cleanup of the new location features:
- GeoAlertScreen + LiveShareScreen use the shared drawList() helper instead
of hand-rolling the scroll/clamp/scrollbar maths, matching every other
list screen.
- Extract UITask::geoAlertDistance() — one place for the target-distance
maths, shared by the crossing evaluator and the proximity beeper.
- Geo Alert re-seeds its crossing engine after every edit, so changing the
target/radius while armed can't fire a stale arrive/leave before exit.
- Trail auto-pause gets its own coarse movement gate
(NodePrefs::TRAIL_AUTOPAUSE_MOVE_M = 15 m) instead of reusing the trail
min-delta, so GPS jitter while parked doesn't keep the idle timer alive.
- Fix stale include comments.
Verified: WioTrackerL1_companion_solo_dual builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>