Commit Graph
279 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 4.8 e26b406e02 touch: P4 honours the UI-size pref again (text size was a dead no-op)
beta_49 hard-pinned s_ui_fscale=100 on the P4 to stop >100% chrome overflow, but
the "UI size" dropdown stays shown (CAP_LARGE_SCREEN), so changing it did nothing
(reported: "changing text size doesn't work on the P4"). Honour the saved pref like
the Tanmatsu does. Large/Huge can still make some P4 chrome that uses unscaled dims
tight; that's a per-screen SC() follow-up, not a reason to disable scaling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 14:15:17 +02:00
Kaj SchittecatandClaude Opus 4.8 3779724000 touch: correct per-board OTA_BIN_NAME for all dual-slot boards
OTA_BIN_NAME only branched T-Deck vs everything-else -> wadamesh-heltec-v4-tft, so
V4-R8, ThinkNode M9, RAK Tap V2, both T-LoRa Pagers and Attaky would self-update by
downloading the V4-TFT bin -- flashing the wrong firmware onto the wrong board.
All are dual-slot OTA-capable (CAP_OTA=1 + app0/app1 partitions + FIRMWARE_OTA_ENV),
so map each to its own release bin. Pager splits on USE_LR1121. Tanmatsu stays out
(AppFS/launcher, CAP_OTA=0, never reaches this file).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 22:47:54 +02:00
Kaj SchittecatandClaude Opus 4.8 71dff36904 touch: wire on-device OTA / beta toggle for the T-Display P4
The update-channel UI (the "Get test builds (beta)" toggle + the Install-update
button) is gated on a non-empty FIRMWARE_OTA_ENV. That is set per-env in
platformio.ini for the 8 S3 boards but was never set for the P4 IDF build, so the
whole update UI was hidden — P4 testers had no way to opt into beta and stayed
stuck reading the stable channel (the beta_49 "no update, 48 up to date" report).

- tdisplay_p4 CMakeLists: define FIRMWARE_OTA_ENV so the UI is shown.
- UITask.cpp: add a P4 branch to OTA_BIN_NAME so the download URL points at
  wadamesh-tdisplay-p4[-lcd].bin, not the V4-TFT bin. AMOLED vs LCD split on
  HAS_TDP4_LCD.

The C6 WiFi shim already routes HTTPClient/Update.h and the P4 partition table has
dual ota_0/ota_1 slots, so the OTA path itself was wired; this only unhides and
correctly targets it. Builds green on P4 (AMOLED) + V4-TFT. On-device toggle/OTA
test still pending (P4 dropped off USB, needs a power cycle).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 20:52:43 +02:00
Kaj 35d0daf203 Merge pull request #169 from Attaky-Module/attaky-mesh-series
attaky_mesh_series: on-screen keyboard via detachable module, POWER button, and board fixes
2026-07-22 19:43:04 +02:00
Tesso M Costa e7c7df65fa pager: fix lock screen (peek gesture, native layout, focus-invert black text)
- Backspace tap now peeks the lock screen (reveal without unlocking),
  mirroring the T-Deck's trackball-click behavior; holding Backspace still
  fully unlocks.
- New native 480x222 lock wallpaper asset so the icon/wordmark aren't
  crushed by the shared 320x240 art's cover-fill on this wide/short panel.
- Lock text forced to pure white and repositioned for this layout: clock
  top-left over the icon, unread count + "Screen locked" stacked
  bottom-right directly above the unlock hint.
- Fixed lock text flipping from white to black a moment after every reveal:
  the lock overlay is CLICKABLE (to absorb taps), so the keyboard/encoder
  nav rebuild was collecting it as a focus target -- navFocusCb's focus
  highlight then reverse-videoed it, flipping every label's text to dark.
  Tagged the overlay NAV_SKIP so it's never a nav target and never inverted.
- serviceLockscreen() now runs in the pager's loop branch too, so the lock
  clock/unread badge keep refreshing after the first reveal instead of
  freezing there.

All changes gated to TLORA_PAGER; no other board's behavior changes.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-21 16:15:03 -06:00
Kaj SchittecatandClaude Opus 4.8 4e6a9e0108 touch: beta_49 - T-Display P4 fixes (exit traps, screen timeout, UI scale, GPS, control center)
P4-focused beta. Non-P4 boards only pick up minor shared exit-trap code, nothing user-facing.

