a3e51f9b1e319bc9103cb3ec7ec8405e21f78be9
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6f7f4c592a |
refactor: Eliminated relative path includes (#1961)
## Summary Relative includes can hide inappropriate dependency relationships. In this case, I found that lib/KOReaderSync/KOReaderCredentialStore.cpp was dependent on src/JsonSettingsIO.h -- a lib -> app dependency, the opposite direction dependencies should flow in this project. This change replaces all relative includes with root-relative includes, and corrects the KOReaderCredentialStore dependency by moving its JSON settings serialization local to the KOReaderSync library. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**PARTIALLY**_ |
||
|
|
7993b2bb97 |
feat: add SD card font support with on-device download and web management
Add a complete SD card font subsystem that enables users to install and use custom fonts beyond the three built-in families. This combines the back-end firmware support (#1327) with the font configuration, build pipeline, CI distribution, and user-facing management UI (#1392). Core font system: - Custom .cpfont binary format (v4) with multi-style support (regular, bold, italic, bold-italic) packed into a single file per size - On-demand glyph loading from SD card with two-pass prewarm rendering to bulk-read glyphs per page, achieving near-flash performance for Latin text (~697ms vs ~681ms) and viable CJK rendering (~32% slower) - Persistent advance cache for layout measurement without SD I/O - Overflow ring buffer for glyph cache misses during rendering - Memory-conscious design: only advance tables kept in RAM; glyph bitmaps, kern tables, and ligatures loaded on demand from SD Font management: - On-device WiFi download from GitHub Releases with manifest-based discovery, install/update detection, and progress UI - Web interface font upload, listing, and deletion via /fonts page - Manual SD card copy to /fonts/ or /.fonts/ directories - Font selection integrated into Settings > Reader > Font Family Build pipeline: - Declarative YAML config (sd-fonts.yaml) as single source of truth for the 17-family font library (serif, sans, mono, accessibility) - Python converter (fontconvert_sdcard.py) for TTF/OTF to .cpfont with FreeType rasterization, class-based kerning, and ligature extraction - Parallel build orchestrator with variable font instance extraction - CI workflow publishing versioned + stable releases to a dedicated crosspoint-fonts repository with auto-incrementing revision tags - Centralized version constants (cpfont_version.py) shared across build tooling and CI, with firmware headers as manual sync points Additional fixes: - CJK characters no longer get hyphens inserted at line breaks - Advance table eliminates 30+ second stalls during CJK section indexing for paragraphs with >512 unique codepoints Closes #930 Co-authored-by: Zach Nelson <zach@zdnelson.com> Co-authored-by: Justin <itsthisjustin@users.noreply.github.com> Co-authored-by: jpirnay <jens@pirnay.com> Co-authored-by: mcrosson <kemonine@kemonine.info> |
||
|
|
b25389b43e |
refactor: Move language setting into JSON settings (#1796)
## Summary
Another follow up to #1408. That PR revised the language.bin settings
file to version 2. After merging, it occurred to me that it would make
more sense to have the language inside the overall settings.json file,
easy for users to see and change directly.
This change adds migration from language.bin to settings.json. It only
migrates from the pre-#1408 v1 language.bin format, because v2 has only
been in for a couple of commits and I don't think we need long-term
maintenance code to handle it.
#1408 had a potential long-term maintenance issue for migration. It
assumed that the enum order of languages never changes, only grows. So,
e.g. "Portuguese (Portugal)" could never be added next to "Portuguese
(Brazil)", only appended. This change includes a hardcoded frozen
ordering of the language indices as they existed at
|
||
|
|
1cf2239742 |
feat: Support for multiple OPDS servers (#1209)
## Summary * Add support for configuring and using multiple OPDS servers, replacing the previous single-server limitation. Closes https://github.com/crosspoint-reader/crosspoint-reader/issues/1178 * New OpdsServerStore singleton (modeled after WifiCredentialStore) that persists up to 8 OPDS servers to /.crosspoint/opds.json with MAC-based password obfuscation. * One-time migration from legacy single-server fields in CrossPointSettings to the new store on first boot. * New OpdsServerListActivity for the device UI — works in two modes: a settings list (add/edit/delete servers) and a picker (select which server to browse). When only one server is configured, the picker is skipped automatically. * Renamed CalibreSettingsActivity → OpdsSettingsActivity for clarity. It now edits individual OpdsServer entries (name, URL, username, password, delete). * OpdsBookBrowserActivity now receives an OpdsServer at construction and uses its credentials for all fetches/downloads, and shows the server name in the header. * HttpDownloader::fetchUrl and downloadToFile accept optional per-call username/password parameters instead of reading from global settings. * REST API endpoints on CrossPointWebServer: GET /api/opds, POST /api/opds, POST /api/opds/delete — passwords are never exposed over the API (only a hasPassword flag), and omitting the password field on update preserves the existing one. * Web UI (SettingsPage.html) with dynamic OPDS server management cards — add, edit, save, and delete servers from the browser. <img width="932" height="906" alt="SCR-20260416-stvu" src="https://github.com/user-attachments/assets/a8f18d84-4204-46a0-bb31-b73d24b3255f" /> ## Additional Context * The OpdsServerStore JSON format and obfuscation scheme are identical to WifiCredentialStore, so the same JsonSettingsIO infrastructure handles both. * The web API uses POST /api/opds/delete instead of DELETE /api/opds because the ESP32 WebServer doesn't support the DELETE method with a request body. * Existing single-server configurations are migrated automatically — no user action required. After migration the legacy CrossPointSettings fields are cleared so it only runs once. * The HttpDownloader changes are backward-compatible: the credential parameters default to empty strings, so existing callers are unaffected. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< YES >**_ --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> |
||
|
|
c4f5c8e931 |
fix: prevent wallpaper clustering with 16-entry recency buffer (#1606)
## Problem Custom sleep wallpapers feel repetitive — the same image appearing multiple times in a short session. Only the single most-recently-shown index was stored (`uint8_t lastSleepImage`), so on collections of 3–5 images, two-pick cycles were common. On larger collections, any image could reappear within the next few picks. ## Solution Add a 16-entry circular recency buffer to `CrossPointState` that excludes recently shown wallpapers from selection. **Recency buffer** (`recentSleepImages[16]`, `recentSleepPos`, `recentSleepFill` — 34 bytes DRAM): - Tracks the last 16 shown wallpaper indices - On each pick, rerolls up to 20 times if the candidate was recently shown - Window auto-shrinks to `numFiles - 1` for small collections (guarantees a non-repeat is always possible) - `isRecentSleep()` clamps to `recentSleepFill` to avoid false positives on unwritten buffer slots - State persisted to `state.json` so the buffer survives sleep/wake cycles **Migration**: - Binary (`state.bin`): old `lastSleepImage` field seeded into the new buffer if valid - JSON (`state.json`): legacy `lastSleepImage` key detected and seeded into the buffer when upgrading from older firmware ## Index type fix `randomFileIndex` upgraded from `uint8_t` to `uint16_t` — silently truncated for collections larger than 255 wallpapers. ## Repeat probability: before vs after Chance of seeing a recently-shown image on the next pick. After: `(numFiles - 16) / numFiles` once collection exceeds the window; 0% while ≤17. | Collection | Before | After | |---|---|---| | 3 | 50% | 0% | | 5 | 75% | 0% | | 10 | 89% | 0% | | 17 | 94% | 0% | | 18 | 94% | 6% | | 20 | 95% | 20% | | 25 | 96% | 36% | | 30 | 97% | 47% | | 50 | 98% | 68% | | 100 | 99% | 84% | ## Memory impact | Addition | Size | |---|---| | `recentSleepImages[16]` | 32 bytes DRAM | | `recentSleepPos` + `recentSleepFill` | 2 bytes DRAM | | **Total** | **34 bytes DRAM** | ## Files changed - `src/CrossPointState.h` — recency buffer fields + `isRecentSleep()` / `pushRecentSleep()` declarations - `src/CrossPointState.cpp` — `isRecentSleep()` / `pushRecentSleep()` implementations + binary migration path - `src/JsonSettingsIO.cpp` — JSON serialisation of buffer state + JSON migration - `src/activities/boot_sleep/SleepActivity.cpp` — retry loop with recency check Co-authored-by: Patryk Radtke <patryk@Patryks-MacBook-Pro.local> |
||
|
|
79b54b3a75 |
fix: Init lastSleepImage (edge case) (#1360)
## Summary * **What is the goal of this PR?** fix edge case for definition ## Additional Context If loadFromFile() returns false (no state file exists — first boot, or SD missing), lastSleepImage is never set and contains garbage. [SleepActivity.cpp:83] then uses it in a while comparison to avoid repeating the same image. The JSON path (doc["lastSleepImage"] | (uint8_t)0) handles it, but only if the file exists. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _** NO **_ |
||
|
|
04242fa221 |
refactor: Simplify new setting introduction (#1086)
## Summary
* **What is the goal of this PR?** Eliminate the 3-file / 4-location
overhead for adding a new setting. Previously, every new setting
required manually editing JsonSettingsIO.cpp in two places (save +
load), duplicating knowledge already present in SettingsList.h. After
this PR, JsonSettingsIO.cpp never needs to be touched again for standard
settings.
* **What changes are included?**
* `SettingInfo` (in `SettingsActivity.h`) gains one new field: `bool
obfuscated` (base64 save/load for passwords), with a fluent builder
method `.withObfuscated()`. The previously proposed
`defaultValue`/`withDefault()` approach was dropped in favour of reading
the struct field's own initializer value as the fallback (see below).
* `SettingsList.h` entries are annotated with `.withObfuscated()` on the
OPDS password entry. The list is now returned as a `static const`
singleton (`const std::vector<SettingInfo>&`), so it is constructed
exactly once. A missing `key`/`category` on the
`statusBarProgressBarThickness` entry was also fixed — it was previously
skipped by the generic save loop, so changes were silently lost on
restart.
* `JsonSettingsIO::saveSettings` and `loadSettings` replace their ~90
lines of manual per-field code with a single generic loop over
`getSettingsList()`. The loop uses `info.key`,
`info.valuePtr`/`info.stringOffset`+`info.stringMaxLen` (for char-array
string fields), `info.enumValues.size()` (for enum clamping), and
`info.obfuscated`.
* **Default values**: instead of a duplicated `defaultValue` field in
`SettingInfo`, `loadSettings` reads `s.*(info.valuePtr)` *before*
overwriting it. Because `CrossPointSettings` is default-constructed
before `loadSettings` is called, this captures each field's struct
initializer value as the JSON-absent fallback. The single source of
truth for defaults is `CrossPointSettings.h`.
* One post-loop special case remains explicitly: the four `frontButton*`
remap fields (managed by the RemapFrontButtons sub-activity, not in
SettingsList) and `validateFrontButtonMapping()`.
* One pre-loop migration guard handles legacy settings files that
predate the status bar refactor: if `statusBarChapterPageCount` is
absent from the JSON, `applyLegacyStatusBarSettings()` is called first
so the generic loop picks up the migrated values as defaults and applies
its normal clamping.
* OPDS password backward-compat migration (plain `opdsPassword` →
obfuscated `opdsPassword_obf`) is preserved inside the generic
obfuscated-string path.
## Additional Context
Say we want to add a new `bookmarkStyle` enum setting with options
`DOT`, `LINE`, `NONE` and a default of `DOT`:
1. `src/CrossPointSettings.h` — add enum and member:
```cpp
enum BOOKMARK_STYLE { BOOKMARK_DOT = 0, BOOKMARK_LINE = 1, BOOKMARK_NONE = 2 };
uint8_t bookmarkStyle = BOOKMARK_DOT;
```
2. `lib/I18n/translations/english.yaml` — add display strings:
```yaml
STR_BOOKMARK_STYLE: "Bookmark Style"
STR_BOOKMARK_DOT: "Dot"
STR_BOOKMARK_LINE: "Line"
```
(Other language files will fall back to English if not translated. Run
`gen_i18n.py` to regenerate `I18nKeys.h`.)
3. `src/SettingsList.h` — add one entry in the appropriate category:
```cpp
SettingInfo::Enum(StrId::STR_BOOKMARK_STYLE, &CrossPointSettings::bookmarkStyle,
{StrId::STR_BOOKMARK_DOT, StrId::STR_BOOKMARK_LINE, StrId::STR_NONE_OPT},
"bookmarkStyle", StrId::STR_CAT_READER),
```
That's it — no default annotation needed anywhere, because
`bookmarkStyle = BOOKMARK_DOT` in the struct already provides the
fallback. The setting will automatically persist to JSON on save, load
with clamping on boot, appear in the device settings UI under the Reader
category, and be exposed via the web API — all with no further changes.
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _**< PARTIALLY>**_
|
||
|
|
2d49c7b7b4 |
feat: split status bar setting (#733)
## Summary This PR aims to reduce the complexity of the status bar by splitting the setting into 5: - Chapter Page Count - Book Progress % - Progress Bar - Chapter Title - Battery Indicator These are located within the new StausBarSettings activity, which also shows a preview of the bar the user has created <img width="513" height="806" alt="image" src="https://github.com/user-attachments/assets/cdf852fb-15d8-4da2-a74f-fd69294d7b05" /> <img width="483" height="797" alt="image" src="https://github.com/user-attachments/assets/66fc0c0d-ee51-4d31-b70d-e2bc043205d1" /> When updating from a previous version, the user's past settings are honoured. ## Additional Context The PR aims to remove any duplication of status bar code where possible, and extracts the status bar rendering into a new component - StatusBar It also adds a new (optional) padding option to the progress bar to allow the status bar to be shifted upwards - this is only intended for use in the settings. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? No - although did help to decode some C++ errors --------- Co-authored-by: Arthur Tazhitdinov <lisnake@gmail.com> |
||
|
|
6e4d0e534d |
feat: Migrate binary settings to json (#920)
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <dave@daveallie.com>
|