05c1e9aa4630714635c57c0315b1cbc4dffbec98
519
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ba4a361d64 |
fix: OTA update on x3 and progress bar on x4 and x3 (#1805)
Co-authored-by: Justin Mitchell <1875695+itsthisjustin@users.noreply.github.com> |
||
|
|
15ea7027df |
style: move file type badges into Type column (#1793)
## Summary The file type was shown twice in the file browser table: as a bold badge in the **Name** column and as plain text in the **Type** column. **Before:** badge in Name column + redundant text in Type column **After:** badge lives exclusively in the Type column | | Before | After | |---|---|---| | Folder | Name: `📁 folder` **FOLDER** · Type: `Folder` | Name: `📁 folder` · Type: **FOLDER** | | EPUB | Name: `📗 book.epub` **EPUB** · Type: `EPUB` | Name: `📗 book.epub` · Type: **EPUB** | | Other | Name: `📄 file.txt` · Type: `TXT` | unchanged | ### Before <img width="1125" height="571" alt="Screenshot From 2026-04-30 22-07-59" src="https://github.com/user-attachments/assets/794c66d3-5ad5-4536-a142-708989b9dee8" /> ### After <img width="1125" height="571" alt="Screenshot From 2026-04-30 22-08-45" src="https://github.com/user-attachments/assets/f9fb2898-b165-478a-813b-f2290ad3e42c" /> ## Changes - Removed `epub-badge` and `folder-badge` from the Name column - Moved badges into the Type column, replacing the redundant plain text - Non-EPUB files are unaffected (extension text remains in Type column) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
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
|
||
|
|
2f969a93b4 |
feat: context-aware screenshot filenames with book title (#1589)
## Summary I love the new screenshot feature but I can tell that soon I'm gonna have a folder full of screenshots and not know what's what. It would be great if the folder could self organize. When a screenshot is taken while reading, the filename would now include the book title, chapter (EPUB), page number, and progress percentage. Example: My-Great-Book_ch3_p5_42pct_12345.bmp * **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.) * **What changes are included?** ## Additional Context * Non-reader screens keep the existing screenshot-<millis>.bmp naming. --- ### 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 >**_ I reviewed all the changes but I don't have any experience with this project so this is my best guess |
||
|
|
5d2e5596b4 |
feat: X3 gyroscope-based tilt page turning via QMI8658 IMU (#1636)
Co-authored-by: justinian <juicecutlus@gmail.com> Co-authored-by: Vincent Politzer <vincent@skipwithjoy.com> |
||
|
|
bc9651b664 |
fix: use same file name as KOReader for OPDS downloads (#1286)
This allows KOSync to work for files downloaded from OPDS when set to use filename matching ## Summary * **What is the goal of this PR?** Brings the names of files downloaded via OPDS into line with the KOReader OPDS plugin so the files sync when KOSync is set to filename matching * **What changes are included?** Change the filename generated by the OPDS browser from "\<title\> - \<author\>" to "\<author\> - \<title\>" so it matches KOReader. ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). Both KOReader and crosspoint sanitise the filename by removing special characters. This step seems to already use the same rules. --- ### 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 |
||
|
|
b8a51522ca |
feat(theme): add roundedraff theme and fix sleep cover crop grid artifacts (#918)
## Summary - Add new `roundedraff` theme. - Fix sleep screen artifact where book cover showed grid lines in `Cover` mode with `Crop`. ## Additional Context ## Key changes - Added `src/components/themes/roundedraff/` theme implementation. - Updated sleep cover rendering logic in: - `src/activities/boot_sleep/SleepActivity.cpp` - `src/activities/boot_sleep/SleepActivity.h` - Improved cover generation/regeneration paths in: - `lib/Epub/Epub.cpp` - `lib/Epub/Epub.h` - `lib/Txt/Txt.cpp` - `lib/Txt/Txt.h` ## Verification - Sleep screen set to Book cover - Sleep screen cover mode set to Crop - Confirmed grid artifacts no longer appear on cover sleep screen. ### 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: CaptainFrito <yzq6x5zypy@privaterelay.appleid.com> Co-authored-by: Zach Nelson <zach@zdnelson.com> Co-authored-by: Uri Tauber <142022451+Uri-Tauber@users.noreply.github.com> |
||
|
|
907e14da28 |
fix: pressing space barely moves input cursor (#1729) (#1733)
## Summary
* **What is the goal of this PR?**
Fix the visual keyboard cursor not advancing when typing a space. Typing
one space leaves the cursor at position 0; typing two spaces advances it
only by one space width. This affects all input types except URL.
* **What changes are included?**
Replace `getTextWidth()` with `getTextAdvanceX()` in four locations
within `KeyboardEntryActivity::render()` for cursor positioning and
line-wrapping calculations.
## Additional Context
* **Root cause**: `getTextWidth()` returns the bounding-box width of the
drawn glyphs. The space glyph has `width=0` and `height=0` (it's
invisible), so `getTextWidth(" ") == 0`. The trailing `advanceX` of the
last character is only flushed when the *next* character is processed,
so a string ending in space reports zero width. `getTextAdvanceX()`
correctly includes the final glyph's advance, matching how `drawText()`
actually positions characters.
---
### 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**_
|
||
|
|
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> |
||
|
|
c5f82709c0 |
fix: Replaced Bookerly with Noto Serif for licensing reasons (#1736)
## Summary Fixes #258. Bookerly is not licensed for use in CrossPoint. Switched to Google's open Noto Serif font. --- ### 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**_ |
||
|
|
867fb7cbe2 |
style: unify page headers across web UI (#1702)
## Summary * **Goal**: Align `FilesPage` and `SettingsPage` headers with the existing `HomePage` pattern. * **Changes**: - Added `<h1>📚 CrossPoint Reader</h1>` at the top of `<body>` on both pages. - Demoted the page-specific `📁 File Manager` heading on `FilesPage` from `<h1>` to `<h2>` (inside `.page-header`), so every page has a single `<h1>` — the app name. - Moved the accent-colored `border-bottom` from `.page-header` to `h1`, matching the HomePage CSS. All three pages now share the same top structure: `<h1>CrossPoint Reader</h1>` → nav links → page content. ## Screenshots ### Home **Before and after is the same** <img width="1153" height="388" alt="home before" src="https://github.com/user-attachments/assets/4eb1c969-89fd-4965-ba17-5ff956576a8b" /> ### File Manager **Before** <img width="1153" height="388" alt="files before" src="https://github.com/user-attachments/assets/ba689d6b-56b1-44d1-a151-3f67b0fcdb16" /> **After** <img width="1153" height="388" alt="files after" src="https://github.com/user-attachments/assets/437db5e4-9102-4a19-b049-7a93e4e403b2" /> ### Settings **Before** <img width="1153" height="388" alt="settings before" src="https://github.com/user-attachments/assets/39a7e489-f0d3-4ac1-a2d7-98028aae65c0" /> **After** <img width="1153" height="388" alt="settings_after" src="https://github.com/user-attachments/assets/1c479618-ec8b-4dee-b704-3da07e6c279e" /> ## Additional Context * Pure markup + CSS, no JavaScript or behavioral changes. * Improves HTML semantics (single `<h1>` per page, referring to the app). * No new CSS duplication introduced — borrows existing styling from HomePage. --- ### 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: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
56d3ab929c |
feat: show long branch names (#1727)
## Summary * This change will make better use of the space available for Title and Subtitle in the Settings (Theme: Lyra). Instead of having a fixed maxwidth in pixels for subtitle, it will use the whole line and shorten the longest one if needed. So instead of: <img width="480" height="140" alt="image" src="https://github.com/user-attachments/assets/67e4777b-10d0-4a05-b5c7-fb775776c186" /> It will now show: <img width="480" height="142" alt="image" src="https://github.com/user-attachments/assets/2f143f4e-75d7-45c4-b61a-aad56bdf39fb" /> --- ### 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**_ |
||
|
|
9dab5f471b |
style: put page name first in browser titles (#1703)
## Summary * **Goal**: Make browser tab titles, bookmarks, and history entries distinguishable across pages (including individual folders in the File Manager). * **Changes**: - Reordered `<title>` so the page name comes first, followed by `CrossPoint Reader`. Browser tabs usually truncate titles from the right, and bookmark/history lists are sorted alphabetically by title — putting the differentiating part first keeps it visible in both cases, instead of every entry starting with `CrossPoint Reader - …`. - On `FilesPage`, added a small JS hook that updates `document.title` with the current subfolder leaf when a `path` query parameter is present — previously all folders shared the same title. | URL | Title before | Title after | |-----|--------------|-------------| | `/` | `CrossPoint Reader` | `CrossPoint Reader` _(unchanged)_ | | `/settings` | `CrossPoint Reader - Settings` | `Settings - CrossPoint Reader` | | `/files` | `CrossPoint Reader - Files` | `Files - CrossPoint Reader` | | `/files?path=/Books` | `CrossPoint Reader - Files` | `Books - Files - CrossPoint Reader` | | `/files?path=/Books/Fantasy` | `CrossPoint Reader - Files` | `Fantasy - Files - CrossPoint Reader` | ## Additional Context * Pure client-side change: `<title>` tags plus ~3 lines of JavaScript on FilesPage. No network, behavior, or memory impact. * The static `<title>` tag remains as a fallback if JS fails. * `HomePage` title left as-is since the app name alone is the standard convention for a root/home page. --- ### 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**_ |
||
|
|
e28918b24d |
fix: pluralize folder/file counts correctly in file list summary (#1701)
## Summary
* **Goal**: Fix incorrect pluralization in the file manager web UI
summary line.
* **Changes**: `FilesPage.html` always rendered the plural forms
("folders"/"files") regardless of count. The summary now selects
singular or plural based on each count.
Example:
- Before: `1 folders, 1 files, 12 KB`
- After: `1 folder, 1 file, 12 KB`
## Additional Context
* Cosmetic-only fix — no behavior, performance, or memory impact.
* Change is fully client-side (JavaScript inside a single HTML
template).
* English-only; web UI localization is out of scope here.
---
### 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: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||
|
|
c0ee096841 |
fix: relative opds paths and query param with copyparty (#1535)
## Summary * **What is the goal of this PR?** This PR fixes bugs when using Copyparty as an OPDS server. https://github.com/9001/copyparty?tab=readme-ov-file#opds-feeds OPDS uses a query parameter `?opds` to differentiate between HTML requests and OPDS requests for the same path. It also uses relative paths in the responses instead of full paths. Crosspoint didn't handle these two cases. * **What changes are included?** Fixes to the two issues above. ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). Here is some example XML from my Copyparty instance: https://gist.github.com/philips/9ecec29dfb69ed0591b032f16e799675 ### 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 | PARTIALLY | NO >**_ Partially. I used Claude code to write the fix. I am not a strong C++ programmer. But, I manually compiled and tested. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
1a145fe085 |
fix: keyboard feedback #1644 (#1697)
Addresses reviewer feedback from #1644: - **Localize keyboard hint strings** — 13 hardcoded English strings replaced with \`tr()\` macro (\`STR_KB_HINT_*\`), making them translatable across all 22 languages (fallback to English when not yet translated) - **Deduplicate \`Lyra3CoversMetrics\`** — now derives from \`LyraMetrics\` via lambda copy, overriding only \`homeCoverTileHeight\` and \`homeRecentBooksCount\` (eliminates ~30 duplicated metric fields) - **Unify keyboard drawing in \`BaseTheme\`** — \`drawTextField\` and \`drawKeyboardKey\` overrides removed from \`LyraTheme\`; variability controlled via \`keyboardKeyCornerRadius\` metric (0=Base, 6=Lyra). Unified text field padding to 6, adopted Lyra's secondary label draw order (main first, then secondary) - **Add URL-optimized keyboard layout** — \`urlLayout\` with \`:\` and \`/\` replacing \`=\` and \`,\` for easier URL input without switching to SYM mode --- ### 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 **_ |
||
|
|
302dea1eea |
fix: Switch to xpath map for paragraph level syncing in KOSync (#1686)
Switch KOReader sync progress mapping from chapter matching to XPath-based mapping. - resolves KOReader positions using real XHTML ancestry paths - supports paragraph-based upload mapping with text offsets where needed - passes the current paragraph index into sync so uploads map back to KOReader more accurately No HTTP client changes are included. No reader-state or resume-flow changes are included. --------- Co-authored-by: jpirnay <jens@pirnay.com> |
||
|
|
e8645ed92e |
docs: fix typos (#1705)
## Summary Fix typos found via `codespell -S *.txt,*.yaml,generate_kerning_ligature_epub.py -L currenty,flate,ser,localy,logicaly,ans,clen,portugues,notin,curren` ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). --- ### 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**_ --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
64f5ef018a |
feat: Support for proportional numeral spacing (#1414)
## Summary **What is the goal of this PR?** Reading a book with frequent numbers, I noticed that the spacing between numeral glyphs was strangely large. This was because Bookerly and Noto Sans default to tabular figures, where every digit gets an identical advance width. This is designed for column alignment in spreadsheets, but in rendering prose it produces visually wide gaps between digits. This change adds a `--pnum` flag to fontconvert.py that applies the font's OpenType `pnum` (proportional numerals) feature during conversion. When active, the converter: - Parses the GSUB table for pnum SingleSubst lookups - Resolves substitute glyph indices via fonttools' glyph order - Loads the proportional alternate glyphs instead of the tabular defaults - Includes substitute glyph names in kern pair extraction, so kerning data that references proportional alternates is captured Bookerly's proportional alternates also carry digit-digit and digit-punctuation kerning that the tabular glyphs lack (e.g., at 16pt 7->4 at -1.69px, 7->. at -2.31px, 7->1 at +1.00px). Noto Sans gains proportional advances but no new kerning (its proportional glyphs have no kern class data in the font). OpenDyslexic is unaffected. Its `cmap` already points to proportional glyphs, so `--pnum` is a no-op. `--pnum` is intentionally omitted from OpenDyslexic in the build script for deliberately uniform digit spacing as an accessibility choice. UI fonts (Ubuntu, notosans_8) also omit `--pnum` to preserve tabular alignment for page numbers, battery percentages, etc. | Before | After | | -- | -- | | <img src="https://github.com/user-attachments/files/26042238/screenshot-31673.bmp" width="300" /> | <img src="https://github.com/user-attachments/files/26042241/screenshot-124075.bmp" width="300" /> | --- ### 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**_ |
||
|
|
77b2c31635 |
refactor: redesign on-screen keyboard (#1644)
# Refactor: Redesign On-Screen Keyboard ## Summary Complete redesign of the on-screen keyboard (used for WiFi password, KOReader, Calibre URLs) with improved layout, navigation, visual style, and new input features: **cursor mode** for text navigation, **password mode** with visibility toggle, and **URL mode** with pre-defined snippets. ## Screenshots ### Base Theme |**master** | **PR #1644** | |----------|-------------| | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/49125857-12d0-4020-b872-05d0ddbf1d94" /> | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/ad16656b-d66e-43dd-8697-2b85f709d7f8" /> | ### Lyra Theme | **master** | **PR #1644** | |----------|-------------| | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/d9901251-9154-48d2-83b4-376d3223d132" /> | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/84b45949-ed61-4924-af1b-d570c4c61e13" /> | ### Keyboard States | ABC Mode | Symbol Mode | URL Mode | |----------|-------------|----------| | <img width="480" height="280" alt="image" src="https://github.com/user-attachments/assets/6397a82e-50b8-4d03-92e0-f707ed7c9054" /> | <img width="480" height="280" alt="image" src="https://github.com/user-attachments/assets/205d34fe-0413-49e9-9db2-30569c297ba0" /> | <img width="480" height="280" alt="image" src="https://github.com/user-attachments/assets/801ddeab-e082-4a22-a9df-c66adb1afc16" /> | | Cursor Mode | Password Toggle | |-------------|-----------------| | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/841773e1-7a3c-45e5-aa89-e5787255608c" /> | <img width="480" height="800" alt="image" src="https://github.com/user-attachments/assets/9487a0b3-3f47-41ed-9cd3-b24e56e342a8" /> | ## Changes ### Layout (10-column uniform grid) - Reduced from 13/11/10 columns per row to **10 uniform columns** across all rows - Keyboard now uses **90% of screen width** (was ~66%) - Row 0: Numbers `1-9, 0` with secondary symbols (`!@#$%^&*()`) - Rows 1-3: Standard QWERTY letters - Bottom row: `shift` | `#@!` | `___` | `←` | `OK` ### New Symbol Mode (#@!) - New mode toggle key `#@!` / `abc` switches between letter and symbol layouts - Symbol layout: 4 rows (numbers, inverted symbols, paired symbols, loose symbols) - Covers all 95 printable ASCII characters - **No secondary hints, no long-press** in symbol mode (simple and direct) - SHIFT key remains visible but **disabled** in symbol mode ### URL Mode - In `InputType::Url`, the Space key becomes a **URL toggle** button - Activating URL mode replaces the 4 content rows with a **3×3 grid of URL snippets**: - Col 0 (protocols): `https://`, `http://`, `/opds` - Col 1 (hosts/ports): `www.`, `192.168.`, `:8080` - Col 2 (domains): `.com`, `.org`, `.net` - Snippets are inserted as full strings at the cursor position - URL mode **persists** after inserting a snippet (does not auto-deactivate) - Column alignment: col 0 over ABC, col 1 over URL, col 2 over Del - Up/Down navigation maps `bottomCol - 1` / `urlCol + 1` - SHIFT disabled in URL mode - SpecMode (`abc`) exits URL mode back to ABC - SpecSpace (`URL`) toggles URL mode on/off; selection always stays on the URL button - Button styled with `KeyboardKeyType::Mode` for consistent outline ### Cursor Mode - **Enter**: Long-press Up (500ms) while in keyboard mode - **Exit**: Short-press Down while in cursor mode (resets `passwordVisible`, clears toggle position) - **Navigate**: Left/Right move cursor position within text (one position per press, no continuous repeat) - **Visual**: - Keyboard mode: underline cursor (2px line + serifs) - Cursor mode: inverted block cursor (black fill + white character) - Block width adapts to the actual character width under cursor (minimum 6px for narrow chars like space) - Block position includes inter-character kerning offset for correct alignment (calculated via string-difference: `getTextWidth(before+char) - getTextWidth(before) - getTextWidth(char)`) - End-of-text: thin 6px block - Password hidden: 3-part drawing (Part 1 + block + Part 3) to prevent block overflow onto `*` characters - Toggle position: caret ("I") cursor at saved position, `[abc]`/`[***]` label with inverted selection - **Inactive key styling**: - BaseTheme: 2px outline rectangle - LyraTheme: gray filled rounded rectangle (`Color::LightGray`) - **Password toggle position**: in cursor mode (Password only), Hold Right (500ms) enters toggle — caret cursor shows saved position, `[abc]`/`[***]` label becomes selected. Press Confirm to toggle `passwordVisible`. Press Left to restore cursor to saved position. Right from toggle is a no-op. Down from toggle exits to keyboard. - `cursorPos` persists between keyboard and cursor modes ### Password Mode - `InputType::Password` enum replaces `bool isPassword` parameter - Text is masked with `*` except for one revealed character: - Keyboard mode: reveals character at `cursorPos - 1` - Cursor mode: no reveal in display text (block cursor draws actual char directly) - **Toggle `[abc]`/`[***]`**: accessible via cursor mode — Hold Right (500ms) enters toggle position, Confirm toggles visibility, Left exits back to cursor. Caret ("I") shown at saved position while in toggle. - `passwordVisible` resets to `false` when exiting cursor mode - **Long-press Del (1.5s)**: clears all text and resets cursor to 0 ### InputType Enum - Replaced `bool isPassword` constructor parameter with `enum class InputType { Text, Password, Url }` - Callers updated: `WifiSelectionActivity`, `KOReaderSettingsActivity`, `CalibreSettingsActivity` ### Contextual Tips - `"Tips:"` header followed by context-sensitive hints, centered between text field underline and keyboard as a block - ABC mode: `"Hold SELECT for UPPERCASE or secondary char"` (shift ON: `"lowercase"` variant) + `"Hold DEL to clear all text"` (only if text not empty) - ABC + `InputType::Url`: same + `"Press URL for snippets"` - Symbol mode: `"Hold DEL to clear all text"` (only if text not empty) - URL mode: `"Press ABC to exit URL mode"` + `"Hold DEL to clear all text"` (only if text not empty) - Cursor mode: `"Press DOWN to return to keyboard"` ### Hint Phases (cursor mode, Password only) - **Phase 1**: `"Hold UP to edit entry"` — shown after 2× DEL press, auto-hides after 4s, positioned below underline - **Phase 2**: `"Press < or > to move cursor"` + dynamic password toggle hint — shown when entering cursor mode, positioned below underline, visible until exit - When `!passwordVisible`: `"Hold > then press [abc] to show password"` - When `passwordVisible`: `"Hold > then press [***] to hide password"` - When in toggle position: `"Press < to return to cursor position"` ### Long-Press Alternative Character - Holding Confirm (>500ms) inserts the **alternative character** instead of the primary - Letters: long-press inserts opposite case (e.g., `a`→`A`, `A`→`a`) - Numbers/symbols (row 0): long-press inserts secondary (e.g., `0`→`)`, `)`→`0`) - Only active in ABC mode; disabled in Symbol mode and URL mode - **`InputType::Url`**: Hold SELECT on ABC rows 1+ (letters) returns primary character only (same as short press). Row 0 (symbols) still returns secondary character on Hold SELECT. ### Shift (2 sticky states) - Reduced from 3 states (shift/SHIFT/LOCK) to **2 sticky states** (shift/SHIFT) - Shift stays active after typing until manually toggled off - Label: `shift` (off) / `SHIFT` (on) ### SpecialKeyType Enum - `enum class SpecialKeyType { Shift, Mode, Space, Del, Ok }` replaces plain `enum` (`SpecShift`, `SpecMode`, etc.) for type safety - All switch cases updated to `SpecialKeyType::*` with `static_cast<int>()` for array indexing - `onExit()` reverted to simple `Activity::onExit()` call (half-refresh removed) - **Bottom row column mapping**: navigating up/down between content rows and bottom row uses `col/2` and `col*2` formulas for consistent positioning (10 cols ↔ 5 cols) - **URL mode column mapping**: `bottomCol - 1` / `urlCol + 1` (3 cols ↔ 5 cols) - **Wrap-around**: row 0 → up → bottom row and bottom row → down → row 0 both apply correct column mapping ### Visual Improvements (both Base and Lyra themes) - **Space key**: underscore-style horizontal line (60% of key width, 3px thick) - **Delete key**: arrow `←` drawn with lines (3px thick) instead of "DEL" text - **Secondary label** (ABC row 0): small hint in top-right corner with separation from primary number - **BaseTheme**: selection uses **inverted fill** (black rect + white text) instead of `[bracket]` markers - **BaseTheme**: text field brackets drawn as **stretchable lines** that adapt to multi-line input (1px normal, 3px cursor mode) - **LyraTheme**: text field uses **fixed-width underline** (16px margins, 8px each side) instead of stretchable line (2px normal, 3px cursor mode) - **Both themes**: special keys (shift, mode, space, del, OK) have bordered/bordered-rounded rectangles - **Font size**: keyboard uses `UI_12_FONT_ID` in both themes (was `UI_10` in Base) - **Key height**: 40px in all themes for better proportions - **Layout unification**: text and password toggle are left-aligned in all themes (`keyboardCenteredText = false` for Lyra/Lyra3Covers) - **`primaryOffset` removed**: dead code eliminated from BaseTheme and LyraTheme `drawKeyboardKey` ### New Theme Metrics - `keyboardVerticalOffset`: per-theme vertical adjustment of keyboard position - Base: `-13`, Lyra: `-7` - `keyboardBottomKeySpacing`: independent spacing for bottom row keys - Base: `5`, Lyra: `5` - Bottom-aligned keyboard in both themes for consistent vertical positioning - Bottom row total width calculated to match content rows width (10-col based, consistent across modes) - 4px extra gap between content rows and bottom row when `bkSpacing > 0` - `keyboardCenteredText`: `false` for all themes (unified left-aligned text) ### Defensive Improvements - **State reset on re-entry**: `onEnter()` resets all mutable state (`symMode`, `urlMode`, `cursorMode`, `togglePos`, `passwordVisible`, `shiftState`, `selectedRow`, `selectedCol`, `rightHeld`, `rightLongHandled`, `savedCursorPos`, `rightStartCursorPos`, `delPressCount`, `hintVisible`, `hintShowTime`) — prevents stale state when re-entering the keyboard - **Bounds checking**: `insertChar`/`insertString` clamp `cursorPos` to `text.length()` before inserting - **Empty string guard**: `insertString` returns early on empty string - **`std::string::npos`**: used instead of `SIZE_MAX` for size_t sentinel (proper C++ idiom) - **`<algorithm>` header**: included for `std::max` ## Files Modified | File | Changes | |------|---------| | `src/activities/util/KeyboardEntryActivity.h` | `InputType` enum, `KeyDef` struct, 10-col layouts, cursor/password/URL/toggle state, hints (`delPressCount`, `hintVisible`, `hintShowTime`), held vars (`rightHeld`, `rightLongHandled`, `savedCursorPos`, `rightStartCursorPos`), `mapColContentBottom` helper | | `src/activities/util/KeyboardEntryActivity.cpp` | Complete rewrite: layout rendering, symbol/cursor/password/URL modes, toggle position, long-press, contextual tips, hint phases, block cursor kerning alignment, defensive bounds checks, state reset | | `src/components/themes/BaseTheme.h` | `KeyboardKeyType` enum, new `drawTextField`/`drawKeyboardKey` signatures, `keyboardVerticalOffset`, `keyboardBottomKeySpacing` metrics | | `src/components/themes/BaseTheme.cpp` | Redesigned `drawTextField` (stretchable brackets), `drawKeyboardKey` (inverted selection, space/delete graphics, secondary label, inactive selection), removed `primaryOffset` dead code | | `src/components/themes/lyra/LyraTheme.h` | Override signatures, `keyboardVerticalOffset`, `keyboardBottomKeySpacing`, `keyboardKeyHeight` adjustments | | `src/components/themes/lyra/LyraTheme.cpp` | `drawTextField` (fixed underline), `drawKeyboardKey` (rounded rects for special keys, space/delete graphics, secondary label, inactive selection), removed `primaryOffset` dead code | | `src/components/themes/lyra/Lyra3CoversTheme.h` | `keyboardCenteredText = false`, `keyboardVerticalOffset = -7`, inherits Lyra overrides | | `src/activities/network/WifiSelectionActivity.cpp` | `bool isPassword` → `InputType::Password` | | `src/activities/settings/KOReaderSettingsActivity.cpp` | `bool isPassword` → `InputType::Text`/`InputType::Password`/`InputType::Url` | | `src/activities/settings/CalibreSettingsActivity.cpp` | `bool isPassword` → `InputType::Text`/`InputType::Password`/`InputType::Url` | ## Backward Compatibility - **API change**: Constructor parameter changed from `bool isPassword` to `InputType inputType` (default `InputType::Text`) - **All callers updated**: WiFi, KOReader, and Calibre integrations migrated to new `InputType` enum ## Testing ### Input & Text Handling - [x] Empty input → press OK (submit empty string) - [x] Back button → cancel (no text returned) - [x] Pre-filled initial text (e.g., editing existing WiFi password) - [x] Password mode: text masked with `*` characters, one character revealed - [x] Delete on empty text (no crash) - [x] Very long text near maxLength limit - [x] URL with path and port (~60 chars) - [x] Multi-line text wrapping in input field - [x] Space insert in middle of text (cursor mode) - [x] Delete last character repeatedly - [ ] Type all 95 printable ASCII characters ### Mode Switching - [x] ABC → #@! preserves typed text and cursor position - [x] #@! → ABC preserves typed text and cursor position - [x] Shift state preserved when switching modes - [x] Switch modes multiple times rapidly ### Shift Behavior - [x] Shift OFF → type letter → inserts lowercase, shift stays OFF - [x] Shift ON → type letter → inserts uppercase, shift stays ON - [x] Shift ON → type number → inserts symbol, shift stays ON - [x] Shift ON → navigate rows → shift stays ON - [x] Shift ON → switch to #@! → shift shows "shift" (disabled) - [x] Shift ON → switch to ABC → shift state preserved - [x] Shift ON → switch to URL → shift shows "shift" (disabled) - [x] Shift disabled in URL mode: pressing shift does nothing ### Long-Press - [x] Long-press letter with shift OFF → inserts uppercase - [x] Long-press letter with shift ON → inserts lowercase - [x] Long-press number → inserts secondary symbol - [x] Long-press symbol (row 0) → inserts opposite (number) - [x] Long-press key without secondary (e.g., `-`, `=` in rows 2-3) → inserts primary character on release - [x] Long-press on special keys (shift, mode, space, del, ok) → no alternative inserted - [x] Long-press in #@! mode → no effect (disabled) - [x] Long-press in URL mode → no effect (disabled) - [x] Long-press number in row 0 with InputType::Url → inserts secondary symbol (same as non-URL) - [x] Short press after cancelled long-press → normal behavior - [x] Long-press at maxLength → no character inserted - [x] Long-press Del (1.5s) → clears all text ### Cursor Mode - [x] Long-press Up → enters cursor mode - [x] Short-press Down → exits cursor mode (resets passwordVisible) - [x] Left/Right navigate within text - [x] Left at position 0 → no movement - [x] Right at end of text → no movement in Text mode, enters toggle in Password mode (Hold Right) - [x] Block cursor visual: correct width for character, thin block at end - [x] Underline cursor visual (keyboard mode): correct position with serifs - [x] Inactive key styling: outline (Base) or gray fill (Lyra) on selected key - [x] Typing with cursor mid-text → inserts at cursor position - [x] Deleting with cursor mid-text → deletes character before cursor - [x] Exit cursor mode → type at cursor position (inserts mid-text, not at end) - [x] Exit cursor mode from toggle → cursor at saved position (not end of text) ### Password Mode - [x] Masked text with one revealed character at `cursorPos - 1` - [x] Cursor mode: block shows actual character, display text all `*` - [x] Toggle `[abc]`/`[***]`: Hold Right (500ms) in cursor mode enters toggle, Confirm toggles visibility, Left exits back to cursor - [x] Exiting cursor mode resets `passwordVisible` to false - [x] Long-press Del clears all text ### URL Mode - [x] URL toggle activates/deactivates URL mode - [x] URL button stays selected after toggle (both on and off) - [x] Deactivating URL mode returns to ABC (not SYM) - [x] 3×3 snippet grid displays correctly - [x] Column alignment: col 0 over ABC, col 1 over URL, col 2 over Del - [x] Snippet insertion: inserts full string at cursor position - [x] URL mode persists after snippet insertion - [x] Shift disabled in InputType::Url - [x] SpecMode (`abc`) exits URL mode to ABC - [x] Up/Down navigation between URL grid and bottom row ### Re-entry State Reset - [x] Enter keyboard → activate URL mode → exit → re-enter → URL mode OFF - [x] Enter keyboard → switch to SYM → exit → re-enter → ABC mode - [x] Enter keyboard → enter cursor mode → exit → re-enter → keyboard mode - [x] Enter keyboard → enter toggle pos → exit → re-enter → togglePos OFF - [x] Enter keyboard → activate shift → exit → re-enter → shift OFF - [x] Enter password keyboard → toggle password visible → exit → re-enter → password hidden ### Navigation - [x] Left/right wrap-around within content rows - [x] Left/right wrap-around within bottom row - [x] Up from row 0 → bottom row (correct column mapping) - [x] Down from bottom row → row 0 (correct column mapping) - [x] Up from bottom row → last content row (correct column) - [x] Down from last content row → bottom row (correct column) - [x] Navigate horizontally in bottom row, then up → correct content column - [x] Navigate horizontally in bottom row, then down (wrap) → correct content column ### Visual (both themes) - [x] Secondary hints only on ABC row 0 - [x] No secondary hints in #@! mode or URL mode - [x] No secondary hints on letter rows (1-3) - [x] Space bar: horizontal line centered, not touching edges - [x] Delete: arrow `←` drawn correctly - [x] Selected key: inverted colors (black fill, white text) - [x] All special keys have border rectangles - [x] Fixed underline in text field (Both themes) - [x] Mode key label: `#@!` in ABC mode, `abc` in symbol mode, `abc` in URL mode - [x] URL key label: `URL` (only in InputType::Url), styled same as other bottom keys - [x] Shift label: `shift` when OFF, `SHIFT` when ON, `shift` when disabled (SYM/URL) - [x] Both themes: bottom row total width matches content rows width - [x] URL snippet grid centered over ABC/URL/Del buttons ### Device & Theme Coverage - [ ] Base Theme on X3 - [x] Base Theme on X4 - [ ] Lyra Theme on X3 - [x] Lyra Theme on X4 - [ ] Lyra Extended Theme on X3 - [x] Lyra Extended Theme on X4 ### Toggle Position - [x] Hold Right > 500ms in cursor mode (Password) → enters toggle, caret visible at saved position - [x] Short-press Right in cursor mode (Password) → advances cursor 1 position, does not jump to toggle - [x] Short-press Left in cursor mode (Password) → moves cursor left 1 position, from toggle returns to saved position - [x] Confirm in toggle → toggles `passwordVisible` - [x] Left from toggle → returns to saved position, caret disappears, block cursor appears - [x] Right from toggle → no-op - [x] Down from toggle → exits to keyboard, cursor at saved position - [x] Hold Right in cursor mode (InputType::Text) → no effect - [x] Hold Right in cursor mode (InputType::Url) → no effect - [x] Hold Right < 500ms released in cursor mode (Password) → short press, advances cursor 1 - [x] No continuous repeat when holding Left or Right in cursor mode ### Caret Visual in Toggle - [x] In toggle: caret "I" visible at saved cursor position - [x] Character under cursor visible (no gap) in password not-visible mode - [x] Character under cursor visible in password visible mode - [x] `[abc]`/`[***]` label with inverted selection in toggle ### Contextual Tips - [x] `"Tips:"` header centered above contextual hints - [x] Single tip → `"Tips:"` + one line - [x] Multiple tips → `"Tips:"` + multiple lines, all centered as block - [x] No tips shown when not applicable (e.g., ABC with empty text and non-URL) - [x] `"UPPERCASE"` shown when shift OFF - [x] `"lowercase"` shown when shift ON - [x] `"secondary char"` shown for InputType::Url ### Hint Phases - [x] 2× DEL → Phase 1 appears ("Hold UP to edit entry") - [x] Phase 1 auto-hides after 4s - [x] Phase 2 appears when entering cursor mode ("Press < or > to move cursor") - [x] Phase 2 shows "Hold > then press [abc] to show password" when `!passwordVisible` - [x] Phase 2 shows "Hold > then press [***] to hide password" when `passwordVisible` - [x] Phase 2 shows "Press < to return to cursor position" when in toggle - [x] Phase 2 disappears when exiting cursor mode ### Long-Press `InputType::Url` Behavior - [x] Hold SELECT on letter rows (rows 1+) with InputType::Url → same character as short press - [x] Hold SELECT on row 0 with InputType::Url → secondary character works normally ### Number Row Reorder - [x] Number row order: 1-9, 0 left to right - [x] `(` and `)` are adjacent (positions 8 and 9) via secondary labels - [x] Long-press on row 0 returns correct secondary symbols in new order - [x] SYM row 1: `(` and `)` also adjacent (positions 8 and 9) ### Block Cursor Alignment - [x] Block cursor correctly positioned for consecutive spaces (kerning offset applied) - [x] Block cursor correctly positioned for mixed characters (letters, numbers, symbols) - [x] Block width minimum 6px for narrow characters (space) — visible as block, not thin line - [x] Password hidden: 3-part drawing prevents block overflow onto `*` characters - [x] Password visible: block post-loop draws correctly on continuous text (no 3-part needed) - [x] End-of-text block: thin 6px block at correct position ### Integration - [x] WiFi password entry (connect to network) - [ ] KOReader username, password, and sync server URL - [ ] Calibre OPDS URL, username, and password - [ ] Calibre OPDS URL: empty → opens with "https://" prefilled - [ ] Calibre OPDS URL: type "http://" or "https://" only → saved as empty - [ ] Calibre OPDS URL: type full URL → saved correctly - [ ] Calibre OPDS URL: existing URL → opens with existing URL (not "https://" prefill) |
||
|
|
3b12c083bc |
fix: make footnotes consider orientation for gutters (#1665)
## Summary * **What is the goal of this PR?** Noticed that the footnotes selection screen does not add proper margins to accommodate screen orientation * **What changes are included?** Copied some code over from `EpubReaderChapterSelectionActivity` to calculate the proper margins in `CW` and `Inverted` orientation | Before | After | |--------|--------| | <img width="578" height="435" alt="image" src="https://github.com/user-attachments/assets/0518a0c6-13d2-48a1-9283-90c83861e4c2" /> | <img width="578" height="435" alt="image" src="https://github.com/user-attachments/assets/ac34365c-72d0-4f07-85a6-17e966b28909" /> | | <img width="328" height="435" alt="image" src="https://github.com/user-attachments/assets/0614f19b-1000-4efe-8ef9-b533d2763a53" /> | <img width="328" height="435" alt="image" src="https://github.com/user-attachments/assets/ce9add2f-88e8-4032-a59c-efb55f366604" /> | --- ### 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**_ --------- Co-authored-by: Jan Ivanov <jan.ivanov@sirma.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> |
||
|
|
ed54f97909 |
fix: Fix ghosting on exit of BMPViewer (#1432)
## Summary * **What is the goal of this PR?** After displaying an image via the filebrowser ghosting artifacts remained on the screen * **What changes are included?** ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). --- ### 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 >**_ |
||
|
|
cced77783f |
feat: add orientation-aware popups for reader activities (#1428)
## Summary Make popups (like "Going to sleep") respect the current screen orientation when shown from reader activities. **What changes are included?** - Apply reader orientation in SleepActivity before showing popup when `lastSleepFromReader` is true - Make popup Y-position proportional to screen height (7.5% for BaseTheme, 16.5% for LyraTheme) instead of hardcoded pixel values, ensuring correct positioning in both portrait and landscape modes. - Add `isReaderActivity()` override to all reader sub-screens (menu, chapter selection, percent selection, footnotes, QR display, KOReader sync), so sleep popups rotate correctly when entering sleep from any reader context. ## Additional Context <img src="https://github.com/user-attachments/assets/47d88c2c-ffc5-41a7-b3f2-af272ea0150e" width="400" height="240"> --- ### 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**_ (Claude Opus 4.5) |
||
|
|
1bd7a1de67 |
refactor: Deduplicate battery drawing code and fix Lyra charging indicator (#1437)
## Summary **What is the goal of this PR?** Following up on #1427: - Extracted shared battery drawing logic, including lightning bolt, to reduce duplication. - In Lyra with segmented battery the lightning bolt was hard to see, so when charging Lyra now uses a solid battery fill. --- ### 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**_ |
||
|
|
23aad213fc |
refactor: Removed redundant FsFile close() calls (#1434)
## Summary **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.) `DESTRUCTOR_CLOSES_FILE=1` is set in platformio.ini, which makes SdFat's FsBaseFile destructor call close() automatically when a file goes out of scope. Three categories of file close calls remain untouched: 1. Close before Storage.remove() on the same path: ScreenshotUtil.cpp closes the file before deleting it on write error. The remove might fail if the file is still open. 2. Close before reopening the same variable: Epub.cpp writes a temp NCX/nav file, closes it, then reopens it for reading. The RecentBooksStore.cpp close before saveToFile() is the same pattern, it rewrites the same file. 3. Close on member variables: BookMetadataCache.cpp (bookFile, spineFile, tocFile), Section.cpp (file), XtcParser.cpp (m_file), ZipFile.cpp (file). These persist beyond any single function scope, so the destructor timing doesn't match the intended close point. --- ### 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**_ |
||
|
|
243ae8b408 |
feat: show crash reason on boot (#1453)
## Summary If the system reboots from crash, display the reason and tell user to include `crash_report.txt` file. To test this, simply add an `assert(false)` somewhere inside the code.  --- ### 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** |
||
|
|
4e9c7a787f |
feat: show full path bar in file browser (#1411)
 ## Summary Adds a full path display at the bottom of the file browser with a separator line matching the header style. Path uses the small font, left-truncates to always show the deepest folder when path is too long. Toggleable via Settings > System > Show Full Path (default on). ### 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 >**_ --------- Co-authored-by: Patryk Radtke <patryk@Patryks-MacBook-Pro.local> |
||
|
|
05f8e6e12d |
fix: webserver /delete API backward compatibility (#1475)
## Summary
* **What is the goal of this PR?** (e.g., Implements the new feature for
file uploading.)
This fixes #682 by restoring API compatibility for external users, eg
deleting on-device books in bulk via calibre using the crosspoint plugin
works now.
* **What changes are included?**
The `/delete` webserver API now accepts the old `path` argument and maps
it to a JSON array element. It is made an error to provide both
arguments.
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
specific areas to focus on).
I have compiled, flashed, and tested this on master at commit
|
||
|
|
405ce0c3c8 |
feat: Rework "Cover + Custom" sleep screens to show covers only when currently reading (#1256)
## Summary The cover/custom setting was a bit misleading to me. Usually after you start reading a book you never see your customs again unless a cover fails to render for some reason. With this, you can easily show your custom images by just sleeping from the menus instead of the reader ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). --- ### 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 >**_ |
||
|
|
9c11f3e4a2 |
feat: enable manual screen refresh on power button short press (#1626)
## Summary Adds an option to allow manual screen refresh on power button short press. ## Additional Context there's an option to refresh the screen after a set number of pages. but sometimes a manual refresh is needed to clear up stale ink pixels. this works everywhere both in and out of reading mode. resolves #550 --- ### 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**_, to understand the project structure. |
||
|
|
fa2a3d2539 |
feat: add OPDS search support & next/prev page navigation (#1462)
## Summary **What is the goal of this PR?** Adds OPDS search support, allowing users to search a catalog directly from the book browser when the server exposes an OpenSearch template. **What changes are included?** - `OpdsParser`: parses the OpenSearch template URL from feed-level `<link rel="search">` elements and exposes it via `getSearchTemplate()` - `OpdsBookBrowserActivity`: fetches and stores the search template after each feed load; shows a Search hint on the Left button when a template is available; launches the existing `KeyboardEntryActivity` for query input; URL-encodes the query and fetches the result feed - Absolute search result URLs are handled correctly in `fetchFeed` (skips prepending the server base URL) - A `consumeConfirm` guard prevents the Confirm release that submits the keyboard from immediately triggering a book download on the first browsing frame after search results load ## Additional Context - Search is silently unavailable if the server does not advertise an OpenSearch template — no UI change in that case - Tested against a Calibre-Web OPDS endpoint which exposes `<link rel="search" type="application/opensearchdescription+xml">` - The inline URL encoder in `performSearch` was necessary as `StringUtils` has no such utility; worth considering extracting to `StringUtils` in a follow-up - No new dependencies introduced --- ### 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**_ --------- Co-authored-by: kira <rammah@tuta.io> Co-authored-by: Justin Mitchell <justin@jmitch.com> |
||
|
|
5c12f2f01e |
fix: avoid skipping chapter after screenshot (#1625)
## Summary * **What is the goal of this PR?** Fixes skipping chapter when it's enabled in Settings and you take a Screenshot. * **What changes are included?** A simple return if Power and Down were released before the skipChapter. ## Additional Context * There is an Issue related #1595. * Checked that it kept the Power button funcionality for Next page and Suspend. --- ### 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**_ |
||
|
|
8d6b35b8e7 |
fix: back navigation from BMPViewer (#1597)
## Summary This fixes navigating back from the BMP Viewer to the FileBrowser which was broken when moving to the new ActivityManager This is fixed by making FileBrowserActivity able to take a full file path on enter and splitting the basePath and fileName from it and navigating to the correct place. fixes: https://github.com/crosspoint-reader/crosspoint-reader/issues/1553 duplicates: https://github.com/crosspoint-reader/crosspoint-reader/pull/910 to some extend but mine has the file cursor at the correct file instead of the first one in the folder ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). --- ### 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**_ --------- Co-authored-by: Jan Ivanov <jan.ivanov@sirma.com> |
||
|
|
5ba85290ab |
refactor: Deduplicated BMP header writing in Xtc (#1439)
## Summary **What is the goal of this PR?** Replaced manual 1-bit BMP header logic in `Xtc::generateCoverBmp()` and `Xtc::generateThumbBmp()` with calls to the existing `createBmpHeader()` utility. Added a `BmpRowOrder` enum and param to support the top-down row order of XTC cover images. --- ### 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**_ |
||
|
|
5349e81723 |
feat: Display file extensions in File Browser (#1019)
## Summary    ## Additional Context Do we want a setting to toggle this? --- ### AI Usage Did you use AI tools to help write this code? _**NO**_ |
||
|
|
825ef56ad8 |
feat: X3 grayscale antialiasing improvements (#1607)
## Summary Improves text antialiasing quality on the Xteink X3 (SSD1677) display to bring it closer to X4 rendering quality. Addresses white lines through letter strokes and ghosting artifacts during page turns and screen transitions. ## Changes ### Display Driver (open-x4-sdk) - Dedicated X3 grayscale LUTs with tuned VDL drive strengths for dark gray (2 time units) and light gray (3 time units), with active GND hold on non-gray transitions to prevent floating source crosstalk - Tight scan timing: TP2/TP3 reduced to 1 (total gate-on 7 units vs 17), minimizing parasitic charge leakage that caused white lines through letter strokes - Fast diff BB reinforcement: Added mild VDH reinforcing pulse to lut_x3_bb_full so black pixels are actively driven during differential refreshes, clearing gray residue/ghosting - displayGrayBuffer() updated to use the dedicated gray LUT bank instead of full refresh LUTs for X3 ### Rendering Pipeline - Re-enabled light gray rendering for X3 text and images, now safe with dedicated gray LUTs providing proper 4-level gray - Removed isLightGrayRestricted() gating that was limiting X3 to 3-level gray - Runtime display dimensions in DirectPixelWriter and ScreenshotUtil, replaced hardcoded constants with runtime getters to support X3 792x528 resolution ### Note The open-x4-sdk submodule references a commit on juicecultus/community-sdk. A corresponding PR to open-x4-epaper/community-sdk should be merged first so the submodule ref resolves on upstream. ## Testing Tested on physical X3 hardware. White lines through letters significantly reduced, in-book ghosting improved via BB reinforcement, antialiasing visually closer to X4 quality. ## AI Disclosure Yes, AI was used to assist with the development of these changes. --------- |
||
|
|
ed0811c898 |
fix: Fix failing very first wifi connection attempt (#1521)
## Summary * **What is the goal of this PR?** The very first Wifi connection attempt with saved credentials failed, subsequent attempts succeeded. This PR fixes the first-attempt-issue. * **What changes are included?** ## Additional Context Claude analysis for WifiSelectionActivity In attemptConnection() ,there's no WiFi.disconnect() before WiFi.begin() — unlike the scan path earlier which does do a disconnect first. The root cause on ESP32 is the built-in auto-connect feature: the ESP32 WiFi stack saves credentials to NVS flash and automatically starts trying to connect on boot before your application code runs. When your attemptConnection() then calls WiFi.begin(), the stack is already in a transitional CONNECTING state, and the new begin() call either gets ignored or collides with the in-progress attempt. The fix is to add WiFi.disconnect(true) + a short delay in attemptConnection() before calling WiFi.begin(), and optionally call WiFi.persistent(false) to stop the ESP32 from auto-connecting on its own. --- ### 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 >**_ |
||
|
|
d29b8ee2f9 |
feat: Adjust Navigation at End of Book (#1425)
## Summary * **What is the goal of this PR? (e.g., Implements the new feature for file uploading.)** Currently, pressing forward at the end of a book loops back to the last page. This change will instead sends you to the home page instead. * **What changes are included?** Applies the change to the three supported format: EPUB, XTC, TXT ## Additional Context * This is more of a QOL improvement than a new feature. If there's interest, we could extend this to track a completed state for ebooks. --- ### 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**_ |
||
|
|
6cd19f5619 |
fix: epub images not rendering correctly on x3 (#1572)
Replace hardcoded DISPLAY_WIDTH, DISPLAY_HEIGHT, and DISPLAY_WIDTH_BYTES constants with runtime values from display object to support multiple device models (X3 and X4) with different screen dimensions. |
||
|
|
11984f8fef |
refactor: Use C++20 'requires' in ActivityResult constructor (#1420)
## Summary **What is the goal of this PR?** Replace SFINAE std::enable_if_t with a C++20 `requires` clause for clearer constraint expression and better compiler diagnostics on mismatch. --- ### 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**_ |
||
|
|
9b3885135f |
feat: Initial support for the x3 (#875)
## Summary Adds Xteink X3 hardware support to CrossPoint Reader. The X3 uses the same SSD1677 e-ink controller as the X4 but with a different panel (792x528 vs 800x480), different button layout, and an I2C fuel gauge (BQ27220) instead of ADC-based battery reading. All X3-specific behavior is gated by runtime device detection — X4 behavior is unchanged. Depends on community-sdk X3 support: open-x4-epaper/community-sdk#19 (merged). ## Changes ### HAL Layer **HalGPIO** (`lib/hal/HalGPIO.cpp/.h`) - I2C-based device fingerprinting at boot: probes for BQ27220 fuel gauge, DS3231 RTC, and QMI8658 IMU to distinguish X3 from X4 - Detection result cached in NVS for fast subsequent boots - Exposes `deviceIsX3()` / `deviceIsX4()` helpers used throughout the codebase - X3 button mapping (7 GPIOs vs X4's layout) - USB connection detection and wake classification for X3 **HalDisplay** (`lib/hal/HalDisplay.cpp/.h`) - Calls `einkDisplay.setDisplayX3()` before init when X3 is detected - Requests display resync after power button / flash wake events - Runtime display dimension accessors (`getDisplayWidth()`, `getDisplayHeight()`, `getBufferSize()`) - Exposed as global `display` instance for use by image converters **HalPowerManager** (`lib/hal/HalPowerManager.cpp/.h`) - X3 battery reading via I2C fuel gauge (BQ27220 at 0x55, SOC register) - X3 power button uses GPIO hold for deep sleep ### Display & Rendering **GfxRenderer** (`lib/GfxRenderer/GfxRenderer.cpp/.h`) - Buffer size and display dimensions are now runtime values (not compile-time constants) to support both panel sizes - X3 anti-aliasing tuning: only the darker grayscale level is applied to avoid washed-out text on the X3 panel. X4 retains both levels via `deviceIsX4()` gate **Image Converters** (`lib/JpegToBmpConverter`, `lib/PngToBmpConverter`) - Cover image prescale target uses runtime display dimensions from HAL instead of hardcoded 800x480 ### UI Themes **BaseTheme / LyraTheme** (`src/components/themes/`) - X3 button position mapping for the different physical layout - Adjusted UI element positioning for 792x528 viewport ### Boot & Init **main.cpp** - X3 hardware detection logging - Adjusted init sequence for X3 (no `HalSystem::begin()` dependency on X3 path) **HomeActivity** - Uses runtime `renderer.getBufferSize()` instead of static `GfxRenderer::getBufferSize()` FYI I did not add support for the gyro page turner. That can be it's own PR. |
||
|
|
710055f02c |
feat: Make directories stand out more in local file browser: "[dir]" instead of "dir" (#1339)
## Summary * **What is the goal of this PR?** It's difficult to distinguish directory names from normal file entries, so they are displayed now as "[dir]" instead of "dir" for classic theme * **What changes are included?** ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). --- ### 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**_ |
||
|
|
0cbfaa007d |
fix: Overlapping battery percentage on image pages with anti-aliasing (#1452)
## Summary **What is the goal of this PR?** When viewing a page with images and anti-aliasing enabled, the `imagePageWithAA` path renders the page twice with fast refreshes (blank image area, then restore). Both passes called `renderStatusBar()`, which reads the battery percentage live. If the value changed between the two renders (e.g. 88% -> 87%), the digits would overlap on screen. Fix: Removed the redundant `renderStatusBar()` from the second BW render. The status bar is already drawn and displayed in the first pass, and only the image area needs restoration. --- ### 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**_ |
||
|
|
7d56810ee6 |
feat: integrated epub optimizer (#1224)
## Problem
Many e-ink readers have limited image decoder support natively.
EPUBs with images in other formats than **baseline JPEG** frequently
cause:
- **Broken images**: pages render as blank, corrupted noise, or never
load
- **Slow rendering**: unoptimized images cause severe delays on e-ink
hardware, up to 7 seconds per page turn, with cover images taking up to
59 seconds to render
- **Broken covers**: the book thumbnail never generates
Fixing this today requires external tools before uploading.
---
## What this PR does
Adds an **optional, on-demand EPUB optimizer** to the file upload flow.
When enabled,
it converts all images to baseline JPEG directly in the browser — no
server, no internet,
no external tools needed.
**Conversion is opt-in. The standard upload flow is unchanged.**
---
## Real-world impact
The optimizer was applied in batch to **61 EPUBs**:
- 60 standard EPUBs: 198 MB → 55 MB (**−72.2%**, 143 MB saved)
- Text-dominant books: 8–46% smaller (covers and inline images
converted)
- Image-heavy / illustrated books: 65–93% smaller
- 1 Large manga volume (594 MB): 594 MB → 72 MB (**−87.8%**, 522 MB
saved)
- EPUB structural integrity fully maintained — zero new validation
issues introduced across all 61 books
*Size and integrity analysis:
[epub-comparator](https://github.com/pablohc/epub-comparator)*
From that set, **17 books were selected** as a representative sample
covering different content
types: image-heavy novels, pure manga, light novels with broken images,
and text-dominant books.
Each was benchmarked on two devices running in parallel, one on `master`
and one
on `PR#1224` — measuring render time across ~30 pages per book on
average.
### Rendering bugs fixed
| Book | Problem (original) | After optimization |
|------|--------------------|--------------------|
| Fairy Tale — Stephen King | Cover took **59.7 s** to render | 2.1 s
(−96%) |
| Cycle of the Werewolf — Stephen King | Cover took **23.3 s** to render
| 1.7 s (−93%) |
| Tomie: Complete Deluxe Ed. — Junji Ito | Cover took **18.3 s** to
render | 2.0 s (−89%) |
| Joel Dicker — El tigre (Ed. Ilustrada) | Cover took **14.5 s** to
render | 1.4 s (−90%) |
| Jackson, Holly — Asesinato para principiantes | Cover failed
completely (blank) | 2.0 s ✓ |
| Sentenced to Be a Hero — Yen Press | Cover failed, **8 images failed
to load** | All fixed ✓ |
| Flynn, Gillian — Perdida | Cover failed completely (blank) | 1.6 s ✓ |
| Chandler, Raymond — Asesino en la lluvia | Cover failed completely
(blank) | 2.0 s ✓ |
### Page render times — image-heavy EPUBs (avg per page)
| Book | Pages | Avg original | Avg optimized | Improvement | File size
|
|------|-------|-------------|---------------|-------------|-----------|
| Fairy Tale — Stephen King | 30 | 3,028 ms | 1,066 ms | **−64.8%** |
32.4 MB → 9.1 MB (−72%) |
| Cycle of the Werewolf — Stephen King | 33 | 3,026 ms | 1,558 ms |
**−48.5%** | 35.1 MB → 2.9 MB (−92%) |
| Joel Dicker — El tigre (Ed. Ilustrada) | 16 | 1,846 ms | 1,051 ms |
**−43.1%** | 5.3 MB → 0.4 MB (−93%) |
| Tomie: Complete Deluxe Ed. — Junji Ito | 30 | 4,817 ms | 2,802 ms |
**−41.8%** | 593.8 MB → 72.2 MB (−87.8%) |
| Sentenced to Be a Hero — Yen Press | 30 | 1,719 ms | 1,388 ms |
**−19.2%** | 15.2 MB → 1.6 MB (−90%) |
### Text-heavy EPUBs — no regression
| Book | Pages | Avg original | Avg optimized | Delta |
|------|-------|-------------|---------------|-------|
| Christie — Asesinato en el Orient Express | 30 | 1,672 ms | 1,646 ms |
−1.6% |
| Flynn — Perdida | 30 | 1,327 ms | 1,291 ms | −2.7% |
| Dicker — La verdad sobre el caso Harry Quebert | 30 | 1,132 ms | 1,084
ms | −4.2% |
| Hammett — El halcón maltés | 30 | 1,009 ms | 966 ms | −4.3% |
| Chandler — Asesino en la lluvia | 30 | 989 ms | 1,007 ms | +1.8% |
*Differences within ±5% — consistent with device measurement noise.*
*Render time benchmark:
[epub-optimization-benchmark](https://github.com/pablohc/epub-optimization-benchmark)*
---
## How to use it
**Single file:**
1. Click **Upload** (top of the page) — a modal opens. Use **Choose
files** to select one EPUB from your device.
2. Check **Optimize**.
- *(Optional)* Expand **Advanced Mode** — adjust quality, rotation, or
overlap; set individual images to H-Split / V-Split / Rotate.
3. Click **Optimize & Upload**.
**Batch (2+ files):**
1. Click **Upload** (top of the page) — a modal opens. Use **Choose
files** to select multiple EPUBs from your device.
2. Check **Optimize**.
- *(Optional)* Expand **Advanced Mode** — adjust quality.
3. Click **Upload** — all files are converted and uploaded sequentially.
Upload a batch of files, without optimization:
<img width="810" height="671" alt="image"
src="https://github.com/user-attachments/assets/d892ae13-0b87-4ea4-b6b8-340d56efc763"
/>
Batch file upload, with standard optimization:
<img width="809" height="707" alt="image"
src="https://github.com/user-attachments/assets/d32dbc88-1208-4555-bfcf-330ab91d2174"
/>
Optimization Phase (1/2):
<img width="807" height="1055" alt="image"
src="https://github.com/user-attachments/assets/fd4cd5f9-e56e-4ca1-9777-6926b9baf2bb"
/>
Upload Phase (2/2):
<img width="805" height="1065" alt="image"
src="https://github.com/user-attachments/assets/483294f0-02f0-4569-ae11-c10b3581d747"
/>
Batch upload successfully confirmed:
<img width="812" height="1043" alt="image"
src="https://github.com/user-attachments/assets/80c135bf-05c3-4c80-8755-2a04c68235bc"
/>
---
## Options
**Always active when the converter is enabled:**
- Converts PNG, WebP, BMP, GIF → baseline JPEG
- Smart downscaling to 480×800 px max (preserves aspect ratio)
- True grayscale for e-ink (BT.709 luminance, always on)
- SVG cover fix + OPF/NCX compliance repairs
**Advanced Mode (opt-in) — single file:**
- JPEG quality presets: 30% / 45% / 60% / 75% / **85%** (default) / 95%
- Rotation direction for split images: CW (default) / CCW
- Min overlap when splitting: 5% (default) / 10% / 15%
- Auto-download conversion log toggle (detailed stats per image)
- Per-image picker: set Normal / H-Split / V-Split / Rotate per image
individually,
with "Apply to all" for bulk assignment
**Advanced Mode (opt-in) — batch (2+ files):**
- JPEG quality presets: 30% / 45% / 60% / 75% / **85%** (default) / 95%
- Auto-download conversion log toggle (aggregated stats for all files)
---
## ⚠️ Known limitations
**KoReader hash-based sync will break** for converted files. The file
content changes,
so the hash no longer matches the original. Filename-based sync is
unaffected.
If you rely on KoReader hash sync, use the Calibre plugin or the web
tool instead.
---
## Build size impact
| Metric | master (
|
||
|
|
8dd365b4da |
feat: Implement silent pre-indexing for the next chapter in EpubReaderActivity (#979)
## Summary * A simple tweak to pre-index the next chapter silently during normal reading. * Triggers silent pre-indexing of the next chapter when the penultimate page of a chapter is rendered to reduce visible interruptions. * Keeps existing indexing with popup when a reader jumps directly into an unindexed chapter. ## Additional Context * Reader input is temporarily blocked during silent indexing to avoid navigation/index state conflicts. * The penultimate page is used because readers typically spend longer there than on the final page. * This change optimizes linear reading flow while preserving reliable indexing for non-linear navigation. ## Possible Improvements * Add a setting for First Page Indexing vs Penultimate Page Pre-indexing * Display an indexing icon in the status bar instead of using a popup that overlaps book text. Tested on device: https://www.dropbox.com/scl/fi/29g5kjqgsi5e4hgujv38u/Silent-Indexing.MOV?rlkey=yemi4mosmev5vicaa7gpe49qw&dl=0 --- ### 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: Jake Kenneally <jakekenneally@gmail.com> |
||
|
|
71719e1d94 |
feat: battery charging indicator (mirroring PR #537) (#1427)
## Summary * **What is the goal of this PR?** All praise goes to @didacta for his PR #537. Just picked up the reviewer comments to contain the changes as suggested (there was no response for more than 6 weeks, so I wanted to reanimate this feature). Just one addition: should recognize usb cable plug ins / retractions and update the icon immediately * **What changes are included?** ## Additional Context see #537 --- ### 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 >**_ |
||
|
|
f9286709d1 |
feat: Show hidden directories in browser (#1288)
## Summary * **What is the goal of this PR?** Add setting to display hidden files / directories in filebrowser / web file browser * **What changes are included?** ## Additional Context - --- ### 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 >**_ |
||
|
|
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 **_ |
||
|
|
11ca208ec2 |
chore: change label while on settings tab actions (#1325)
## Summary * **What is the goal of this PR?** The "Toggle" label while on the "tab" actions of settings screen was misleading. Will show "Select" now , while "Toggle" remains in place for all 'real' settings * **What changes are included?** ## Additional Context <img width="240" alt="1" src="https://github.com/user-attachments/assets/dc198716-0aad-4c75-96fe-52595625e69d" /> <img width="240" alt="2" src="https://github.com/user-attachments/assets/85ce5368-801c-489d-aa94-51f126c3ddc8" /> --- ### 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 **_ |