Implement chunked RAM cache for .pxc pixel data to avoid repeated SD card reads during multi-pass grayscale rendering. Uses 16KB chunks up to 96KB total, heap-gated with fallback to streaming. Cache is released after each page render to avoid memory retention across page turns.
Some EPUB converters emit byte-identical stylesheets per chapter (100+ entries). Now scan the ZIP central directory once to identify duplicates by CRC32 and compressed size, then skip parsing identical files. This avoids redundant ZIP lookups and SD extraction round-trips while preserving all styles since rules merge into a global set. Also add extractItemToFile helper and allowEarlyStop parameter to readItemContentsToStream.
ESP_SR (speech recognition) only exists in the S3 core and its wrapper includes ESP_I2S.h, which the isolated core rebuild cannot resolve. Since ESP_SR is unused, disable it via CONFIG_ARDUINO_SELECTIVE_ESP_SR=n to allow the sticky environment to build successfully. Also ignore sdkconfig.sticky file.
Expand scope to support multiple e-ink devices beyond X4. Add guiding
principle to fill gaps in stock firmware rather than duplicate
functionality. Establish current focus on memory/flash optimization and
code cleanup. Temporarily freeze new themes and network connectors to
consolidate codebase before multi-device expansion.
ActivityManager.h forward-declares Activity but holds
std::unique_ptr<Activity> members. Instantiating that unique_ptr's
destructor requires the complete type, so any TU including
ActivityManager.h without Activity.h fails to compile.
Include Activity.h directly. Adding it to ActivityManager.h instead does
not work: Activity.h depends on HomeMenuItem, which ActivityManager.h
defines.
## Summary
It would be nice to switch back to the file list from an Reader activity
via a short back button press. This change adds an Reader option to
switch the default behavior, so a short back button press in the Reader
activity can now go back to the file list, and a long press on back goes
back to the home view. This does a fair bit of refactoring, introducing
a new constant for the ms limit.
* **What changes are included?**
- Changes to the translation
- Additional global Reader option
- Refactoring of the back button behavior in the Reader activity
Implements createUser() endpoint to allow account creation via the
KOSync protocol. Changes default sync server from sync.koreader.rocks to
sync.crosspointreader.com with migration logic to preserve existing
users' server settings. Extends sync protocol to include position data
(spine index, page numbers, xpath) that crosspoint-sync supports while
remaining compatible with standard kosync servers.
Fix CPU pinning by tracking buildHeapPaused state when background build is blocked. Re-check heap gate after acquiring lock to handle race conditions where rendering expands glyph buffers. Refine plane buffer allocation to check both free heap and max contiguous block, preventing fragmentation issues that could cause OOM during page renders. Skip async refresh for image pages since they use blocking double-FAST path.
Closes#2558.
Enables the Arduino WebServer's built-in CORS support
(`enableCORS(true)`), which adds
`Access-Control-Allow-Origin/Methods/Headers: *` to every response, and
answers preflight `OPTIONS` requests with `204` in `handleNotFound()` —
routes are registered per-method, so OPTIONS always lands there. The
AP-mode captive-portal redirect is untouched (the OPTIONS check runs
before it, and browsers don't send preflights for captive-portal
probes).
This lets web-based clients and PWAs served from other origins call the
JSON API (`/api/status`, `/api/files`, `/api/settings`, ...) directly
from the browser.
Overhead is three static response headers; no behavior change for the
built-in web UI.
Note: not yet tested on hardware.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: metoli <metoli@metoli-Mac-mini.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary
Make KOReader progress sync a one-click flow for common cases while
keeping the existing manual mode available.
### Changes
- Add a **Sync Behavior** setting:
- **Smart sync** for new configurations
- **Ask every time** for manual control
- Preserve **Ask every time** when migrating an existing credential file
that predates this setting.
- In Smart mode:
- upload local progress when no remote record exists;
- show a short confirmation when already synced;
- upload when local progress is further ahead;
- apply remote progress when remote progress is further ahead.
- Probe both KOReader document-matching hashes before making the Smart
decision, while keeping uploads on the user's configured matching
method.
- Auto-return after successful Smart terminal states, with Back/Confirm
still available.
- Persist the setting alongside the current credential, matching-method,
and send-metadata fields.
- Document both behaviors in the user guide.
## Additional context
This pairs well with #2189 (smart Wi-Fi auto-connect), but does not
depend on it. It does not add background Wi-Fi or passive network
detection; sync is still triggered by the user from the reader menu.
Smart sync uses the furthest progress because CrossPoint does not
currently persist local progress timestamps. Users who prefer explicit
conflict resolution can select **Ask every time**.
## Verification
- `git diff --check`
- `platformio check --fail-on-defect low --fail-on-defect medium
--fail-on-defect high` — no defects
- `platformio run -e default` — firmware build successful
- The original implementation was manually smoke-tested on an X4 device.
---
### AI Usage
Did you use AI tools to help write this code? _**YES**_
AI tools were used to inspect the codebase, draft and review the
implementation, resolve the rebase against current `develop`, and
prepare the PR text. The resulting firmware was built locally.
---------
Co-authored-by: Alexander Hoffer <git@alexanderhoffer.com>
Co-authored-by: Alexander Hoffer <contact@alexanderhoffer.com>
On a fresh CI checkout cppcheck has no resolved include paths, so it
reports every project header as missing (~400 information-level lines)
and --fail-on-defect low fails the job. Suppress it alongside
missingIncludeSystem, matching the fix already proven on the BLE branch.
Revalidate the section under RenderLock before the idle prewarm scan,
add a largest-block floor to its heap gate, rebuild the differential
baseline when the grayscale scratch allocation fails after an async
refresh, and anchor generated-file gitignore patterns to the repo root.
- Keep-if-fits font buffer reuse (SdCardFont), the page-turn fragmentation fix
- Background-build heap floors + buildTickHeapGate() (the ParsedText::addWord abort fix), with the BLE-shed branch removed
- KOSync TLS gate split (free ≥ 50K, largest block ≥ 20K)
- wolfSSL SP ECC flags + FP_MAX_BITS 8192 in the patch script
- custom_sdkconfig: timer-stack trims (~7KB) and, per your answer, the WiFi IRAM opts (~25-30KB); plus the cloud-component removal the hybrid build requires. All verified present in the generated sdkconfig after the build.
- Web server watchdog registration fix (this branch's handlers already call esp_task_wdt_reset without it)
Changes all sleep screen display refreshes and boot screens from
FULL_REFRESH to HALF_REFRESH to match OEM firmware behavior. The stock
firmware uses a single-pass 0xD7 waveform for sleep screens, not the
multi-flash GC waveform (0xF7) that FULL_REFRESH triggers. For grayscale
mode, HALF_REFRESH is required because the gray nudge LUT is calibrated
against the pixel charge state left by the single-pass waveform; using
FULL_REFRESH causes blotchy noise in gray areas. Also adds X3 wb_gc
passive byte (the one-byte 0x54 to 0x00, matching stock's passive WB
table in the gc nudge bank): Every X3 grayscale render, reader AA,
covers, and the sleep-cover nudge, gets it.
Introduces non-blocking refresh capability with displayBufferAsync() that starts panel waveform and returns immediately, plus waitRefreshComplete() to block until refresh finishes. Panels without async support fall back to blocking refresh. Requires framebuffer to remain untouched during refresh and differential baseline rebuild before next update.
1. X3 wb_gc passive byte (the one-byte 0x54 to 0x00, matching stock's passive WB table in the gc nudge bank) Every X3 grayscale render, reader AA, covers, and the sleep-cover nudge, gets it. 2. X4 blotchy grays on sleep covers: the displayGrayscaleBase(HALF_REFRESH) revert (the gray nudge LUT being calibrated against the HALF pixel state, not FULL's)
Changes all sleep screen display refreshes from FULL_REFRESH to HALF_REFRESH to match OEM firmware behavior. The stock firmware uses a single-pass 0xD7 waveform for sleep screens, not the multi-flash GC waveform (0xF7) that FULL_REFRESH triggers. For grayscale mode, HALF_REFRESH is required because the gray nudge LUT is calibrated against the pixel charge state left by the single-pass waveform; using FULL_REFRESH causes blotchy noise in gray areas.