Commit Graph
13 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 5 a8f813c5f9 Merge PR #599 (oumike): battery saver, Condense Nav, Pager keyboard nav
Closes #591, #592, #593 and the M9 Bluetooth status-bar overlap museifu696
reported. Also the T-Pager battery saver zomgugoff asked for and the status-bar
clock fix.

Review notes, since this landed on top of a day of P4 and M9 work:

* The prefs change is done the right way. condense_nav is appended at the TAIL,
  CURRENT_VERSION goes 65 -> 68 with v66/v67/v68 each described, and the
  trailing static_assert moves to the new last field. v67 and v68 are one-time
  per-board resets with no new field, which is the pattern v63 and v65 used.
* Six conflicts, all in test_touch_prefs_schema.cpp, all the same shape: we had
  both repaired the same broken sums. His version is a superset (it adds
  condense_nav on top of the report_ping/report_done_n I added), so his wins.
  Verified afterwards that both sides survived rather than trusting the merge:
  the channel-send guard, the P4 tile-FS fix, statusPctOverflow, m9NoticeSet,
  m9DiscoveredUnseen and TDP4_C6_ID are all still present, as are his kbd_nav
  and Condense Nav work. Host suite 17 pass / 0 fail at v68.

Also here: PUBLISH the translations the PR added.

It added five strings to all fourteen language files with real translations, but
left "# ver: 22" alone and did not touch langs.json. Devices fetch lang/<ver>/,
so the version is the only thing that makes one re-download: without a bump the
work sits in the repo and reaches nobody, and every non-English device shows
those strings in English. The baked-in table was regenerated, so compiled-in
languages were fine -- it is the downloaded ones that would have silently
stayed behind. Bumped to 23, snapshotted into lang/23/, and langs.json points
at it. deploy-apps.sh validates: 14 languages and 14 apps, every referenced
file present.

That store still has to be pushed with the release; editing deploy/apps/ is not
publishing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 12:30:55 +02:00
Kaj SchittecatandClaude Opus 5 820bc21f46 build: verify the P4 artifacts, and make the host tests runnable
The T-Display P4 ships EIGHT images: panel (AMOLED/LCD) x radio (SX1262/LR2021)
x C6 stack (esp-hosted/ESP-AT). The C6 axis is new and it is the dangerous one,
because the two builds are identical apart from the artifact name they carry,
and flashing the wrong one at an older unit used to boot-loop the board.

scripts/build/p4-release.sh builds all eight and VERIFIES each image against the
OTA name baked into it before accepting it. That is the same string the device
builds its self-update URL from, so a disagreement with the filename is exactly
the failure that bricks people -- the T-Deck Max self-updating to Pro firmware,
and this table has been wrong three times. A mismatch aborts the release.

It earned that check immediately: building the eight by hand produced two
mislabelled images, and every mechanism meant to prevent it was itself broken.

  * build.sh fullclean has NEVER worked from a plain shell. It ran `cmake -E
    remove_directory` above the esp-idf/export.sh that puts cmake on PATH, so it
    printed that it had removed the tree, exited 0, and removed nothing. Now a
    plain rm -rf, and it verifies the directory is gone instead of assuming.
    This matters beyond tidiness: the hosted and ESP-AT builds SHARE a build dir
    (the C6 axis does not change P4_BUILD_TARGET), the backend is read from the
    environment at CMake CONFIGURE time, and CMake does not reconfigure because
    an env var changed -- so a clean that silently did nothing yields an
    incremental build of the OTHER variant, with no configure output to show for
    it. That is precisely how the two mislabelled images were produced.
  * Deleting CMakeCache.txt alone is not a clean either; it leaves CMakeFiles/
    behind and the next configure dies in CMakeDetermineCCompiler.
  * Detecting failure by grepping a log for "error:" misses a failed CMake
    configure, which prints "Configuring incomplete" and no such line -- and the
    previous application.bin is still sitting in the directory to be picked up.
    Exit codes now.

gen-flasher-meta.py gains the four ESP-AT entries, labelled "older unit, ESP-AT
C6": someone choosing in the web flasher has no serial log and needs to pick on
something they can check.