- Snake + full-screen apps (Monitor/Spectrum/Discover/Web mirror/Advert) exit on status-bar tap
- Screen timeout / standby / burn-in dimming wired for the AMOLED (setBrightness path)
- UI scale pinned to 100% on the P4 (text overflow above 100%)
- No hard-lock on screen-off (P4 has no lock screen yet)
- GPS: L76K detect at 9600 baud, skip boot-detect race, 4096 RX buffer
- Control center dropdown taller on tall screens so the GPS line clears the toggle buttons
- P4 LCD SKU: HI8561 backlight PWM on GPIO51 (fixes the black screen)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 20:34:17 +02:00
Attaky 2d6a88bd1f attaky_mesh_series: include this board in the landscape home-chart width 2026-07-21 11:32:12 -07:00
Attaky b1ad449147 attaky_mesh_series: board fixes - crystal clock, GPS acquisition, sensor addresses 2026-07-21 11:32:12 -07:00
Attaky b1b689c48f attaky_mesh_series: keep remote landscape upright on this panel 2026-07-21 11:32:12 -07:00
Attaky b525f75335 attaky_mesh_series: summon the on-screen keyboard with the module's '#' key 2026-07-21 11:32:12 -07:00
Attaky 3d6e6f0963 attaky_mesh_series: read front buttons and toggle the panel with POWER_BTN 2026-07-21 11:32:12 -07:00
Kaj SchittecatandClaude Opus 4.8 5e4f9a221e touch: beta_48 fixes - crash, boot hang, MQTT save, message times, keyboard, more
Chat typing crash (#98), SD boot hang after update (#142, #148), MQTT
settings not saving (#128), wrong message times (#100, #152), Dutch keys
1 and ! typing ij (#144, #164), compose length limit (#119), clipboard
truncation (#120), quick messages scroll (#151), Join screen name glyph
(#116), Tanmatsu emoji picker nav (#141).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 11:24:31 +02:00
Kaj SchittecatandClaude Opus 4.8 99423e5bf8 touch: beta_47 — Discover app (find/list/map nearby nodes + wardriving), Attaky release wiring, P4 LCD
- New Discover app: active NODE_DISCOVER sweep -> signal-ranked nearby list, tap-to-add-contact, GPS wardriving log (SD CSV) + signal-coloured coverage overlay on the map. Board-agnostic.
- T-Display P4 TFT-LCD (HI8561) variant support (WADA_P4_LCD build; AMOLED bin untouched).
- Wire the Attaky Core board (#158, @attakygit) into the release matrix + web flasher.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 17:45:05 +02:00
Attaky b67f559d15 Attaky mesh series (#158)
* attaky_mesh_series: add board variant with FT6636 touch and landscape UI

* attaky_mesh_series: P3 battery (MAX17048) + GPS + shared-I2C mutex

- AttakySharedI2C: one Wire.begin() + a mutex serializing the shared board
  I2C bus (SDA=2/SCL=1) across the FT6636 touch poll (core 0), the MAX17048
  fuel gauge (core 1), and the future AW9523 keyboard. Arduino's TwoWire
  buffers are not concurrency-safe.
- board.begin() owns the authoritative bus init; the touch driver now takes
  the lock around every transaction instead of its own Wire.begin().
- getBattMilliVolts(): real MAX17048 VCELL read (78.125 uV/LSB -> mV = raw*5/64),
  2 s cache + lock, replacing the 4000 mV stub. Upstream maps mV -> % itself.
  Hardware-verified ~4058 mV.
- GPS: rename the dead GPS_BAUD flag to GPS_BAUD_RATE (the core reads that; 9600
  only worked via the #else default). Pins verified on hardware: ESP RX = IO17
  receives ATGM336H NMEA; MeshCore's module-centric PIN_GPS_TX=17 maps correctly.

* attaky_mesh_series: Control_Keyboard_(Bottom) dual-AW9523 keyboard

Add the Control_Keyboard_(Bottom)_1.0 module as an ADDITIVE hardware
keyboard on the touch board. Two AW9523 expanders (0x5A left / 0x5B
right) each scan a 5x5 matrix over the shared board I2C bus, serialized
through the AttakySharedI2C mutex alongside the FT6636 touch poll and the
MAX17048 fuel gauge.

The driver emits final bytes (printable ASCII, backspace, enter, space)
with momentary Shift resolved to uppercase in-driver. UITask injects them
into the focused textarea exactly like the web-mirror keystroke path, so
CAP_KEYBOARD stays 0 and the on-screen keyboard is not suppressed (touch
typing does not regress). Focus-gated: the matrices are only scanned
while an editable field is bound, so a key has zero side effect when
nothing is focused.

Hardware-verified, both halves: q/w/e/r/t (0x5A) + y (0x5B), Shift
uppercase, backspace, space, enter; on-screen keyboard intact.

* attaky_mesh_series: use reliable 115200 upload speed

* attaky_mesh_series: use core default GPS baud

* attaky_mesh_series: restore GPS_BAUD setting

* attaky_mesh_series: rename PlatformIO environment
2026-07-19 13:58:05 +02:00
Tesso Codemonkey 85e8681fef pager: keep encoder focus inside the chat message list, remap composer nav (#162)
* pager: keep encoder focus inside the chat message list, remap composer nav

The rotary encoder's plain turn stepped LVGL's focus group directly, which
only ever contains the currently-materialized bubbles (the message list is
virtualized; loading more history is scroll-driven, not focus-driven). So
turning past the first/last *loaded* bubble escaped the list onto the
header/composer even with plenty of un-loaded history in that direction.
A turn at that edge now scrolls the list instead when more messages exist
(reusing the existing free-scroll helper), which triggers virtualization to
load the next batch; focus only leaves the list at the true oldest/newest
message. Fast turning folds into a pending target so it doesn't escape
mid-load, and the render's focus re-aim now runs synchronously so the
transient auto-focus LVGL assigns a deleted row's neighbor never paints.

Also removes the two touch-only jump-to-oldest/latest arrows on this board
(no touchscreen to tap them) and remaps the chat's key bindings now that
plain turns stay in the list:
- Orange/Alt tap: jump to the newest message + focus the composer (was
  Backspace's job).
- Backspace tap: jump to the first unread message below the "NEW ----"
  divider, or the newest message if nothing's unread.
- Composer row edges: Send+NEXT reaches the channel-settings gear (falls
  back to the newest bubble in a DM, which has no gear); the quick-reply
  chip+PREV goes straight to the newest bubble.

TLORA_PAGER_SHORTCUTS.md documents the new chat-screen behavior.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>

* fix scroll

---------

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-19 13:58:02 +02:00
041dd60b8d touch: beta_46 — control-center redesign, Do Not Disturb, contact safety + board fixes
- Contact-loss fix (microSD devices): atomic temp+swap saveContacts, unified robust
  boot SD-mount ladder, and a storage-truth line in the Store-on-SD setting.
- Heltec V4-R8: route MISO for the shared FSPI bus so the microSD mounts; wake-from-sleep
  freeze fixed (panel sleep goes through LovyanGFX's own bus, not the HSPI shim).
- Do Not Disturb (originally PR #156 by Tesso Codemonkey / @codemonkeybr): scheduled
  sound-silencing window in Settings > Sound + a status-bar bell.
- Control-center redesign: round icon-only chips that spread to fill each row (T-Deck 2x4,
  V4 portrait 2 rows), frosted-glass panel, real lock glyph + dynamic bell/bell-slash DND
  chip + keyboard icon+word chip (new cc_icons_16 FontAwesome font), per-chip confirmation
  toasts, press-grow clip fix, and a V4-portrait status-bar BLE/clock overlap fix.
- LilyGo T-Pager: map tile cache falls back to the microSD under the launcher (fixes the
  "storage error / reflash the tiles partition" dead-end).

Co-Authored-By: Tesso Codemonkey <798026+codemonkeybr@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 22:30:05 +02:00
Kaj SchittecatandClaude Opus 4.8 fd0b952620 fix: R8 I2C freeze (shared-bus wedge) + Tanmatsu Wi-Fi settings unreachable
- Heltec V4-R8: the CHSC6x touch ran on a 2nd I2C controller (Wire1) bound to the
  SAME pads (17/18) as the board Wire (RTC + sensors), driven from two cores — a
  classic SDA-stuck-low wedge = the tester's random hard-freezes-needing-reset.
  Touch now shares the one board Wire (V4CAP_WIRE selector), so arduino-esp32's
  per-instance TwoWire mutex serialises all access, the proven T-Deck pattern.
- Wi-Fi settings: the join/details/hidden sheet floats on the base layer above the
  settings page; navMaybeRebuild only recognised the settings sheet + chat + top
  layer as focus roots, so on no-touch/keypad-nav boards (Tanmatsu) its SSID/
  password fields + Join button were never collected into the focus group and
  couldn't be selected. Add s_wifi_sheet as a nav focus root (priority over the
  settings sheet beneath it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:27:20 +02:00
Kaj Schittecat be0a49255c Merge PR #149: LilyGo T-LoRa Pager port (LR1121 + SX1262) by @codemonkeybr
Full keyboard+encoder-driven board port: TCA8418 QWERTY matrix, rotary encoder
focus-nav, ST7796 display, ES8311 notification sound, both radio variants.
Hardware-verified by the author against a live mesh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

# Conflicts:
#	src/ui-touch/UITask.cpp
2026-07-15 19:16:51 +02:00
Kaj SchittecatandClaude Opus 4.8 2353c56adc p4: T-Display P4 port — web VNC/remote, time-sync hardening, brightness + sound
LilyGo T-Display P4 (ESP32-P4 + factory ESP-AT C6) reaches POC: LoRa mesh, Wi-Fi
companion (TCP:5000), GPS, SD, battery gauge, chat persistence, and now:

- Web VNC/REMOTE/terminal via a first-byte router on the single ESP-AT listener
  (HTTP verbs -> WS server, '<' frames -> phone companion; both live at once).
  C6Server gains a _begun gate so never-listening instances stay inert.
- VNC freeze fixed: bounded one-CIPSEND-chunk drains under the WS client mutex
  (a whole-band blocking write starved the main loop), fast-drop of stalled
  mirror clients, SEND-OK wait 8s->4s.
- Wi-Fi robustness: CWJAP? miss-streak tolerance, CIPSERVER liveness verify +
  re-arm (a re-association silently killed the listener until reboot).
- Time sync: HTTP-Date fallback when SNTP is blocked; ClockFloorRTC gains a
  MAX_PLAUSIBLE_EPOCH guard (all boards) after a garbage-future hardware RTC
  read (2043) latched the ratchet.
- Brightness: RM69A10 DCS 0x51 driven live (CC slider + persisted pref).
- Sound: ES8311 + NS4150B notification chimes (P4Audio, esp-bsp-derived
  register sequence on Wire1), full HAS_UI_SOUND surface + CC Screen/Sound
  sliders; ccVolumeReleaseCb's hardcoded tanBeep -> uiSoundPreview.
- tdisplay_p4/: IDF project (build.sh, c6_at AT-over-SDIO driver, sdkconfigs,
  fetch-deps.sh vendoring, .gitignore for local payloads).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:13:23 +02:00
Tesso M Costa 0adcd26b78 Revert "pager: hide the touch-only chat jump-to-oldest/latest arrows"
This reverts commit 53a60dfbb2.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-14 21:52:21 -06:00
Tesso M Costa 53a60dfbb2 pager: hide the touch-only chat jump-to-oldest/latest arrows
Both floating jump arrows in the open-chat view are touch-only
affordances with no purpose on the T-LoRa Pager (no touchscreen), so
skip creating them on that board and leave the pointers null like
every other consumer already expects. The pager's existing
Backspace-tap "jump to latest" shortcut no longer depends on the
button object itself -- it checks chatVirtAwayFromBottom() directly
and calls chatVirtJumpToLatest(), the same virtualization-aware jump
the button's own click handler used.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-14 17:54:55 -06:00
Tesso M Costa e5cd766f73 pager: move Home-jump to Alt+Backspace, works everywhere including mid-edit
Alt+Shift previously did double duty: toggle Caps Lock while editing a
field, or jump Home otherwise. Splitting these apart -- Alt+Backspace is
now the dedicated Home-jump gesture, and unlike Alt+Shift it isn't
context-dependent: it works whether or not a field is being edited, so
it's reachable from anywhere without first backing out of what you're
typing. Alt+Shift outside a field is now a no-op instead.

PagerKeyboard.cpp suppresses a Backspace press entirely while Alt is
held (no '\b' ring-push, no hold-to-back/hold-to-unlock tracking) and
reports it as a one-shot chord instead, mirroring the existing Alt+Shift
chord plumbing. Also closes the accent/@-mention popups first, since
those live on lv_layer_top() outside the tab content the Home jump
switches away from.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-14 12:15:29 -06:00
Tesso M Costa 5c97d02fbe Merge remote-tracking branch 'origin/main' into tlora-pager-port-lr1121
Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>

# Conflicts:
#	NOTICE
#	platformio.ini
#	src/ui-touch/UITask.cpp
#	src/ui-touch/device_caps.h
2026-07-14 10:45:53 -06:00
Tesso M Costa 6073b6fa4e ui: app-drawer tiles use their own accent tint for keyboard/encoder focus
App tiles previously fell through to navFocusCb's generic white
reverse-video fill when focused via keyboard/encoder nav, instead of
the accent-tinted LV_STATE_PRESSED look they already show on a touch
press -- a solid white block over a small rounded icon chip reads as a
glitch, not a highlight, and looked inconsistent with the touch-driven
boards. Tiles now opt into the same accent tint for both states.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-14 09:29:06 -06:00
Tesso M Costa 57802eba3d pager: make the @-mention contact picker encoder/keyboard-navigable
Typing "@partial" in a composer popped up a touch-only contact list
(NAV_SKIP_FLAG cells, same as the accent-variant box before it got the
same treatment) -- on this board's no-touch input model there was no
way to actually pick a name from it. Mirrors the accent box's pager nav
(rotary encoder walks the highlighted cell, Enter/click confirms,
Backspace cancels without touching the typed text), but grabs the
encoder the instant the list appears rather than requiring an explicit
Fn+Space arm: unlike the accent box (which pops up after almost every
letter typed and must not steal focus from ongoing typing), the mention
list only shows once the user has deliberately typed "@partial" looking
for someone to pick, so there's no ongoing typing to protect.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-14 09:28:29 -06:00
Tesso M Costa b16206bf7d pager: Fn+Shift toggles Caps Lock only while editing a field, else jumps Home
The Fn(Alt)+Shift chord previously toggled Caps Lock unconditionally,
even with no text field focused to see the change reflected in --
reported as surprising and purposeless outside of typing. PagerKeyboard
now only reports the chord (pagerKeyboardConsumeAltShiftChord()); the UI
decides the effect: Caps Lock toggle while actually editing a field,
otherwise a direct jump to the Home screen (mirrors the "ta" derivation
handleHwKey() already uses to tell bound-but-unfocused fields apart from
one actually being edited).

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-14 08:46:55 -06:00
Tesso M Costa c4c28d493d pager: rotary encoder short click opens the message action menu
A focused chat bubble already opened its Ack/Mention/Copy/Info/Block
action menu on Enter (navEnterBubble), but the encoder's short click
only sent a plain ENTER, which a bubble doesn't otherwise react to.
Mirrors handleHwKey()'s Enter branch so both inputs agree.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-14 08:46:18 -06:00
Kaj SchittecatandClaude Opus 4.8 687eb3865c touch: add Heltec WiFi LoRa 32 V4-R8 board (8 MB octal PSRAM + Expansion Kit V2 SD)
The V4-R8 uses an ESP32-S3R8 (8 MB OCTAL PSRAM) which claims GPIO33-37, so
Heltec moved every control signal off them and the Expansion Kit V2 rebuilt the
display/touch/SD block onto a hardware-SPI bus:
  Vext 36->40 (active-LOW) . GPS_EN 34->42 . LED 35->46 . ADC_Ctrl removed
  TFT (LovyanGFX HW-SPI): MOSI=15 SCK=16 MISO=45 CS=47 DC=48 RST=21 BL=44
  touch (CHSC6x, shares board I2C 17/18) . micro-SD (shared TFT SPI, CS=3)

The build defines HELTEC_LORA_V4_TFT too, so it reuses all the V4 touch-UI code;
HELTEC_LORA_V4_R8 gates only the deltas. 8 MB enables the on-device web browser
(CAP_WEB_BROWSER) and the SD card (CAP_SD). Also wires the board into the release
+ web-flasher pipeline (release.sh, gen-flasher-meta.py, the flasher picker).

UNTESTED — no V4-R8 hardware yet. Pin sources: Meshtastic heltec_v4_r8 variant,
Heltec V4-R8 pinmap, and the Expansion_board_V2.03 schematic. All three touch
envs build clean; V4/T-Deck unregressed. On-device TODO: CHSC6x shared-Wire on
17/18, SD_CS=3, and the V2 PAM8904 buzzer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 11:27:40 +02:00
Tesso M Costa 9881bb0038 pager: WASD pans the Map tab, move slider nudge to Q/E
Reuses mapNudge() (previously Tanmatsu's Ctrl+Arrow-only helper, now
shared) so W/A/S/D pan the map north/west/south/east on this board's
touch-less Map tab. Frees up D from the slider-nudge binding, which
moves to Q/E so the two gestures don't collide when a slider (e.g. the
map zoom bar) is focused while WASD panning is in play.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-13 13:15:16 -06:00
Tesso M Costa 43f040ec0a pager: make the accent-variant popup reachable via Fn+Space + encoder
The accent-variant popup (issue #22) that appears above the field when
typing a letter with accented variants was touch-only -- its cells are
NAV_SKIP_FLAG, deliberately excluded from the keyboard/encoder focus
group. On the pager (no touch at all) this made the popup pure visual
noise: it would appear and just sit there unreachable.

Added a small parallel nav path scoped to this board: Fn (Alt) held +
Space jumps into the popup (instead of typing a space or the usual
double-tap-cycle-layout action); the rotary encoder then walks the
highlighted variant; the encoder's own click confirms (reusing the
existing accentBoxCellCb via a synthesized CLICKED event, rather than
duplicating its delete-base-letter + insert-variant logic); Backspace
cancels without deleting the base letter. Everything else is swallowed
while active so a stray keypress can't leak into the message mid-pick.
Focus never actually leaves the text field throughout -- the popup's
cells stay outside the normal nav group the whole time -- so nothing
extra is needed to "return" focus after a pick or cancel.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-13 10:45:31 -06:00
Kaj SchittecatandClaude Opus 4.8 c22c76b704 docs: add Web browser section + screenshots to the site docs
New "Web browser" section in deploy/site/docs.html (and a TOC entry)
covering the on-device text browser and tappable chat links
("Open in web" / "Create QR"), with three new device screenshots
(app_web, app_web_links, app_web_qr). Existing screenshots unchanged.

Also render the reader page inline in the DOC_CAPTURE tour: the reader
paints from the UI-update poll, which doesn't run while the tour blocks
the loop, so the tour now drives the fetch-wait + render itself before
capturing the Web app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:08:43 +02:00
Tesso M Costa 79df5281e4 pager: real Shift-hold, Alt+Shift chord for Caps Lock
The Shift/Caps key previously toggled Caps Lock on every bare press --
a deliberate design choice reused from a working reference build, but
not conventional keyboard behavior. Changed to: held alone, it's a
momentary Shift (uppercases letters typed while held, same as a real
Shift key); held Alt then a Shift press instead chords into a toggle
for persistent Caps Lock. kCapsPos renamed kShiftPos to match its new
primary role -- zero other call sites reference it, and s_caps itself
is unchanged (still just "is Caps Lock engaged", only the trigger
changed).

Alt (row2,col0) and Shift (row2,col8) share a matrix row, so holding
all three of Alt+Shift+O or Alt+Shift+L at once phantom-ghosts a
'q'/'a' (diode-less-matrix 3-key rectangle, no software fix possible)
-- documented in the matrix-legend comment; harmless in practice since
the intended gesture is hold-Alt-tap-Shift-release-both, not holding
all three down together.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-13 09:57:39 -06:00
Kaj SchittecatandClaude Opus 4.8 308be40b96 touch: beta_44 — on-device text web browser + tappable chat links + French keyboard fix
- New "Web" app: on-device HTTPS text browser (strip HTML to readable text,
  tappable links, back/fwd/refresh, no proxy). Gated to 8 MB boards via
  CAP_WEB_BROWSER; hidden on the 2 MB V4 (can't do the TLS handshake).
- Tappable URLs in chat -> Open in web (8 MB) / Create QR (all boards).
- Drawn vector globe icon for the Web tile.
- Fix: French AZERTY on-screen keyboard lost its spacebar/OK/kbd-switch row
  (a stray "" mid-map terminated the LVGL button map early). Restored.
- V4 web fetch resilience (192->96->48->24 KB buffer fallback).
- Home signal box: "tap for details" cue back at the top.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:26:05 +02:00
Kaj SchittecatandClaude Opus 4.8 d787d77365 docs: web control panel + Remote/VNC guide, refreshed device shots
- New "Remote & web app" docs section covering the browser control panel
  (Chats / Contacts / Discovered / Settings / Terminal), Screen mirror (VNC)
  and Remote UI, with a 7-shot phone gallery of the real web app.
- Refreshed all device screenshots via the DOC_CAPTURE tour (now also
  captures the Remote screen) and added the MQTT bridge settings page.
- New automated web-capture harness (scripts/doc/web-demo/): extracts the
  real page from firmware, serves it with mock data, screenshots via Playwright.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 17:18:20 +02:00
Kaj SchittecatandClaude Opus 4.8 831959ea5c touch: beta_43 - web control panel becomes a full remote app + V4 map-tile fix
The browser control panel gains full Chats / Contacts / Discovered / Settings
tabs (search, sort, filter, per-contact actions, notifications + live refresh,
ACK/repeats, add-from-discovered, live radio settings, quick-command picker).

Heltec V4 map tiles: fixed the "only the top renders" bug on the 2 MB-PSRAM
board with a persistent tile-buffer pool + viewport culling (no more per-render
128 KB churn/fragmentation). Adds an opt-in "Tile debug overlay" (Map options).

Also: V4 physical-keyboard input in remote/VNC, clearer remote-access screen,
complete Terminal help on device + web, WADAMESH header + status icons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 16:29:08 +02:00
Kaj SchittecatandClaude Opus 4.8 4df391a283 touch: beta_42 - REMOTE fits the Heltec V4 + keyboard-less exit
Remote mode showed a black screen on 2 MB-PSRAM boards (Heltec V4): the
mirror ring/shadow were too big to allocate. Size the ring to one frame
and drop the remote render to 400x240 so the whole footprint is smaller
than VNC's (which already worked). Add a touch-and-hold (3 s) exit for
keyboard-less touch boards and an Exit button on the web page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 23:34:30 +02:00
Kaj SchittecatandClaude Opus 4.8 3f375f2fe4 touch: beta_41 - VNC + REMOTE web mirror (experimental)
Stream the live LVGL UI to a phone/desktop browser over the WS companion
server (VNC app) plus a headless REMOTE mode that renders the UI at a
browser-friendly resolution. Non-blocking per-client TX buffering, a
core-0 stream task guarded by a recursive mutex, an on-page Rotate
orientation toggle, and a small 480x288 render upscaled in the browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 22:32:25 +02:00
Tesso M Costa 1514e7638c pager: pick notification WAVs from the SD card
Completes the microSD work from the previous commit: wavOpen()'s
"sd:"-prefixed path branch, fmIsAudio()/fmOpenAudio() (the .wav ->
notification-sound chooser opened from the File Manager), and the
popup-dismiss registry entry for it (so Back/Escape via the encoder or
keyboard closes the picker, not just the on-screen X) are all widened
the same way as the previous commit's browsing support -- specific
call sites only, CAP_SD/CAP_FILESYSTEM stay 0 in device_caps.h.

Also updates that file's now-stale CAP_SOUND_FILES comment, which said
the pager's WAV picker only ever reads from SPIFFS -- it can read from
the SD card now too.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-10 16:25:16 -06:00
Tesso M Costa 8c7fb6af5c pager: mount the microSD card and browse it in the File Manager
pagerKeyboardSetBacklight-style dead-hardware situation: the board-level
SD wiring (power rail, card-detect, chip-select parking, shared-SPI bus
reuse) was already done during earlier bring-up, but nothing ever called
SD.begin(), so the slot was unusable. CAP_SD/CAP_FILESYSTEM already
existed for exactly this in device_caps.h, left at 0 with a "pager needs
its own SD wiring" comment.

CAP_SD turns out to gate ~37 call sites across UITask.cpp -- file
manager, WAV picker, DataStore contacts/channels routing, map-tile
fallback, wallpaper scanning, settings backup, screenshots. Scope for
this pass is file-manager browsing only (WAV picker follow-up commit);
flipping the top-level CAP_SD/CAP_FILESYSTEM macros would have silently
turned on all ~37 at once, several referencing T-Deck-only helpers never
vetted for this board. So device_caps.h is untouched -- CAP_SD/
CAP_FILESYSTEM stay 0 for the pager -- and only the specific call sites
needed for browsing are widened with `|| defined(TLORA_PAGER)`.

- fmSdTryMount()/fmSdUnmount()/fmSdClickCb(): widened, plus a pager-only
  sdSharedSPI() (reuses TFT_eSPI::getSPIinstance(), the same shared bus
  the radio already uses -- variants/lilygo_tlora_pager/target.cpp) and
  a card-detect pre-check via the XL9555 expander (PAGER_EXPAND_SD_DET,
  inverted polarity confirmed against LilyGoLib's own installSD()).
  Format support is explicitly out of scope -- the pager's SD row is
  tap-to-open only, no long-press-to-format.
- fmShowRoots(): pager SD row, shown only when a card is actually
  detected (no T-Deck-style "tap to mount" fallback -- the detect line
  makes "not present" unambiguous).
- fmIsSd(), the CAP_FILESYSTEM-gated Apps-drawer "Files" entry (dispatch
  + grid tile), and the insert/remove poll: widened the same way.

All ~30 other CAP_SD/CAP_FILESYSTEM sites (DataStore SD routing, tile
fallback, wallpaper, telemetry, settings backup, screenshots) are
untouched and stay exactly as unavailable on the pager as before.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-10 16:21:46 -06:00
Tesso M Costa c9033a3823 pager: wire up the keyboard backlight (off/on/auto)
pagerKeyboardSetBacklight() existed but nothing called it. The mode
state, NVS persistence, and boot restore were already generic across
every CAP_KEYBOARD board (s_kb_bl_mode, touchPrefsGetKbBacklight()),
so only two pieces were missing for this board:

- updatePagerKbBacklight(): a per-tick apply step, the pager's
  analog of the T-Deck's inline backlight calc / Tanmatsu's
  tanKbBacklightTick(). No brightness curve needed here (the pager's
  backlight is a plain LEDC PWM strip, not a dimmable slider like the
  T-Deck's) -- on/auto-active drive full duty (255), everything else
  (off, idle, screen-off, locked) drives 0. Runs unconditionally in
  the pager's loop branch so it can force the light dark the instant
  the screen goes off/locked.
- A "Keyboard backlight" row in Settings -> Keyboard (pager-only,
  sibling to the T-Deck's slider block) that cycles off/on/auto,
  mirroring the Control Center chip's semantics -- the Control Center
  itself is unreachable on this board (its launcher is touch-only).

Also fixed auto mode actually responding to input: neither
handleHwKey() nor updatePagerEncoder() called noteKbActivity() (the
function that stamps the auto-mode idle timer) -- they only called
the separate noteUserInput() (the screen idle-dim timer), so auto
mode would light briefly at boot and then never again regardless of
typing or turning the encoder. Both now stamp it too.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
2026-07-10 10:40:25 -06:00
Kaj SchittecatandClaude Opus 4.8 946e73bb0b touch: beta_40 - card-less contacts-save stall fix + long-text auto-fit
Fixes:
- Card-less devices (Heltec V4) froze for seconds: a full contacts rewrite to
  SPIFFS on every advert refresh triggered a multi-second GC in the mesh loop.
  Coalesce advert-driven saves (a contact-set change still saves promptly); SD
  boards unchanged. Flush on reboot/power-off so nothing is lost.
- Long translations (FR/DE/...) overflowed fixed-width labels: new
  uiFitLabelWidth() shrinks the font to fit one line instead of clipping/wrapping
  - applied to the message menu, confirm dialogs, and app-drawer tiles.
- Home signal graph: "tap for details" hint moved to bottom-right so it no longer
  overlaps the "Sig" chip in longer languages.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 18:31:26 +02:00
Kaj SchittecatandClaude Opus 4.8 9547ce6ede touch: chat-history persistence fix + faster delete + emoji tofu fix
Persistence (Leon P / MrWeavis, beta_38):
- Deleting a message or clearing a chat now persists synchronously, so it
  survives an immediate hardware reset instead of "coming back" (the tombstone
  used to ride the 30 s lazy SD flush, which a reset beats).
- The history file is now written compactly — only the used records,
  oldest-first, instead of all ~5000 ring slots every time. A typical chat
  writes ~48 KB instead of ~1.2 MB, so the synchronous delete/clear/reboot
  flush no longer hitches the UI (and normal message flushes are lighter too).
  Old history files still load (the reader derives its slot count from size).

Emoji picker:
- Baked the 14 glyphs that were listed but never in the font (umbrella, coffee,
  pizza, cake, rocket, camera, phone, laptop, calendar, antenna, e-mail, house,
  car, watch); they rendered as the notdef box.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:38:37 +02:00
Kaj SchittecatandClaude Opus 4.8 49a8f31b8c touch(i18n): full untranslated-text audit + 307 new translation rows
Audited every user-visible string sink across the touch UI and closed the
gaps that had accumulated since the language table was last touched:

- TR() now strips a leading LVGL-symbol glyph (+ spaces) off the lookup key
  and rebuilds the label, so icon-prefixed menu/sheet rows ("<glyph>  Copy")
  translate instead of silently falling back to English.
- Wrapped 95 bare literal sinks (settingsRowLabel / showAlert / showConfirm /
  lv_label_set_text / dropdown / placeholder / checkbox) in TR(); unwrapped 29
  format-only strings that never should have been keys.
- Added 307 missing rows to kI18n[] covering all 11 non-English languages
  (NL DE FR ES IT RU UK BG SR EL PT-BR); keys byte-identical, printf
  placeholders and %-order preserved, Serbian in Cyrillic.

Builds green on T-Deck + Heltec V4; Tanmatsu app 3,169,296 B (107 KB under
the AppFS 50-page load ceiling).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 19:38:07 +02:00
Kaj 814c8f5941 Merge pull request #130 from scratchdiver/steve/touch-chat-ui-pr
touch: virtualize chat scrolling (bubble + compact)
2026-07-09 13:14:44 +02:00
Kaj SchittecatandClaude Fable 5 76cd025bf5 tanmatsu: move identity + prefs to the SD card; open-probe the prefs load chain
The P4's internal FFat 'locfd' has a broken FAT metadata layer: open/
read/write work but f_stat/exists()/size() return garbage - and WHICH
garbage shifts with the build. At -Og the exists()-gated identity and
NodePrefs loads worked by luck; the -Os switch flipped the lie and the
device booted with a fresh identity and default name, and profile
changes never survived a reboot (the loads failed, not the saves).
Chat history was unaffected because it already lives on SD_MMC.

- DataStore::useSdMmcStorage(): full-store adoption of the card
  (/meshcomod root, identity store included), mirroring the T-Deck's
  useSdStorage(). The old card-root contacts3/channels2/adv_blobs from
  the secondary-FS era are renamed under /meshcomod.
- Tanmatsu boot: one-time rescue of identity + prefs off FFat by OPEN
  and READ probing (never exists()/size() on that FS), then adopt the
  card. FFat remains the no-card fallback only.
- DataStore::loadPrefs() probes candidates by opening and reading a
  byte instead of exists() - truthful on the P4, identical elsewhere.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:00:54 +02:00
Kaj SchittecatandClaude Fable 5 d20241627c touch: flush chat history on the companion CMD_REBOOT (fix messages lost on app-initiated reboot)
Since the beta_35 lazy history writer, message-ring flushes are spaced
up to 30 s apart on the deep SD ring. Every on-device reboot/power path
compensates by calling persistHistoryNow() first, but the companion
protocol's CMD_REBOOT (the phone app's reboot button) went straight to
board.reboot(), dropping everything since the last flush - the newest,
just-read messages, which reads as 'my channel and DM messages get
deleted after a manual reboot' (Leon P, T-Deck under Launcher, beta_35
through beta_37).

persistHistoryNow() is now an AbstractUITask hook (default no-op, so
non-touch companions are unchanged) and CMD_REBOOT calls it before
rebooting, matching the power-menu contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:48:53 +02:00
Kaj SchittecatandClaude Fable 5 a49726d4ce touch(tdeck): migrate SPIFFS data to SD on adoption + recovery copy button (beta_36 lost-profile fix)
Users who flipped 'Store data on SD' before beta_36 ran with the toggle
IGNORED (the flag never survived a reboot), so their identity, prefs and
contacts kept living on SPIFFS. When beta_36 made the flag finally take
effect, useSdStorage() pointed the store at an EMPTY card, the identity
store minted a brand-new node, and the user 'lost' their profile. The
data was never gone - it was orphaned on internal flash.

Two-part fix:
- Boot: adopting the card while SPIFFS holds an identity the card lacks
  now copies every SPIFFS file into SD:/meshcomod first (kv files
  flatten to the card's SdNvsPrefs layout). If the identity file fails
  to land, the device stays on SPIFFS for the boot instead of adopting
  a card without it.
- Settings -> Storage gains 'Copy internal data to SD' for the users
  already bitten (their card now holds a fresh identity the boot-time
  no-clobber gate rightly won't touch): confirmation, overwrite-copy of
  everything from internal flash, force the SD pref on, reboot.

Workaround until this ships: toggle 'Store data on SD' OFF and reboot -
the device reads internal flash again and the old profile returns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:29:54 +02:00
Kaj SchittecatandClaude Fable 5 6aa1147eb3 touch: message action menu lays out two buttons per row
With Delete the menu carries up to 7 actions; a single column no longer
fit the 240-px screens without scrolling. Two per row (wider 200-px card,
odd last button on its own row) keeps the whole menu on one screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:06:52 +02:00
Kaj SchittecatandClaude Fable 5 d9f45405d5 touch: slim scroll-fade jump arrows + message delete + per-chat history wipe
- The chat jump buttons are bare arrows flush against the right edge now
  (28x36 transparent touch target, accent-colored glyph) instead of 40 px
  filled circles; they run at full opacity while the list scrolls and dim
  to 50 percent one second after the last scroll activity. The Tanmatsu
  keeps its F-key chip styling and geometry.
- The long-press message menu gains a Delete row: the message is
  tombstoned in the ring (thread tag blanked, so every walker skips the
  slot and live ring indexes stay valid) and both the chat and the thread
  list refresh.
- The chat/channel settings sheet (in-chat gear and list long-press)
  gains a 'Delete history' row for both DMs and channels: confirmation
  prompt, then every message of the thread is tombstoned. The thread row,
  channel membership and contact stay; only the conversation is wiped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:01:46 +02:00
Kaj SchittecatandClaude Fable 5 9785d8b7d1 fix(chat-virt): review fixes from the PR 130 deep review
- chatVirtReset no longer queues lv_obj_del_async on the divider: the
  empty-thread paths lv_obj_clean two lines later freed it first and the
  deferred lv_obj_del then ran on freed memory (confirmed UAF, plus a
  cross-panel variant when both panels are open). Downstream cleans own
  the widget; the reset only nulls the pointer now.
- Compact rows register bubbleLongPressMenuCb for LV_EVENT_LONG_PRESSED
  (the only event the callback acts on) instead of PRESSED/RELEASED/
  PRESS_LOST/DELETE, all of which it filtered out - the per-message menu
  was completely dead in compact mode.
- Bubble mode gets tap-to-resend back on failed sends (CLICKED handler
  plus the 'tap to resend' footer), matching the compact path and the
  pre-virtualization behavior.
- need_layout gains a content-generation guard (endpoint ring slots): at
  ring capacity n stays constant while content rotates, and equal-count
  thread switches reused stale offsets - rows rendered the next message's
  text at the previous message's position. The dead dirty_timeline rescue
  (cleared before its async consumer could read it) is removed.
- New chatVirtTryAppendLayout fast path: pure appends extend offsets for
  the new tail instead of re-measuring the whole thread (the full measure
  is ~100 ms of UI stall per received message on a 5000-message SD ring).
- lv_async_call failure no longer wedges the render busy flag; the nav
  focus fallback validates s_nav_objs[0] before focusing it (bubbles now
  recycle on scroll-end, so slot 0 can be one tick stale); dead
  chatVirtClearAllChildren/DeleteMsgsChild pair removed; stray
  FIRMWARE_BUILD_DATE bump reverted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 11:38:25 +02:00