Skip to content

Theming, GPS time, mesh, setup, Launcher, and more - #728

Merged
KenVanHoeylandt merged 13 commits into
mainfrom
develop
Oct 31, 2026
Merged

KenVanHoeylandt merged 13 commits into
mainfrom
develop

Conversation

@KenVanHoeylandt

@KenVanHoeylandt KenVanHoeylandt commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Mesh

  • Backend id validation: mesh_backend_register() rejects ids that are empty or contain characters other than letters, digits, _ and - (ERROR_INVALID_ARGUMENT). Blocks ../escape storage paths.
  • Storage events: configure() emits MESH_EVENT_CONTACT and MESH_EVENT_CHANNEL when the selected backend's storage changes. Consumers reload without a radio.
  • State event on backend switch: a backend becoming active while the state is already ON now emits MESH_EVENT_STATE. Fixes the Mesh settings app not showing a Meshtastic to MeshCore switch, and stale channel lists.
  • Store opened before publish: onStart opens the selected backend's store before publishing service. The store is closed on thread-start failure.
  • Query errors: mesh_history_get, mesh_conversation_get and mesh_get_contacts return ERROR_INVALID_STATE when a backend is selected but its store is closed.
  • Contacts load state: loadContacts() reports failure and the service tracks contactsLoaded. Contact add, remove and save are blocked until a load succeeds, so a failed read can't overwrite saved contacts. mesh_get_contacts returns an error meanwhile. A failed load is retried on the next configure().
  • Feature check: new enum MeshFeature (MESH_FEATURE_NAMED_CHANNELS, MESH_FEATURE_KEYED_CHANNELS) in mesh.h, uint32_t features in MeshBackendApi, and bool mesh_supports(uint32_t features) (exported). MeshCore declares both, Meshtastic only keyed.
  • Channels app: the "Add channel" dialog lists only supported options, and the "+" button is hidden when none are supported.
  • Tests: backend id rejection, storage and state events, contacts load failure (contacts path is a directory), mesh_supports.
  • Finding, no change: "Join public channel" can't work on Meshtastic as implemented. It sends an empty key, which meshtastic_expand_psk() rejects. The protocol's public channels use the default PSK with a name. Not implemented.

Location and GPS time

  • Subscriber descriptions: location_subscribe(sub, event_group, description) takes a description, truncated to 24 bytes at a UTF-8 boundary. New location_for_each_subscriber(). The Location settings app shows a "Subscribers" card (focusable list) at the bottom, refreshed every second.
  • Time event restrictions in location_host_report():
    • only from the selected provider (isBest)
    • only from providers with the new provides_time flag (GPS)
    • only for plausible dates, 2025-01-01 to 2100-01-01
  • GPS time service:
    • subscribes to location as soon as location services are enabled, via a 2 s timer
    • unsubscribes after the time was announced
  • Setup "Time from GPS" step: defaults to Disabled unless location was enabled in the previous step. Steps can supply a defaultChoice.
  • Time & Date app: the GPS row's explanation text refreshes when location settings change, on the app's task.
  • Tests: subscriber descriptions and UTF-8 truncation, time events once per minute, implausible dates ignored.

Theme (spacing and size)

  • lvgl_theme_lerp() computes in 64-bit, with a test for extreme values.
  • Button padding follows $size instead of $spacing:
    • @density-control-pad-hor/ver in both themes
    • @text-button-pad-hor in material.lss
    • themes regenerated
  • Minimum paddings: vertical 1, horizontal 2.
  • AppearanceSettings loader: an explicit empty spacing= or size= overrides the legacy density key (loads as unset). Test updated.
  • Device properties lvgl.uiDensity migrated to lvgl.uiSpacing and lvgl.uiSize. Tactility/Kconfig has TT_UI_SPACING and TT_UI_SIZE. device.py writes them, with validation. theme.cpp reads them. The docs are updated in ../TactilityDocs, uncommitted.