scripts/run-host-tests.sh runs test/ as one command. Nothing ever did, which is
why test_touch_prefs_schema had been failing to COMPILE since v64 appended
report_ping and report_done_n without updating its sums -- and a static_assert
that does not compile is not a guard. That one exists to catch the mid-struct
insert that broke v44 and shipped the poisoned beta_57 layout. Repaired, with a
note where the next person appending a field will read it. 17 pass, 0 fail, 2
skipped with their reasons stated rather than quietly dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 11:31:36 +02:00
Michael A. CojocariandClaude Opus 5.5 08750ab3a1 feat(ui): Condense Nav, a landscape nav rail with icon-only Home launchers (#592)
On short landscape panels (the T-Pager is 480x222) the bottom nav bar
spends a full-width strip on five icons. New Settings > Display switch
"Condense Nav", off by default, applies after a restart:

- The nav bar becomes a rail on the right edge (LV_DIR_RIGHT tabview).
  Pager cells show the key letter under the icon; the Tanmatsu keeps its
  coloured F-key shapes; the T-Deck / P4 key hints sit under the icons.
- Home's launcher column (Advert, Terminal, Files, Apps, Control) shows
  icons only; the buttons keep their height and spacing, and the status
  text, chart and info card widen into the freed space.
- The active-tab marker turns vertical beside the active cell, the
  unread and update badges move to the top-right of their cells, and the
  rail stays solid (and the marker visible) on the Map.
- Content that kept the bottom bar visible now stops at the rail
  instead: tabContentW() subtracts the rail, and the map canvas, Settings
  landing grid, chat-list rows, Mentions and the app drawer use it.
  Swipe-left on the rail opens the drawer (swipe-up on the bottom bar).

Portrait layouts and the M9 are unchanged: the mode is decided once in
buildUiTree from the pref and the orientation at boot, the switch is
greyed out in portrait and absent on the M9.

TABBAR_H becomes a runtime value (0 with the rail) next to a new
TABBAR_W. The pref is a new trailing field, condense_nav (schema v66);
the schema test's layout asserts now count the v64 fields they missed.
Strings added to every deploy/apps/lang file and i18n_builtin.h
regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
2026-10-04 13:47:46 -04:00
Michael A. Cojocari 0aa2f28c16 feat(ui): add high-contrast day and night themes 2026-09-20 19:08:45 -04:00
Kaj SchittecatandClaude Opus 5 793a673c64 touch: Bluetooth LE keyboards on every S3 board
Bluetooth serves either the phone app or a keyboard (Settings > Bluetooth).
Pairing lists keyboards in pairing mode and pairs with or without a code;
the paired keyboard reconnects by itself.

Keys are read the way phones and computers read them: the keyboard's Report
Map says which report carries the keys and how, so media keys and touchpads
are left alone, and the boot protocol is only the fallback. Key positions are
translated with the chosen layout (US, UK, German, French, Belgian), with
AltGr and dead keys.

While a keyboard is connected:
- text goes into the focused field, and touchscreen-only boards keep the
  on-screen keyboard down (a second tap on a field brings it up anyway)
- the arrows, Tab, Enter and Page Up/Down drive the focus highlight
- Esc is the back button; a "Back key" setting picks another key for
  keyboards whose Esc key types a character
- tab hotkeys and their hints over the tab bar are switched on
- Command tapped alone opens the emoji picker in a chat
- the status bar shows a keyboard instead of the Bluetooth glyph

The Bluetooth page is now a flex column with the choice on top, and the
settings page refit keeps it scrollable when its content grows.

Settings: prefs v61 (mode, layout, paired keyboard) and v62 (Back key).
Tests: test/test_hid_report_map.cpp, test_touch_prefs_schema.cpp.
Not on the P4 boards (Tanmatsu, T-Display P4).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 19:52:21 +02:00
Michael A. Cojocari 2a481ef64c Add M9 lock-home-to-drawer option 2026-09-10 20:04:57 -04:00
Kaj SchittecatandClaude Opus 5 503b2fe1f2 touch: sound previews obey the master switch (#464), exact telemetry position (v59)
#464 (PD0RCM, T-Deck Plus): "I can hear the sample sound and the volume change,
but not incoming messages." That board has the hardware, and previews and
arrivals go through the same playback function on it, so the audio path was
never the problem.

Every preview on the Sound page played UNCONDITIONALLY: the sample, the volume
steps, and each per-type toggle. An arriving message checks the master Sound
switch and Do Not Disturb. So with the master off the page chimes at you the
entire time you configure it while nothing ever sounds on arrival, which reads
exactly like a broken notification path and is almost certainly what he hit.
A preview is now silent precisely when a real notification would be, and says
which of the two silenced it rather than just going quiet. The master switch
keeps its own confirmation chime, because that fires exactly when you turn it
ON, which is the one case where a sound is the right answer.

Telemetry position (v59, honza_87628, via Discord): the advert displacement of
#399 was extended to telemetry answers because a privacy setting a telemetry
request walks straight around is a hole rather than a feature. He then made the
fair counter-argument that an answer is not a broadcast: it is encrypted to one
contact who already holds the permission, and it only goes out because they
asked. Losing the ability to give trusted contacts a real fix is a real loss.

So make it a choice instead of an assumption. New tail field telem_loc_exact,
OFF by default, so the private behaviour is what you get unless you say
otherwise, and the broadcast advert stays displaced either way. The switch sits
directly under "Share my location when asked", where the decision belongs.

Schema 58 -> 59. The host test enumerates every appended field and caught the
change, as designed; all three of its migration assertions are updated and it
passes.

Reported-by: PD0RCM, honza_87628
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 11:37:54 +02:00
Michael A. Cojocari 28fb57ef4c Fix Attaky usability issues (#423) 2026-09-06 16:55:41 -04:00
Kaj SchittecatandClaude Opus 5 ee15fcb335 Fix two beta_76 regressions of mine, and advertise a position without your address
#415: a Lua app was covered by the app drawer whenever a message arrived.
Reported by jadestarwatcher, reproduced by mysterywavi, both on T-Deck. Mine,
from the #393 badge fix. Apps launched from the drawer deliberately leave it
alive underneath (appTileCb says so outright), and my badge refresh rebuilt it on
every unread-count change, with openAppDrawer() ending in a move_foreground that
threw it on top of the running app.

The refresh now runs only when nothing is drawn in front of the drawer, tested by
sibling order rather than by listing the tools, so a tool added later is covered
without anyone remembering to update a list. The status bar is excluded, since it
legitimately floats above the drawer at all times and treating it as covering
would have stopped the badges refreshing at all. Leaving the signature stale is
what makes it self-healing: the check runs again each tick, so the count is right
by the time the drawer is back in front, with no need to hook every close path.
Scroll position is now preserved across the rebuild too, which my change had also
been resetting.

#410: on the M9 the accent variants appeared but could not be selected. Also mine,
from the #387 work. The picker was handled below m9HandleArrowKey, which takes
LEFT and RIGHT for the caret and returns, so the arrows never reached it: the box
was drawn and nothing could touch it. Lifted above that call, as its own function
rather than a second copy of the logic.

#399, requested by @Danie10: an option to advertise a position near you rather
than your address. Settings, GPS, cycling exact / 100 m / 250 m / 1 km.

The displacement is derived from the node identity, so it is the same offset
every time. That is the whole point rather than an implementation detail: a fresh
random offset per advert would scatter points around the true position, and
averaging a night of them would recover the centre exactly. A fixed displacement
instead looks like a node that sits somewhere else, which is what a manually set
location already looks like. It applies only to our own adverts; the map and the
GPS page keep the real fix, because the aim is to tell other people less, not to
lie to yourself.

Schema v57. The host test caught the new field the moment it was added, which is
what it is for.

All nine S3 envs and both ESP32-P4 targets green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 09:57:25 +02:00
Kaj SchittecatandClaude Opus 5 2a8b62c6a7 Merge PR #375: Day and Night themes (#296)
oumike. A firmware-wide appearance setting: Night stays the default and renders
exactly as before, Day is a low-glare light palette with its own semantic roles
for text, panels, fields, borders, controls, focus, charts, status colours and
chat surfaces. Selecting a mode restarts, so every LVGL object is rebuilt from
one coherent palette rather than half-repainted. There is a Night/Day selector
in Settings, Display, the Control Center Theme chip is now a direct toggle whose
sun/moon icon shows the active mode, and the standalone console UI has a
matching Day palette.

Main already carried the palette scaffolding for this, pinned to Night behind a
comment saying it was waiting on this branch, so the merge mostly replaces those
stubs with the real thing.

The prefs schema needed care. The branch appended theme_mode as v54, but v54 and
v55 were taken by boot_wifi_time/boot_wifi_open and loud_alerts before this
merged, so the field moves to the tail behind them and the version becomes v56,
with the trailing-field assert and the migration step moved to match. A packed
struct read back at the wrong offsets is the failure this schema's asserts exist
to prevent, so the invariants from both sides are kept rather than one replacing
the other.

That also surfaced a stale test: the v53 case asserted that v53 plus exactly two
bytes was the whole struct, which stopped being true when loud_alerts landed and
was not caught because the host test was not run then. It now counts every byte
appended since and asserts the new fields come back at their defaults. The
schema host tests pass. While there, the size comment said "about 500 bytes";
the struct is 114.

Built on all nine S3 envs and both ESP32-P4 targets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 09:11:54 +02:00
Michael A. Cojocari 092528a8fb Fix RTC retention and cold-boot time sync (#383) 2026-09-02 21:37:35 -04:00
Michael A. Cojocari 952075a22e Day/Night 2026-08-31 09:37:45 -04:00
Pixel Perfect 65f25334c4 fix(prefs): repair beta 57 config migration
Beta 57 inserted retry_echo into the middle of the packed TouchCfg blob, shifting the remote-mode and later fields during migration. Define a v45 layout with retry_echo appended at the true tail and recover ambiguous v44 suffix bytes to safe defaults while preserving the stable prefix. Add a host regression test for v43, broken v44, and v45 blobs.

Signed-off-by: Pixel Perfect <me@pixp.cc>
2026-08-03 19:01:35 -07:00