132 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 4.8 13fca6369f touch: OTA previous-version — one button + version popup + confirm
Reworks the earlier-version OTA UI from three inline buttons into a single
'Install a previous version' button that opens a popup of the available older
betas; picking one shows an are-you-sure confirm (reusing showConfirm) before it
closes and hands off to the existing download/verify/reboot flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:21:37 +02:00
Kaj SchittecatandClaude Opus 4.8 abdb878006 touch: OTA — install a previous version (up to 3 back)
Adds a 'Reinstall an earlier version' section to the Wi-Fi OTA UI (native
dual-OTA boards only): up to three buttons for beta_(latest-1..3), each flashing
that exact published bin via the same download/verify/reboot path. The worker URL
is parameterised on a target version; the existing 'Install update' button now
routes through the same otaStartInstall(latest) helper. Buttons auto-hide when
there aren't that many earlier releases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:08:18 +02:00
Kaj SchittecatandClaude Opus 4.8 2b4f2bc29e touch: fix status-bar clock overlapping the node name on the V4
The clock's runtime placement used the wide-bar -142 offset on every board,
overriding the V4's intended -126 build slot. On the narrow V4 portrait bar that
pulled the clock left into the width-capped node-name window — the long-standing
'clock overlaps the name' issue. Make the placement bar-width-aware: <300 px bars
(V4) use -126/-94, wide bars (T-Deck/Tanmatsu) keep -142/-110.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:47:15 +02:00
Kaj SchittecatandClaude Opus 4.8 79b4f10c25 touch: V4 Sensors tab + Home env widget only when sensors present
Refines the Expansion-Kit UI (#29) so it doesn't intrude on a bare V4: the
Sensors tab and the Home environment widget now appear only when an actual
environment sensor (BME280 / GXHTV3) is detected at boot — battery and GPS no
longer count, so a V4 without the Expansion Kit board shows the normal 5-tab
layout and a clean Home (no empty "Batt ----" / "Temp ...." lines). Adds a
"Show Sensors tab" toggle in Display settings to hide it even when sensors are
present (applies on restart). Tab indices resolve at boot so Settings shifts
back to slot 4 when the Sensors tab is absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:34:29 +02:00
42746ef387 touch: Heltec V4 Expansion Kit UI + mesh console (issue #29)
Heltec V4 Expansion Kit local-sensor UI, board-gated behind HAS_EXPANSION_KIT
(V4 touch env only — T-Deck/Tanmatsu unaffected): a Sensors bottom tab
(atmosphere / GPS / battery + history charts), a Home env summary widget, an
Expansion Kit card in Device settings, and a Local Sensors detail page. Reads
on-board MeshCore sensors (BME280, SHT4x, battery, GPS, buzzer) via the standard
sensor/telemetry API — no new hardware drivers.

Mesh console: enable the existing Terminal on the V4 (HAS_TOUCH_UI), renamed
"Mesh console", with wifi/tcp/ble on-off + ota status quick commands. The
SD-backed file-manager pieces stay T-Deck-only (V4/Tanmatsu get a no-op path).

Tab indices are conditional so non-Expansion boards keep 5 tabs (Settings=4) and
the V4 gets 6 (Sensors=4, Settings=5). The PR's GPS-nav/waypoint and weather
pressure-alarm bundles were intentionally left out.

Ported from PR #29 by @samuelcoustet, re-applied against current main.

Co-Authored-By: samuelcoustet <257299705+samuelcoustet@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:57:40 +02:00
45e46cd3ab touch: EU keyboard layouts (FR/NL/DE/ES/IT) + auto-select by UI language
Adds French (AZERTY), Dutch, German (QWERTZ), Spanish and Italian layouts — both
the on-screen keyboards and the T-Deck physical-keyboard maps (KeyboardLayoutId
FR..IT). Picking a UI language now auto-enables and selects the matching keyboard
layout, so choosing French gives AZERTY without a separate trip to keyboard
settings. Accented letters still come from the long-press accent popup; no new
fonts required. Extended the settings layout-enable list to match the larger enum
(was a latent out-of-bounds once the layout count grew).

Ported from PR #29 by @samuelcoustet — the layout data applied cleanly; the
UITask wiring was re-applied by hand against current main (the PR was cut from
beta_12 and no longer merges).

Co-Authored-By: samuelcoustet <257299705+samuelcoustet@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 12:41:50 +02:00
Kaj Schittecat 97b24c4268 Merge PR #48: per-channel scope shows default region as placeholder (tun0) 2026-06-24 12:11:23 +02:00
Kaj Schittecat 981fcba226 Merge PR #49: contact action-sheet landscape overflow fix (tun0) 2026-06-24 12:11:23 +02:00
Kaj Schittecat 50f73f41b0 Merge PR #50: edge-scroll — modals block background scroll (tun0) 2026-06-24 12:11:23 +02:00
Kaj SchittecatandClaude Opus 4.8 c5c0a04484 touch: restore Wi-Fi OTA self-update on native dual-OTA boards
Re-enables the over-the-air update that #74 stubbed out — the block then was
undersized 1.5 MB OTA slots, and both touch tables are now 3.875 MB A/B. Wi-Fi
self-update runs again on natively-flashed boards (every V4 + standalone T-Deck);
Launcher single-slot installs and the Tanmatsu (AppFS) keep the manual path.

- Gated on touchHasOtaUpdateSlot() (a spare A/B slot exists) — already drives the
  update badge, so Launcher/Tanmatsu never reach the install path.
- Self-contained Arduino Update flow in the touch app (the vendored core's OTA
  allow-list is meshcomod-only, so we don't route through board.startHttpOtaFromUrl).
  Fetches the immutable releases/TOUCH/beta_<N>/<board>.bin over plain HTTP from
  firmware.wadamesh.com on the tile worker (off the UI thread); progress polled by
  a UI timer.
- Update.begin(len) does the spare-slot select AND the size check, so a legacy
  too-small slot fails cleanly and falls back to the manual flasher message.
- Verify (MD5) + set-bootable via Update.end(true), then rebootDevice() (saves
  chat history first). A corrupt/short download leaves the running slot intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:56:04 +02:00
Ruben Laban 10ea0d8399 touch: fix contact action sheet overflow on landscape display
The grid_items count missed the conditional "Show on map" button,
causing the card height to be calculated short when the contact has a
GPS fix. Additionally, on landscape displays (sh=240) the backdrop is
only 218px, which repeater cards with many buttons already exceeded.

Fix both: pre-check the GPS condition and include it in grid_items,
and cap the card's displayed height to the available backdrop space
with vertical scroll enabled when the content overflows. The close
badge is FLOATING so it stays anchored regardless of scroll position.
2026-06-24 11:11:56 +02:00
Kaj SchittecatandClaude Opus 4.8 d491065719 touch: power-save shows as an amber battery, not a moon icon (T-Deck)
Idle power-save (light-sleep) advertised itself with a separate crescent-moon
glyph in the status bar. Replace it with an iPhone-Low-Power-Mode-style amber
tint on the battery icon (iOS systemYellow): clearer, and frees the bar slot.
The moon stays in the tree but hidden, keeping the layout and its tap handler.

A single applyBattColor() helper owns the battery colour so the map-chrome
restyle (black/white over light tiles) and the per-tick refresh never fight —
the amber overlays whatever base colour the context wants when power-save is on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:54:06 +02:00
Ruben Laban ab52385f98 touch: edge-scroll — don't fall through to background when dialog is at scroll limit
If lv_layer_top() has a visible widget at the search coordinate (i.e. a
modal or dialog is open), stop the layer search there regardless of whether
a scrollable was found. Prevents the content behind the dialog from being
scrolled when the dialog itself has no more room to scroll.
2026-06-24 10:49:40 +02:00
Kaj SchittecatandClaude Opus 4.8 d11a5bb6b2 touch: keep list scroll position when closing an overlay (issue #45)
Closing a settings item / modal / chat with keyboard-nav on snapped the
underlying list back to the top. navMaybeRebuild rebuilds the focus group when
the overlay closes, but the previously-focused element lived inside the overlay
and is gone, so focus fell to the first list item and the list scrolled up to
show it. Affected every screen because every overlay closes through this path.

Stash the page item that had focus when leaving for an overlay/chat, and restore
it on return — so focus, and the scroll position, land back where they were.
Falls back to the prior behaviour if that item is genuinely gone. Most visible on
beta_14 (keyboard-nav defaulted on); new installs now default it off.

Reported by ZorkaTheDog (#45).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:31:47 +02:00
Ruben Laban 80af33e7f9 touch: show default region scope as placeholder in per-channel scope field
When a global region scope is configured, the per-channel override input
now shows it as greyed-out placeholder text instead of the generic "#region",
so users can see at a glance what scope an empty field will inherit.
Falls back to "(no default)" when no global scope is set.
2026-06-24 10:29:35 +02:00
Kaj SchittecatandClaude Opus 4.8 c8849916df touch: show companion-app-sent DMs on the device (issue #46, half 1)
A DM sent from the MeshCore companion app went out over the mesh but never
appeared on the T-Deck: the wire protocol deliberately doesn't echo sent
messages back to clients, so the on-device UI (a separate consumer) never saw
app-originated sends. Add an appSentMsgToContact() UI hook (default no-op for
non-touch UIs); the touch UI mirrors the message as a local outgoing bubble in
the recipient's thread. Called from the CMD_SEND_TXT_MSG handler on first
handling only (a client/transport retry was already mirrored).

Still open: the receive direction (T-Deck-received messages reaching the app) —
queueMessage already pushes RESP_CODE_CONTACT_MSG_RECV_V3 to all transports, so
that half needs a frame capture against the real app to pin down.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:54:38 +02:00
Kaj SchittecatandClaude Opus 4.8 278b31cc92 touch: keyboard navigation OFF by default (Tanmatsu stays on)
Reverses the v15 force-on. New installs and existing T-Deck/V4 devices now
default to keyboard-nav OFF (a v18 one-time migration flips existing installs
to off); the user's later explicit on/off then persists. The Tanmatsu is exempt
— it has no touchscreen, so keyboard nav is its only input and stays on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:39:28 +02:00
Kaj SchittecatandClaude Opus 4.8 24f338438e touch: tapping an already-selected field enters edit mode
The touch-to-edit path keyed off navFocusCb (a focus CHANGE), so a field that was
already the selected one — e.g. the Profile node-name field, auto-selected on
entry — couldn't be made typable by tapping it (the tap doesn't re-focus, so
navFocusCb never fires). settingsFieldFocusCb already runs on CLICKED; now a real
tap also sets edit mode, so a selected field becomes typable on tap. Keyboard-nav
onto a field still starts in navigate mode (press Enter).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:29:05 +02:00
Kaj SchittecatandClaude Opus 4.8 3e1b45504f touch: navTreeSig must not COUNT NAV_SKIP children (complete the accent-box fix)
The previous fix returned 0 from the recursion for NAV_SKIP/HIDDEN subtrees, but
the parent loop still iterated over the added/removed child (h = h*prime + 0),
so the child COUNT still moved the signature. The accent box + @-mention picker
are created/deleted on every keystroke, so the count flipped each time → rebuild
→ edit-mode reset → typing broke mid-word (and Esc then read as navigate-back,
exiting the screen). Now skip those children in the loop entirely so they never
affect the signature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:22:18 +02:00
Kaj SchittecatandClaude Opus 4.8 12c34a2b35 touch: accent/mention box no longer breaks typing in non-chat fields
Non-chat text fields (add-channel, Wi-Fi, room login, settings modals) live on
the top UI layer, so keyboard nav is rooted there (useTop). The accent box and
@-mention picker are ALSO on the top layer, and they were counting toward
navTreeSig() — so each keystroke that showed/hid the box flipped the signature,
triggered a navMaybeRebuild(), and that rebuild's remove-all + re-focus churn
reset edit mode, dropping you out of typing mid-word.

- navTreeSig() now skips NAV_SKIP_FLAG subtrees (accent box, mention picker), so
  those passive overlays never trigger a rebuild.
- navFocusCb() ignores the transient f==null a rebuild's remove-all emits, so any
  other rebuild can't reset edit mode either.

(The T-Deck never used the keyboard mirror — kbMirrorActive() is false there; the
mirror is V4-only for the on-screen keyboard.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:05:25 +02:00
Kaj SchittecatandClaude Opus 4.8 0a3f9fe82f touch: @-mention picker (#42), edit-mode cursor cue, robust edit-state
- @-mention picker (issue #42): typing "@" in a chat composer pops a touch list
  of matching contacts (prefix-filtered, up to 6); tap one to insert "@Name ".
  Passive NAV_SKIP_FLAG overlay like the accent box; touch-select for now. Takes
  priority over the accent box while a mention is active (composerSuggestRefresh).
- Cursor now reflects edit mode: shown only while editing, hidden while a focused
  field is just the keyboard-nav target — a reliable "ready to type" cue
  (navSyncCursor() each nav tick).
- Edit mode is decided only on a genuine move to a different field, so a rebuild
  that re-focuses the same field (e.g. while the accent box is up) no longer
  kicks you out of typing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 08:20:40 +02:00
Kaj SchittecatandClaude Opus 4.8 02cff3389d touch: tap-to-edit on touch focus + map-render watchdog yield
- Keyboard nav: tapping a text field by touch now enters edit mode immediately
  (focus came from the pointer indev) instead of requiring Enter. Reaching a
  field with the letter-nav keys still starts in navigate mode (press Enter).
- Map: yield to IDLE after each tile in renderMapTiles() so a slow multi-tile
  render on loopTask can't starve the task watchdog and panic — fixes a beta_14
  TWDT reboot seen during map use (CPU1=loopTask). The download worker already
  does the same vTaskDelay(1) on its side.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 08:08:16 +02:00
Kaj SchittecatandClaude Opus 4.8 7111789fa7 touch: fix typing dead when keyboard-nav is OFF (beta_13 regression)
The keyboard-nav rework gated the physical-keyboard typing target on
s_nav_ta_editing (edit mode). That flag is only ever set while keyboard
navigation is ON (navFocusCb, driven by the nav focus group). With keyboard
navigation turned OFF the nav group isn't maintained, so the flag stayed false
and `ta` was always null — keys never reached the focused field and typing was
completely dead on the T-Deck (and Tanmatsu).

Fix: when keyboard-nav is OFF there is no navigation to protect, so a focused
field is always the typing target (ta = ta_focused). Edit-mode gating still
applies only when keyboard-nav is ON. Affects handleHwKey (T-Deck) + navPump
(Tanmatsu); the V4 (no keyboard nav) was never affected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 00:39:38 +02:00
Kaj SchittecatandClaude Opus 4.8 f817fca41c touch: disable crash-report UI on Heltec V4 (no way to retrieve/send the dump)
The boot crash-report prompt + the About-page export button are only useful
where the saved coredump file can actually be retrieved and sent to the devs:
the T-Deck (SD card) and Tanmatsu (FFat, via the launcher file manager). On the
Heltec V4 there's no SD and no user-reachable internal storage, so the prompt
was a dead end. Gate crashDumpCheck() to those boards only — on the V4 the dump
is never detected, so the whole crash-report UI stays off (everything downstream
gates on s_crash_dump_size). The panic coredump still sits in its flash
partition for a USB/esptool pull if ever needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 23:49:41 +02:00
SamandClaude Sonnet 4.6 5fcf6ddac3 fix: block commands while transport-off confirmation is pending
Any command arriving while s_meshcomod_pending_action is non-NONE and
the timeout hasn't expired previously fell through the confirmation
block and was processed normally, letting a second 'tcp off' / 'ble off'
silently overwrite the pending action and lose the original confirmation.

Add a final else clause that blocks the new command and reminds the user
to 'ok' or 'cancel' the in-flight request.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:25:02 +02:00
SamandClaude Sonnet 4.6 5e6aaa8852 fix: restart TCP immediately on enable, guard OTA slot, fix toggleTcp alert race, re-enable warnings
- enableTcp() now calls startTcpServer(false) directly instead of relying
  on the main loop — fixes TCP not restarting when wifi_started is false
  (BLE-only mode, radio toggled off, or cold re-enable from USB).

- Add touchHasOtaUpdateSlot() guard to the OTA button creation block so
  the update affordances stay hidden under Launcher (single app slot),
  where ota url would fail silently.

- Fix toggleTcpCb alert race: snapshot isTcpEnabled() before the toggle
  so the alert text reflects the transition, not a post-action re-read
  that could mismatch during OTA suspension.

- Replace global -w with -Wno-deprecated-declarations -Wno-unused-parameter
  in both touch environments to keep third-party lib noise down while
  surfacing real regressions in OTA, network, and companion paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:25:02 +02:00
Kaj Schittecat cd2dc8b3f8 Merge PR #38: touch — optional trackball edge-scroll (tun0) 2026-06-23 23:22:34 +02:00
Kaj Schittecat f4fb7171e5 Merge PR #22: touch — beer emoji, status-bar unread tap, new-contact toast toggle, Czech accents (Vybo)
# Conflicts:
#	src/helpers/esp32/TouchPrefsStore.cpp
2026-06-23 23:22:23 +02:00
Kaj Schittecat 8322b5361d Merge PR #40: ui-touch — room-server posts show author, not room name (Yazutsu, closes #32) 2026-06-23 23:19:14 +02:00
Kaj Schittecat 266f5c8adc Merge PR #39: touch — Direct (0-hop) contact + map filter (tun0) 2026-06-23 23:19:01 +02:00
Kaj SchittecatandClaude Opus 4.8 069aeb8908 touch: keyboard-nav typing fixes, dedicated symbol picker, 12h everywhere
Post-beta_12 touch-UI fixes (T-Deck / Tanmatsu keyboard nav + composer):

Keyboard navigation
- Chat composer auto-edits on open (cursor shows, type immediately); every
  OTHER field stays in navigate mode so the letter-nav keys keep working —
  press select/Enter to edit it. Enter (or Esc/X on Tanmatsu) on an empty
  composer drops back to navigate mode.
- Menu-bar tab-hotkey letters no longer fire while a text field is the focused
  element (fixes a stray tab-jump while typing); directional nav still moves.
- New "Show menu-bar letters" setting (off by default) for the tab hotkeys.

Accent box
- The tap-to-pick accent box no longer steals the keyboard-nav focus group
  (NAV_SKIP_FLAG) — it's a passive hint, so typing continues and a keybind no
  longer navigates. Ready to wire arrow-key selection later.

Composer
- Dedicated "Special characters" picker (% $ @ # & ... currency/math/accents),
  separate from the emoji picker.
- Long-press a sent message -> Resend.

Misc
- 12-hour clock honoured everywhere (top bar, chat bubbles, lock screen), not
  just the chat overview.
- GPS UART RX buffer enlarged only when GPS is enabled (restores V4 RAM).
- Derive time-of-day from received mesh message timestamps when Wi-Fi/GPS off.
- Self-record the task-watchdog context on a TWDT reset for the crash report.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 23:18:34 +02:00
Yazutsu 61a914240f ui-touch: show post author instead of room name for room-server messages
Room posts arrive as TXT_TYPE_SIGNED_PLAIN with the author in the 4-byte sender_prefix and a bare body. The display path passed the room contact's name, so every post showed the room name. Resolve the author from the prefix and pass it as the per-message sender via a new newRoomMsgFromPubWithMeta hook; the room stays the thread. Tested on T-Deck Plus.

Signed-off-by: Yazutsu <andrzej@gruziel.pl>
2026-06-23 22:45:39 +02:00
Ruben Laban 29204dd33f touch: add Direct (0-hop) filter to contact list and map
Contact list gains a "Direct (0-hop)" option in the Filter sheet (value 5)
that shows only nodes heard without any repeater hops (out_path_len == 0).
Header chip shows "Dir" when active. Selecting a filter now auto-dismisses
the sheet (matching sort behaviour).

Map options panel gets a "Direct (0-hop) only" toggle that hides both
contact markers and link lines for any multi-hop or path-unknown node.
2026-06-23 14:36:57 +02:00
Ruben Laban bb86a32b04 touch: add optional edge-scroll for trackball cursor mode
When enabled in Settings → Keyboard, pushing the cursor past any screen
edge routes the overflow into the nearest scrollable widget rather than
absorbing it.  Works on regular tab content (lv_scr_act children) and on
lv_layer_top() overlays such as the app drawer.

Implementation:
- tbFindScrollableAt() hit-tests at the vertical centre of the content
  area (avoids status/tab-bar chrome and per-tab headers), searches
  lv_layer_top() before lv_scr_act(), then walks ancestors to the first
  scrollable with room in the overflow direction.
- scroll_by direction is negated relative to raw overflow (scroll_by(+dy)
  moves children down = reveals earlier content = scroll up).
- Scroll amount is capped to lv_obj_get_scroll_top/bottom/left/right so
  we never push past the widget's content bounds.
- Preference persisted as NVS key "tb_edgesc" (default off).
- Map, Snake, and emoji-picker modes are unaffected (they return before
  cursor mode runs).
2026-06-23 14:07:57 +02:00
Kaj SchittecatandClaude Opus 4.8 30083efdd7 touch: beta_12 — GPS hotfix (enlarge GPS UART RX buffer)
The GPS NMEA UART used Arduino's default 256-byte RX ring (~66 ms of slack at
38400 baud). On the busy touch UI a single long LVGL/map frame stalls the loop
past that window, dropping bytes and corrupting NMEA ephemeris subframes — each
loss costs the receiver ~30 s, turning a ~1-minute fix into several minutes or,
with frequent stalls, never acquiring. Confirmed on-device: peak UART backlog
hit 448 B at idle, well past the 256 B default. Bump the ring to 4096 B (~1 s of
slack) before the core opens Serial1 in sensors.begin(). App-side (no core
re-tag); gated to ENV_INCLUDE_GPS so both touch boards get it. A -DGPS_BUF_DEBUG
backlog probe is included (compiled out of releases).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 08:49:00 +02:00
Dan Vybiral 4a0d7f5247 touch: beer emoji, status-bar unread tap, new-contact toast toggle, Czech accents
- emoji: bake the beer mug U+1F37A and clinking beers U+1F37B as colour glyphs
  into emoji_data.c (now 252) and add the beer mug to the composer picker; the
  clinking pair was already listed but only rendered monochrome, now colour.
- status bar: tapping the unread badge (envelope + count) on the left now jumps
  to the Chats inbox. The CLICKABLE flag is toggled per-tick so only the badge
  state intercepts the tap — every other left-zone state (Back chevron, profile
  name, OSM credit, blank) keeps the bar's control-center / Back tap.
- contacts: add a "Notify on new contact" toggle to the Auto-add page
  (TouchCfg v8, default ON). When off it suppresses the auto-discovery toast
  chip in UITask::notify(); the diag-log line and tab badge are unaffected.
- keyboard: add the Czech accents c-caron, e-caron, t-caron, z-caron, r-caron
  and u-ring (plus uppercase) to the long-press / tap accent popup. Glyphs are
  already baked in the extras fallback fonts, so no font regeneration needed.

Builds verified on both LilyGo_TDeck_companion_radio_touch and
heltec_v4_tft_companion_radio_usb_tcp_touch.

Signed-off-by: Dan Vybiral <dan.vybiral@greencode.cz>
2026-06-22 20:37:24 +02:00
Kaj SchittecatandClaude Opus 4.8 1f755ec3b8 touch: beta_11 — bindable keyboard navigation, crash-report prompt, map zoom buttons, app-drawer-as-home; publish Tanmatsu port source
Headline: full keyboard navigation (on by default) — WASDZ spatial focus move, S select,
Q back, per-tab hotkeys (E/R/T/U/I) shown on the icons, plus scroll up/down (F/C). Every
key is remappable in Settings -> Keyboard. Foundation for keyboard-first devices like the
Tanmatsu T-Pager (no touchscreen) and a reliable keyboard workflow on the T-Deck today.
Also: #27 receive/flood reboot fix, on-boot crash-report prompt, map zoom +/- buttons (#26),
route-replay wide overview, 'App drawer as home' option. The in-progress Tanmatsu (ESP32-P4)
port source is now public. Mesh America catalog + release.sh kept from main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 19:10:40 +02:00
Kaj SchittecatandClaude Opus 4.8 c931df6054 touch: beta_10 — trackball D-pad mode, microSD tile-cache fill, Battery group
- T-Deck: trackball mouse/D-pad navigation toggle (Settings -> Keyboard).
- Map (#20): microSD-tile mode now caches Wi-Fi-fetched tiles onto the SD card so
  the offline library fills in + grows instead of going blank past its edge.
- Power: PR #19 idle power-save (Dan Vybiral / @Vybo) relocated into a new
  'Battery' group under Settings -> System, alongside a button that opens the
  battery / power-history screen.
- TouchCfg v10 (ui_scale, tb_keypad, sleep_idle). Mesh America catalog -> beta_10.

Curated touch overlay onto main (the WIP Tanmatsu ESP32-P4 build system stays on
the tanmatsu-port branch). Co-Authored-By: Dan Vybiral <dan.vybiral@greencode.cz>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:08:13 +02:00
Dan Vybiral 11b31e1cdd touch: idle power-save — loop-throttle + downclock when parked (#19)
Opt-in Idle power-save for the T-Deck: when parked (screen off, on battery, no
companion client, Wi-Fi+BLE off, mesh idle) the main loop is throttled with
vTaskDelay so the FreeRTOS idle task can halt the CPU between ticks, on top of the
existing 80 MHz screen-off downclock — without ever stopping mesh RX. Default OFF
(Settings -> Lock, persisted TouchCfg v8 sleep_idle); status-bar moon indicator +
idle stats. Heltec V4 compiles (gate never passes there). A plain scheduler throttle
rather than esp_light_sleep_start(), which trips INT_WDT in this build.

Co-authored-by: Dan Vybiral <dan.vybiral@greencode.cz>
2026-06-21 22:39:59 +02:00
Kaj SchittecatandClaude Opus 4.8 7a63ffe86c touch: beta_9 — crash/Wi-Fi reliability, crash-report export, room fixes, app-drawer icon size
- Stability: Wi-Fi auto-reconnect; corrupt chat-history quarantine (fixes reboot-on-
  receive / inaccessible messages); draw-buffer + flush NULL-guards (boot-panic).
- Crash-report export to SD/SPIFFS (Settings -> About).
- Map: reload purges on-disk tiles offline + visible zoom band; corrupt-tile self-heal
  on load; z15 dedup-key overflow fixed.
- Contacts: favourites always listed (#17).
- Chat: room message send-time (#26) + sender labels; QR share uses the official
  meshcore://contact/add format (#16); unread badge capped at 99+.
- App drawer: icon-size setting (Compact/Large) + smoother scrolling.
- ~5 KB internal DRAM reclaimed (backup arrays + msg_idx -> PSRAM).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 15:16:04 +02:00
Kaj SchittecatandClaude Opus 4.8 2aa8f59410 touch: beta_8 — Ack reply, map visibility toggles + auto-refresh, RAM trim
- Chat: quick replies insert at the cursor (no longer replace typed text);
  long-press 'Ack' drops a signal-strength + route reply into the composer
  (@mentions the sender on channels); fixed the action-menu overflowing the screen.
- Map: +/- zoom glyph; per-line on-map visibility toggles (coords / tile xyz /
  contacts); microSD-tiles regrouped under a heading; auto-refresh follow button
  with on/off toast; close-X overlap fix when scrolled.
- Perf: ~18.9 KB internal DRAM reclaimed (6 scratch buffers -> on-demand PSRAM).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 14:36:46 +02:00
Kaj SchittecatandClaude Opus 4.8 9e567e1789 touch: beta_7 — app-drawer fit + visible scroll, Snake touch steering & icon
- App drawer: size tiles to fit so the bottom row's labels aren't clipped, and
  show a persistent scrollbar when the grid overflows (discoverable scroll).
- Snake: steer via the hardware swipe detector (works on the V4 cap-touch and
  the T-Deck), stop touch/swipe leaking to the screen behind the overlay, and
  gate the status bar while playing.
- Snake app-drawer icon drawn from shapes — the U+1F40D emoji isn't in the baked
  colour-emoji set, so it rendered as a tofu box.
- Add beta_7 release notes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 12:21:49 +02:00
Kaj Schittecat bbcafa3088 Merge PR #12 (Vybo): status-bar tools, battery history, map night mode, image viewer, Snake
# Conflicts:
#	src/ui-touch/UITask.cpp
2026-06-18 11:04:08 +02:00
Kaj Schittecat b3af35666b Merge PR #15 (tun0): fix inaccurate unread badges + split history persistence
- closeChatPanel marks-read + flags dirty immediately (no stale badge)
- post-load reconciliation clears phantom unread for evicted threads
- split thread-meta (200ms flush) vs message-ring (lazy 2s) persistence
- ring 96 -> 500
2026-06-18 10:58:08 +02:00
Kaj SchittecatandClaude Opus 4.8 eca5281f22 touch: post-beta_6 batch — BLE PIN, wallpaper-from-files, storage & memory fixes
- BLE: user-settable 6-digit pairing code (Settings -> Bluetooth), MyMesh::setBLEPin.
- Lock wallpaper: pick via the file manager (fast, any folder) + 'Set as wallpaper'
  in the image viewer; downscaling JPEG decoder accepts images > 1024 px and keeps
  the held buffer small; clear message for progressive JPEGs.
- Map tiles: validate JPEG magic before caching + re-fetch bad cached tiles
  (fixes black quadrants).
- Channel @reply: quick-reply now appends at the cursor instead of wiping the
  mention.
- Audio: heap pre-flight before i2s_driver_install (fixes the esp_timer NO_MEM
  abort when toggling sound under BLE+Wi-Fi memory pressure).
- App drawer: squircle tiles. Log hygiene: CORE_DEBUG_LEVEL 1->0 (mute benign
  vfs/Preferences spam off the USB-CDC companion stream).
- Core bump to core-v1.16.4 (drops the 4-byte chat-payload trailer, issue #13).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 10:57:50 +02:00
Ruben LabanandClaude Sonnet 4.6 3a70a8ab0f touch: fix legacy history migration when ring size grew
loadLegacyHistoryFromStorage() tried to read MAX_UI_MESSAGES (now 500)
records from a file that only contained 96 (old ring size). It read the
thread metadata fine — populating unread counts — then hit EOF on the
messages and returned false. The reconciliation pass that resets phantom
unread counts never ran, so the first boot after the ring bump showed
high unread counters against empty channels.

Fix: derive the actual slot count from the remaining file size and only
read that many message records, zero-filling the rest. The phantom-badge
reconciliation in loadHistoryFromStorage() then clears any thread whose
messages were already evicted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:45:30 +02:00
Ruben LabanandClaude Sonnet 4.6 2894776a11 touch: split history save into threads+msgs files; bump ring to 500
Thread metadata (~4 KB, contains unread counts) and the message ring
(now ~115 KB at 500 slots) are now persisted to separate files
(/ui_threads_v1.bin and /ui_msgs_v1.bin).

Thread metadata flushes on the original 200 ms delay so unread counts
survive a hard reset. The message ring flushes lazily every 2 s,
batching burst arrivals into a single write instead of hammering SPIFFS
on every message — reducing flash wear and WdtHeavyGuard exposure as
the ring grows.

MAX_UI_MESSAGES 96 → 500 (PSRAM cost: 26 KB → 133 KB, well within
budget). Ring overflow within a session is now extremely unlikely on
LoRa; the cross-session phantom-badge fix (post-load reconciliation)
from the previous commit handles the remaining edge case.

Migration: on first boot the loader falls back to the legacy combined
file (/ui_chat_history_v1.bin), then schedules writes of the new split
files. Old file is left in place and ignored on subsequent boots.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:31:33 +02:00
Ruben LabanandClaude Sonnet 4.6 88a9d386f1 touch: reset phantom unread on load when ring has no messages for thread
The display ring is a fixed-size cache independent of the per-thread
unread counter. After a reboot the ring may not contain the messages
that were counted as unread (they were evicted before the save), leaving
a non-zero badge on a thread that opens to an empty chat.

Fix: after loading history, zero the unread counter for any thread whose
messages are absent from the ring. The unread counter and the ring are
independent — the ring is a display buffer only — so opening the thread
via the first fix (markThreadRead on close) handles the mid-session case;
this one-shot check handles the cross-reboot case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 09:05:36 +02:00
Ruben Laban a6647dcf55 Revert "touch: fix phantom unread badge when ring evicts channel messages"
This reverts commit 28879f55c4.
2026-06-18 09:04:37 +02:00
Ruben LabanandClaude Sonnet 4.6 28879f55c4 touch: fix phantom unread badge when ring evicts channel messages
The 96-slot message ring can overflow when there is sustained traffic
across many threads. When ring wraparound silently overwrites a channel's
messages, thread.unread stays non-zero but getThreadMessageIndexes
returns 0 — producing a badge with a count but an empty chat on open.
The same phantom persists across reboots because unread is saved to
disk independently of the ring contents.

Fix has three parts:

1. counted_as_unread per UIMessage (RAM-only): set true when a message
   is the reason a thread's unread counter was incremented. Cleared when
   the thread is marked read, so future evictions of those slots don't
   double-decrement.

2. Eviction hook in appendMessage: before overwriting a ring slot,
   decrement its thread's unread if the slot was counted_as_unread.
   Keeps the counter in sync as the ring wraps.

3. Post-load reconciliation in loadHistoryFromStorage: after restoring
   thread+message state from disk, any thread whose persisted unread
   count exceeds its actual incoming messages in the ring gets clamped
   (or reset to 0 if the ring has nothing for it). Also reconstructs
   the counted_as_unread flags so evictions after boot are tracked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 08:34:37 +02:00