Setup app

  • Layout:
    • content scrolls above the button bar, so Back and Skip no longer cover it
    • the content takes keyboard focus only when it overflows
    • hidden choice chips are disabled so the focus group skips them
  • Navigation: the spacer in the button row is not clickable, so grid navigation skips it.
  • Last page: "You're all set" focuses Finish when there is no touch screen.
  • Title font: small at size 20 or below, regular below 50, large otherwise.
  • Statusbar: hidden while Setup is shown, via new systemBarsSetStatusbarVisible().
  • A moveButtonsLast() change was made and then reverted at your request.

Boot

  • SD card missing: with power-off support, shows [ Reboot ] / Power off options.
    • keyboard and encoder: arrows, Tab or wheel select, Enter confirms
    • touch: tap selects, long press confirms
    • Without power-off support, the old "Press any key to reboot" prompt remains.
  • Logo selection: picks the largest logo that is at most 60% of the screen width and height and at most an eighth of its area. The smallest is used if none fits. Size is read from the PNG header.
  • Diagnostic: Power off supported: 0/1 log line. Your cl32-v04 was running old firmware, so there was no bug.

Launcher and TileGrid

  • Side mode: the side container is applied before the grid is populated, so the grid is laid out at its final size.
  • Bar buttons: the grid is populated after the bar buttons are added, through the new finishWidgetsWithBottomBar(). Fixes a one-frame jump on lilygo-thmi (portrait, split bars).
  • The jump you reported may still be there. I didn't confirm the split-bars cause.

Kernel

  • path_tree_size() (used by du) is iterative: no recursion, one open directory at a time, work list on the heap. It also fixes the silent skipping of subdirectories beyond 32 per directory and of names of 64 bytes or more. New test path_tree_size_test.cpp.
  • directory_remove_tree() is unchanged and still recursive.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • Resume reviews
  • Trigger review

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8ad5f619-a12d-4865-9884-c37d68813827
Commits

Reviewing files that changed from the base of the PR and between 1c741e4 and 78bdd6e.

Files selected for processing: 3
  • Documentation/ideas.md
  • Tactility/Private/Tactility/app/boot/BootScreen.h
  • Tactility/Source/app/boot/BootInit.cpp
Files skipped from review as they are similar to previous changes: 1
  • Documentation/ideas.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Walkthrough

The pull request adds independent spacing and size controls to LVGL themes and appearance settings. It adds network availability checks, location subscriber descriptions, rate-limited location-time events, and optional GPS time synchronization. Mesh storage becomes backend-specific, with migration from shared storage. Boot screens gain reboot and power-off choices, and setup gains versioned completion and configurable choice pages. The changes also update ESP32 SDK release mappings.

Merge Risk: Moderate · up to 78bdd

Resolve the remaining mesh migration and recovery issues before merging, alongside incomplete SDK component registration and the boot power-off fallback. Smaller setup and appearance inconsistencies also remain. The appearance precedence and failed-contact-load protections are now corrected.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 27.55% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title names several major changes in the pull request, including theming, GPS time, mesh, and setup. It is broad and includes the vague phrase "and more," but it still conveys meaningful change ar…
Full details: Title check

Explanation

The title names several major changes in the pull request, including theming, GPS time, mesh, and setup. It is broad and includes the vague phrase "and more," but it still conveys meaningful change areas.

Full details: Docstring Coverage

Explanation

Docstring coverage is 27.55% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 98 functions across 54 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Major comments: 7
Tactility/Source/app/boot/BootInit.cpp: 1

218-222: Major · Stability & Availability · Quick win

Stop the boot screen from waiting forever after a successful power-off request.

After powerOff() returns true, the loop at BootInit.cpp:220-222 never exits. The papers3 driver returns ERROR_NONE even when the device stays on. The device then stays on the boot screen with no retry or reboot. Replace the infinite wait with a bounded delay so the retry prompt returns.

Suggested fix
     while (waitForBootChoice(screen, {}, message) == BootChoice::PowerOff) {
         if (powerOff()) {
-            // Waits for the power to go away
-            while (true) {
-                delay_millis(1000);
-            }
+            // Give the power supply time to cut power.
+            delay_millis(5000);
         }
         LOG_E(TAG, "Power off failed");
         message = lines;
         message.push_back("Power off failed.");
     }
Modules/mesh-module/source/mesh_service.cpp: 6

436-436: Major · Data Integrity & Integration · Quick win

Keep added contacts when no backend storage is open.

With no backend open, mesh_add_contact returns success, but the contact is dropped at the next storage switch or service stop. Reject the add with ERROR_INVALID_STATE when storageBackend is empty. The base code saved these contacts to the shared contacts file.

Suggested fix
     lock();
     error_t result = ERROR_INVALID_STATE;
-    if (service != nullptr) {
+    if (service != nullptr && service->storageBackend[0] != '\0') {
         result = addContact(service, *address, name, true);
     }
     unlock();

1123-1125: Major · Data Integrity & Integration · Quick win

Check the target flush result before closing the migration store.

migrateSharedMessages() calls close() on each target store without checking whether the final flush succeeded. MeshStore::close() discards the flush result and frees the buffer, so the pending records are lost. The function still returns success, and migrateSharedStorage() then removes the shared store.

Suggested fix
     for (size_t t = 0; t < targetCount; t++) {
-        targets[t].store->close();
+        if (targets[t].store->flush() != ERROR_NONE) {
+            ok = false;
+        }
+        targets[t].store->close();
         tt::deleteObject(targets[t].store);
     }

1092-1092: Major · Data Integrity & Integration · Heavy lift

Do not delete the shared store when getAfter() skipped a message.

getAfter() drops entries whose readEntry() fails and returns only the successful reads. migrateSharedMessages() does not set ok to false for these skips, so migrateSharedStorage() removes the shared store. Compare the total read count with shared.getMessageCount(), and keep the shared store when they differ. A retry must also skip messages that already reached a backend store.


1148-1152: Major · Data Integrity & Integration · Quick win

Check fclose in migrateSharedContacts and do not remove the shared file after a failed flush.

migrateSharedContacts checks the fwrite result but ignores the fclose result. A flush error at fclose leaves ok true. migrateSharedStorage then deletes the shared contacts.bin, so that contact is lost. The fix is to treat a non-zero fclose return as a failure and keep the shared file for a retry.

Suggested fix
         char path[FILE_MAX_PATH_STRING_LENGTH];
         FILE* target = getBackendPath(contact.backend, CONTACTS_FILE_NAME, path, sizeof(path), true) ? fopen(path, "ab") : nullptr;
-        if (target == nullptr || fwrite(&contact, sizeof(contact), 1, target) != 1) {
+        if (target == nullptr) {
             ok = false;
-        }
-        if (target != nullptr) {
-            fclose(target);
+        } else {
+            if (fwrite(&contact, sizeof(contact), 1, target) != 1) {
+                ok = false;
+            }
+            if (fclose(target) != 0) {
+                ok = false;
+            }
         }

1097-1099: Major · Data Integrity & Integration · Heavy lift

Preserve legacy records that use dotted backend IDs.

migrateSharedMessages() skips records with invalid backend IDs, but migrateSharedStorage() still removes the shared store when migration reports success. Since mesh_backend_register() accepts dotted IDs, rejecting future registrations does not preserve records written by older versions.

Keep skipped records in the shared store or quarantine them. Do not only set ok = false: MeshStore::add() appends records, so retries could duplicate records already copied.


1137-1152: Major · Data Integrity & Integration · Quick win

Do not delete the shared contacts file after a failed or partial read.

migrateSharedContacts exits its loop whenever fread returns less than one complete MeshContact. It does not check ferror(file) or detect a truncated final record, so ok remains true. migrateSharedStorage then calls file_remove(contactsPath), which can permanently remove contacts that were not migrated.

Suggested fix
-    while (fread(&contact, sizeof(contact), 1, file) == 1) {
+    while (true) {
+        const size_t bytesRead = fread(&contact, 1, sizeof(contact), file);
+        if (bytesRead == 0) {
+            if (ferror(file)) {
+                ok = false;
+            }
+            break;
+        }
+        if (bytesRead != sizeof(contact)) {
+            ok = false;
+            break;
+        }
         contact.backend[MESH_BACKEND_ID_MAX_LENGTH] = '\0';
Minor comments: 7
Tactility/Source/app/setup/Setup.cpp: 3

583-583: Minor · Stability & Availability · Poor tradeoff

Move the GPS settings write off the LVGL thread.

The Continue callback runs the selected step's run() lambda synchronously on the LVGL thread. The new GPS step's lambda calls settings::setGpsTimeSyncAtBoot(), which opens a preferences file and writes a value. A slow storage device can block the UI until that write returns. Store the GPS choice in the setup context and perform the write from the setup task after it receives an event. Keep the step's advanceTo() call on the LVGL side.

As per coding guidelines: "Code that runs on the LVGL thread must not do file operations (including reading or writing settings): create_widgets, LVGL event callbacks, timers, and anything under the LVGL lock."


124-124: Minor · Performance & Scalability · Quick win

Use tt::std containers for the new choice labels.

The new StepConfiguration::choices is a std::vector<std::string>. Setup keeps these steps for the whole run, so the vector and its strings use the default allocator. The base code stored these labels in a flash-resident LOCATION_CHOICES array. Use tt::std::vector<tt::std::string> and include its header.

As per coding guidelines: "Containers that live long use the tt::std replacements from TactilityKernel: tt::std::vector, tt::std::deque, tt::std::map and tt::std::string... They prefer external RAM."

Suggested fix
-    std::vector<std::string> choices;
+    tt::std::vector<tt::std::string> choices;

Source: Coding guidelines


583-584: Minor · Data Integrity & Integration · Quick win

Report failed GPS preference writes in the setup step.

setGpsTimeSyncAtBoot() silently returns when preferences cannot be opened. The setup step then advances and can mark setup complete. Because preferences_put_bool() returns void, the current code cannot detect its write result. Expose a status from the complete persistence path and advance only after a successful write.

Modules/mesh-module/source/mesh_service.cpp: 2

1117-1119: Minor · Data Integrity & Integration · Quick win

Make shared-message migration retry-safe.

MeshStore::add always assigns a new sequence and appends a record. If a later backend fails, earlier copies remain while the shared store is retained. The next startup adds them again, and mesh_history_get returns both records. Roll back copied records or persist migration progress.


1144-1146: Minor · Data Integrity & Integration · Quick win

Skip migrated contacts that already exist in the destination.

migrateSharedContacts appends each contact to the backend file and does not roll back earlier writes when a later write fails. The shared contacts file remains in place after that failure, so the next startup repeats the migration. loadContacts and mesh_get_contacts do not deduplicate contacts by address.

A temporary file containing only migrated records is not sufficient. Renaming it over an existing destination file can discard contacts already stored there. Use address-based deduplication, or an atomic progress strategy that preserves existing destination records.

Tactility/Source/service/gpstime/GpsTimeSyncService.cpp: 1

41-43: Minor · Stability & Availability · Quick win

Keep GPS synchronization active after a failed clock update.

When setSystemTime(timeMs) fails, done remains true because done.exchange(true) runs first. The timer then unsubscribes from location updates, so later fixes cannot retry during the same boot. Keep in-flight exclusion separate from terminal completion, and set done after a successful update or an accepted no-op.

Tactility/Source/app/timedatesettings/TimeDateSettings.cpp: 1

84-85: Minor · Stability & Availability · Quick win

Make gpsSyncAtBoot atomic.

onGpsSyncChanged writes the plain Context::gpsSyncAtBoot on the LVGL task. appMain reads it on the app task after the event signal. A second toggle can write the field while the app task handles the earlier signal. The event group does not protect the context field.

Suggested fix
+#include <atomic>
 #include <string>
 #include <vector>
 
 ...
-    bool gpsSyncAtBoot = false;
+    std::atomic<bool> gpsSyncAtBoot = false;

Note

Follow-up findings are listed below so you can review them separately from inline comments.

Follow-ups (2)
Minor (2)
Minor · Seek to afterSeq in getAfter instead of scanning from the first entry. · mesh_store.cpp:455

Modules/mesh-module/source/mesh_store.cpp:455
Minor · Performance & Scalability · Quick win

Seek to afterSeq in getAfter instead of scanning from the first entry.

getAfter restarts at index.begin() on every call. The migration loop calls it once per batch of eight messages, so the in-RAM index walk grows roughly with N²/16 for N messages. Storage reads stay linear. The index is sorted by seq, so std::upper_bound can start at the first newer entry.

Suggested fix
 size_t MeshStore::getAfter(uint32_t afterSeq, MeshMessage* messages, size_t maxCount) const {
     size_t count = 0;
-    for (auto iterator = index.begin(); iterator != index.end() && count < maxCount; ++iterator) {
-        if (iterator->seq > afterSeq && readEntry(*iterator, &messages[count]) == ERROR_NONE) {
+    auto iterator = std::upper_bound(index.begin(), index.end(), afterSeq, [](uint32_t value, const IndexEntry& entry) {
+        return value < entry.seq;
+    });
+    for (; iterator != index.end() && count < maxCount; ++iterator) {
+        if (readEntry(*iterator, &messages[count]) == ERROR_NONE) {
             count++;
         }
     }
     return count;
 }
Minor · Keep buffered messages when the flush in MeshStore::close() fails. · mesh_service.cpp:461

Modules/mesh-module/source/mesh_service.cpp:461
Minor · Data Integrity & Integration · Poor tradeoff

Keep buffered messages when the flush in MeshStore::close() fails.

MeshStore::close() ignores the result of flush(). It then frees the buffer and clears the index, so unflushed messages are lost. switchStorage() ignores the result of close() too, so the backend switch continues. Make close() report the flush error. Abort the switch and keep the store open when the flush fails.

Nitpick comments: 4
Modules/lvgl-module/tests/source/theme_test.cpp: 1

5-9: Trivial · Maintainability & Code Quality · Poor tradeoff

Add one rendered-style density assertion.

theme_test.cpp tests only lvgl_theme_lerp. It does not verify that spacing or size changes a rendered theme style. Add a focused LVGL theme test that sets a concrete density value, creates a representative themed widget, and checks a style such as container padding or button padding.

Modules/mesh-module/tests/source/service_test.cpp: 1

315-317: Trivial · Maintainability & Code Quality · Quick win

Assert that no escaped backend folder is created.

The ../escape fixture is skipped by migrateSharedMessages, but the test does not check the corresponding resolved path. If validation regresses, migration can create <mesh data>/escape while the current assertions still pass.

Suggested test assertion
         // The shared files are gone, and each backend has its own
         CHECK_FALSE(directory_exists(storePath));
         CHECK_EQ(access(contactsPath, F_OK), -1);
+
+        char escapePath[FILE_MAX_PATH_STRING_LENGTH];
+        REQUIRE_EQ(service_paths_get_user_data_path(MESH_SERVICE_ID, "escape", escapePath, sizeof(escapePath)), ERROR_NONE);
+        CHECK_FALSE(directory_exists(escapePath));
TactilityKernel/tests/source/network_test.cpp: 1

33-40: Trivial · Functional Correctness · Quick win

Add a disconnected-first, connected-second regression case.

The test registers only one Wi-Fi device. Add a disconnected device before a connected device to verify that network_is_available() scans all Wi-Fi devices. This protects against a regression that could make location lookups return ERROR_RESOURCE when a later device is connected.

Tactility/Tests/Source/GpsTimeSyncTest.cpp: 1

24-28: Trivial · Maintainability & Code Quality · Heavy lift

Add integration coverage for GPS time service startup and delivery.

GpsTimeSyncTest.cpp tests shouldApplyGpsTime() and calls GpsTimeSyncService::onLocationTime(...) directly. It does not exercise onStart(), callback registration, or location_subscribe(). A broken startup path can therefore remain undetected while the tests pass. Add a test that starts the service and delivers a KERNEL_EVENT_LOCATION_TIME event.


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6511b780-04f6-4697-8f72-7258c0704e89
Commits

Reviewing files that changed from the base of the PR and between 752923b and d88aee1.

Files selected for processing: 59
  • Buildscripts/release-sdk-esp32.py
  • Devices/simulator/Source/module.cpp
  • Documentation/ideas.md
  • Modules/location-module/include/location/location.h
  • Modules/location-module/source/ip_provider.cpp
  • Modules/location-module/source/location_service.cpp
  • Modules/location-module/source/module.cpp
  • Modules/location-module/tests/source/service_test.cpp
  • Modules/lvgl-module/CMakeLists.txt
  • Modules/lvgl-module/include/lvgl/lvgl.h
  • Modules/lvgl-module/include/lvgl/theme.h
  • Modules/lvgl-module/source/lvgl.c
  • Modules/lvgl-module/source/symbols.c
  • Modules/lvgl-module/source/theme.cpp
  • Modules/lvgl-module/source/themes/lv_theme_material.c
  • Modules/lvgl-module/source/themes/lv_theme_material.h
  • Modules/lvgl-module/source/themes/lv_theme_material_mono.c
  • Modules/lvgl-module/source/themes/lv_theme_material_mono.h
  • Modules/lvgl-module/source/widgets/statusbar.cpp
  • Modules/lvgl-module/source/widgets/toolbar.cpp
  • Modules/lvgl-module/tests/source/theme_test.cpp
  • Modules/lvgl-module/themes/material.lss
  • Modules/lvgl-module/themes/material_mono.lss
  • Modules/mesh-module/private/mesh/mesh_store.h
  • Modules/mesh-module/source/mesh_service.cpp
  • Modules/mesh-module/source/mesh_store.cpp
  • Modules/mesh-module/tests/source/service_test.cpp
  • Modules/mesh-module/tests/source/store_test.cpp
  • Tactility/Include/Tactility/network/Ntp.h
  • Tactility/Include/Tactility/service/gpstime/GpsTimeSyncService.h
  • Tactility/Include/Tactility/settings/AppearanceSettings.h
  • Tactility/Include/Tactility/settings/Time.h
  • Tactility/Kconfig
  • Tactility/Private/Tactility/app/boot/BootScreen.h
  • Tactility/Private/Tactility/lvgl/SystemBars.h
  • Tactility/Source/InitServices.cpp
  • Tactility/Source/app/TileGrid.cpp
  • Tactility/Source/app/appearancesettings/AppearanceSettings.cpp
  • Tactility/Source/app/boot/BootInit.cpp
  • Tactility/Source/app/boot/BootScreen.cpp
  • Tactility/Source/app/locationsettings/LocationSettings.cpp
  • Tactility/Source/app/setup/Setup.cpp
  • Tactility/Source/app/systeminfo/SystemInfo.cpp
  • Tactility/Source/app/timedatesettings/TimeDateSettings.cpp
  • Tactility/Source/lvgl/SystemBars.cpp
  • Tactility/Source/lvgl/SystemMenu.cpp
  • Tactility/Source/lvgl/Theme.cpp
  • Tactility/Source/network/Ntp.cpp
  • Tactility/Source/service/gpstime/GpsTimeSyncService.cpp
  • Tactility/Source/settings/AppearanceSettings.cpp
  • Tactility/Source/settings/time.cpp
  • Tactility/Tests/Source/AppearanceSettingsTest.cpp
  • Tactility/Tests/Source/GpsTimeSyncTest.cpp
  • TactilityKernel/include/tactility/network.h
  • TactilityKernel/include/tactility/system_event.h
  • TactilityKernel/source/network.c
  • TactilityKernel/source/symbols.c
  • TactilityKernel/tests/source/network_test.cpp
  • device.py

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Comment thread Buildscripts/release-sdk-esp32.py Resolved
Comment thread Modules/lvgl-module/CMakeLists.txt Resolved
Comment thread Tactility/Source/app/setup/Setup.cpp Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6d7b3023-deda-464d-ab09-fad80cfbc5d6
Commits

Reviewing files that changed from the base of the PR and between d88aee1 and 085fa05.

Files selected for processing: 18
  • Modules/location-module/include/location/location.h
  • Modules/location-module/include/location/provider.h
  • Modules/location-module/source/gps_provider.cpp
  • Modules/location-module/source/ip_provider.cpp
  • Modules/location-module/source/location_service.cpp
  • Modules/location-module/tests/source/service_test.cpp
  • Modules/lvgl-module/include/lvgl/theme.h
  • Modules/lvgl-module/tests/source/theme_test.cpp
  • Modules/mesh-module/include/mesh/backend.h
  • Modules/mesh-module/source/mesh_service.cpp
  • Modules/mesh-module/tests/source/service_test.cpp
  • Tactility/Source/app/boot/BootInit.cpp
  • Tactility/Source/app/boot/BootScreen.cpp
  • Tactility/Source/app/setup/Setup.cpp
  • Tactility/Source/app/timedatesettings/TimeDateSettings.cpp
  • Tactility/Source/service/gpstime/GpsTimeSyncService.cpp
  • Tactility/Source/settings/AppearanceSettings.cpp
  • Tactility/Tests/Source/AppearanceSettingsTest.cpp
Files skipped from review as they are similar to previous changes: 7
  • Modules/lvgl-module/tests/source/theme_test.cpp
  • Tactility/Source/app/boot/BootScreen.cpp
  • Tactility/Source/service/gpstime/GpsTimeSyncService.cpp
  • Tactility/Source/app/boot/BootInit.cpp
  • Modules/lvgl-module/include/lvgl/theme.h
  • Tactility/Source/app/timedatesettings/TimeDateSettings.cpp
  • Tactility/Source/app/setup/Setup.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a4fe76ce-8007-4576-b2ee-4cc2f07b8da3
Commits

Reviewing files that changed from the base of the PR and between 085fa05 and 6e6f769.

Files selected for processing: 21
  • Devices/cl32-v02-v03/device.properties
  • Devices/cl32-v04/device.properties
  • Devices/heltec-wifi-lora-32-v3/device.properties
  • Devices/lilygo-tdeck-plus/device.properties
  • Devices/lilygo-tdisplay-s3/device.properties
  • Devices/lilygo-tdisplay/device.properties
  • Devices/lilygo-tdongle-s3/device.properties
  • Devices/lilygo-tembed-cc1101-plus/device.properties
  • Devices/lilygo-tembed-cc1101/device.properties
  • Devices/lilygo-tlora-pager/device.properties
  • Devices/m5stack-cardputer-adv/device.properties
  • Devices/m5stack-cardputer/device.properties
  • Devices/m5stack-stickc-plus2/device.properties
  • Devices/m5stack-sticks3/device.properties
  • Devices/waveshare-esp32-s3-geek/device.properties
  • Devices/waveshare-s3-lcd-13/device.properties
  • Devices/waveshare-s3-touch-amoled-206/device.properties
  • Devices/waveshare-s3-touch-lcd-128/device.properties
  • Devices/waveshare-s3-touch-lcd-147/device.properties
  • Modules/mesh-module/include/mesh/mesh.h
  • Modules/mesh-module/source/mesh_service.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Comments

Minor follow-up Failed contacts loads are not retried from tick().

464-465: Minor · Stability & Availability · Poor tradeoff

Failed contacts loads are not retried from tick().

When loadContacts() fails, contactsLoaded stays false. tick() never calls loadContacts() or switchStorage(), so the load runs again only when configure() runs. That happens on radio attach, region change, backend changes, or service restart. Until one of these events occurs, contact operations return ERROR_INVALID_STATE, even after the contacts file becomes readable.

Open the source location


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 278937ca-2d21-48b9-91d3-e67a66566ca5
Commits

Reviewing files that changed from the base of the PR and between 6e6f769 and 1c741e4.

Files selected for processing: 2
  • Modules/mesh-module/source/mesh_service.cpp
  • Modules/mesh-module/tests/source/service_test.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 1 remain after this review.

@KenVanHoeylandt KenVanHoeylandt changed the title Theming, mesh, setup and tweaks Theming, GPS time, mesh, setup, Launcher, and more Oct 31, 2026
@KenVanHoeylandt
KenVanHoeylandt merged commit 619d9bf into main Oct 31, 2026
78 checks passed
@KenVanHoeylandt
KenVanHoeylandt deleted the develop branch October 31, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant